如何在Python套接字中接收和组合长度可变的字节数组?

时间:2019-03-19 12:41:54

标签: python arrays sockets protocol-buffers

我正在尝试将Protobuf类的大型字节数组从Java客户端发送到Python服务器。但是,它们的长度是变量,因为有时我从ClassA发送对象的字节,有时从ClassB发送对象的字节。

我有一个Python套接字服务器,在侦听套接字的函数中包含以下代码:

byte_array = bytearray()

# receive the data in small chunks and print it
while True:
    data = connection.recv(64)
    if data:
        # output received data
        logger.debug("Data: %s" % data)
        byte_array.extend(data)

    else:
        # no more data -- quit the loop
        logger.debug("no more data.")
        break

logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

我通过将获得的64个字节放在一起来组装接收到的大字节数组。

但是,当字节数组已完全发送并且没有剩余要发送的内容时,服务器将挂在connection.recv行上。

我读到这是因为recv一直阻塞,直到它接收到某些东西或连接被关闭。但是,我不想关闭连接,因为我想在处理完整个字节数组后将响应发送回客户端。

我想知道我接收的字节数组何时已完全传输,,以便避免这种阻塞。

我可以想到三种选择:

  • 设置预定义的“结束”字节,以字节数组的结尾定界。
  • 事先发送字节数组的大小,然后我有一个while True周期,而不是while bytes_read < expected_bytes
  • 在连接上设置超时,我假设发生超时意味着所有内容都已发送。

我倾向于第一种选择,但是我不知道应该使用什么字符来结束字节数组,也不知道如何在Python代码中读取它。

有什么建议吗?

谢谢。

2 个答案:

答案 0 :(得分:3)

I would personally go for the second option (combined with a reasonable timeout to cater for evil clients that send only half of the file and hang there forever). Delimiting character is good if you can absolutely guarantee it is unique in your stream (but you still need the timeout).

If you cannot guarantee your delimiter to be unique, sending the size the client needs to expect solves the problem. If your metadata is padded to a fixed length, you do not need to worry about delimiters and detecting them.

答案 1 :(得分:1)

选项1:

因此,对于第一个选项,您可以设置结束字节,该字节不会出现在实际消息中的任何地方。 您可以为例如“ END”创建一个字符串,并将其转换为字节数组,然后通过Java程序发送。收到后,您可以使用decode()将其转换为字符串并进行比较。 :

注意:您将发送的结束字节必须小于或等于要解码并获取准确的结束字节的块的大小。

byte_array = bytearray()

# receive the data in small chunks and print it
while True:
    data = connection.recv(64)
    command = data.decode()
    if command != "END":
        # output received data
        logger.debug("Data: %s" % data)
        byte_array.extend(data)

    else:
        # no more data -- quit the loop
        logger.debug("no more data.")
        break

logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

选项2:

对于第二个选项,您将需要修改while循环以根据元数据运行。我已经考虑过元数据将由第一个块组成,这将是要发送的块的数量。它可能类似于:

byte_array = bytearray()

# receive the data in small chunks and print it
loop_count = 0
count = 1
meta = 1
while loop_count >= count:
    data = connection.recv(64)
    if(meta):
        count = int(data.decode()) # first chunk is the number of chunks that will be sent 
        meta = 0
    logger.debug("Data: %s" % data)
    byte_array.extend(data)
    loop_count = loop_count + 1
else:
    # no more data
    logger.debug("no more data.")
logger.info("Generating response...")
connection.send(generate_response(byte_array))
logger.info("Sent response.")

选项3:

如果您确定没有网络延迟,并且只有问题是Java程序必须等待python服务器的响应,直到超时发生,它也可以正常工作

选项4:

您可以使用非阻塞套接字,该套接字将一直运行直到在预定的时间段内未收到该套接字为止。尽管我不建议您使用它,但您可以阅读并查看它是否适合您的需求。