struct.error:结构形式中的错误字符

2024-04-20 16:33:25 发布

您现在位置:Python中文网/ 问答频道 /正文

在Python2.7中,我有一个小的服务器客户端分配问题。

客户端可以向服务器发送5种类型的请求:

  1. 获取服务器的ip
  2. 获取服务器上目录的内容
  3. 在服务器上运行cmd命令并获取输出
  4. 在服务器上打开计算器
  5. 断开

基本上,这就是我得到的错误:

line 19, in server
    data_size = calcsize(client_structs) - 3

struct.error: bad char in struct format

请解释一下这个错误以及如何解决它。

服务器代码:

__author__ = 'eyal'

from struct import pack, unpack, calcsize
import socket
from os import listdir
from subprocess import check_output, call


def server():
    ser_soc = socket.socket()
    ser_soc.bind(("0.0.0.0", 8080))
    ser_soc.listen(1)
    while True:
        accept_flag = raw_input("Would you like to wait for a client? (y/n) ")
        if accept_flag == "y":
            client_soc, client_address = ser_soc.accept()
            while True:
                client_structs = client_soc.recv(1024)
                data_size = calcsize(client_structs) - 3
                data_str = 'c' * data_size
                unpacked_data = unpack("BH" + data_str, client_structs)
                if unpacked_data[0] == 1:
                    ip = socket.gethostbyname(socket.gethostname())
                    ip_data = 'c' * len(ip)
                    to_send = pack("BH" + str(len(ip)) + ip_data, unpacked_data[0], len(ip), ip)
                elif unpacked_data[0] == 2:
                    content = listdir(str(unpacked_data[2]))
                    content_str = "\r\n".join(content)
                    content_data = 'c' * len(content_str)
                    to_send = pack("BH" + str(len(content_str)) + content_data, unpacked_data[0],
                                   len(content_str), content_str)
                elif unpacked_data[0] == 3:
                    command = str(unpacked_data[2:]).split()
                    output = check_output(command)
                    message_data = 'c' * len(output)
                    to_send = pack("BH" + message_data, unpacked_data[0], len(output), output)
                elif unpacked_data[0] == 4:
                    call("gnome-calculator")
                    msg_data = 'c' * len("The calculator is open.")
                    to_send = pack("BH" + msg_data, unpacked_data[0], len("The calculator is open."),
                                   "The calculator is open.")
                elif unpacked_data[0] == 5:
                    client_soc.close()
                    break
                else:
                    to_send = pack("BH" + 'c' * len("invalid message type, try again"),
                                   unpacked_data[0], len("invalid message type, try again"),
                                   "invalid message type, try again")
                if unpacked_data[0] != 5:
                    client_soc.send(to_send)
        else:
            break
    ser_soc.close()


def main():
    server()


if __name__ == "__main__":
    main()

客户代码:

__author__ = 'eyal'


from struct import pack, unpack, calcsize
import socket


def client():
    my_soc = socket.socket()
    my_soc.connect(("127.0.0.1", 8080))
    while True:
        send_flag = raw_input("Would you like to send the server a request? (y/n) ")
        if send_flag == "y":
            msg_code = input("What type of request would you like to send?\n"
                             "1. Get the server's IP address.\n"
                             "2. Get content of a directory on the server.\n"
                             "3. Run a terminal command on the server and get the output.\n"
                             "4. Open a calculator on the server.\n"
                             "5. Disconnect from the server.\n"
                             "Your choice: ")
            if msg_code == 1 or msg_code == 4 or msg_code == 5:
                to_send = pack("BH", msg_code, 0)
            elif msg_code == 2:
                path = raw_input("Enter path of wanted directory to get content of: ")
                to_send = pack("BH" + 'c' * len(path), msg_code, len(path), path)
            elif msg_code == 3:
                command = raw_input("Enter the wanted terminal command, including arguments: ")
                to_send = pack("BH" + 'c' * len(command), msg_code, len(command), command)
            else:
                print "Invalid message code, try again\n"

            if 1 <= msg_code <= 5:
                my_soc.send(to_send)
        else:
            break
    data = my_soc.recv(1024)
    unpacked_data = unpack("BH" + 'c' * (calcsize(data) - 3), data)
    print "The server's response to your type-" + str(msg_code) + " request:"
    print unpacked_data[2]
    my_soc.close()


def main():
    client()


if __name__ == "__main__":
    main()

Tags: toipclientsenddatalenservercode
1条回答
网友
1楼 · 发布于 2024-04-20 16:33:25

我认为问题在于:

data_size = calcsize(client_structs) - 3

客户端发送的数据似乎是任意二进制数据,而不是struct格式的字符串。你不能就此打电话给^{}

例如,如果我在客户机上选择1,它将把消息代码1压缩为一个字节,把数字0压缩为一个短字节,因此它将发送b'\x01\x00\x00'。但是在服务器上,您接收到它并尝试将其用作struct格式。因此,如果说b'\x01'不是有效的格式代码,就会得到一个错误。

既然你给了我们大量不工作的代码,却没有解释它应该如何工作,很难猜测你应该在这里做什么,只是,不管你想要什么,这不可能是实现它的方法。

看起来您的格式总是一个1字节的代码,一个2字节的长度,然后是一堆与该长度匹配的任意字符。如果是这样的话,解析它的方法如下:

code, length = unpack('BH', buffer[:3])
other_stuff = unpack('c' * length, buffer[3:])

尽管事实上,使用packunpack来打包一个字节串并没有多大意义;您只需要得到已经在buffer[3:]中的相同内容,而在另一方面,您只需要打包path或它本身的任何其他内容。

不管怎样,这只是一个猜测,因为我实际上不知道您希望代码如何工作,所以这可能不是您希望它做的。


与此同时,你至少还有一个严重的问题需要解决。TCP sockets are byte streams, not message streams。当您从客户端执行send操作时,它可能会在服务器上的两个recv上出现,或者与以前的send合并。你不能只是recv假设你有一条完整的消息。

这个问题最糟糕的地方是,当您在一台没有负载的机器上测试本地主机套接字时,它99.9999%的时间“工作”,这意味着您可能没有意识到自己有问题。但是,一旦你试图在互联网上部署它,或者在你的机器忙的时候运行它,它就会开始到处失败,直到那时你才意识到要调试这个问题,你必须编写大量的代码,并且经常重组你的整个程序。

您似乎设计了一个协议,其中包含足够的信息,可以将消息从流中分离出来。但你必须真正做到这一点,而不是期望它像魔法一样发生。

相关问题 更多 >