TCP接收到的数据偶尔错位

2024-06-01 11:05:48 发布

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

我正在通过TCP与一台实验室设备通信。该设备有一个命令集,将回复每个命令,并确认已收到命令和命令中要求的任何数据。问题是,当使用例如socket.recv()或其任何变体在I send()命令之后从设备获得响应时,该方法似乎在接收到任何数据时返回,而不是在接收到所有我想要/期望的数据时返回。这导致一些数据不在我期望的recv()调用中,而是在下一个调用中显示

我正在考虑的一个解决方案是,将接收到的数据与发送的数据完全分开/异步地处理,并使用重复的recv()调用对其进行解析,但是,当我想象有一种简单的方法来使用我所知道的关于接收到的数据的信息(例如,它总是以回车和换行结束,但我不知道消息要多长时间)来等待整个消息被接收到而不再是

总结一下:有没有一种现有的方法可以通过TCP以更受控制的方式接收数据,从而使数据在我期望的地方结束


Tags: 数据方法命令send信息消息socket变体
2条回答

根据abarnert的建议,我可以利用我所知道的关于我接收到的数据的信息来构建它。具体地说,我正在谈论的东西向我抛出了很多我不想要的垃圾行,所以我只是在每一行中搜索我知道的与我关心的内容相关的子字符串:

def send_message_return_response(sock, sock_file, message, substring):
   #discard remainders from commands I sent but didn't read back due to not caring
   sock_file.flush()
   sock.send(message)
   response = ''
   while substring not in response: response = sock_file.readline()
   return response

TCP sockets are streams of bytes, not streams of messages.。如果你想要一个消息流,你必须在上面定义一个协议,并用代码来处理该协议中的发送和接收数据

如果您的消息都是字符串,并且从不包含换行符,那么最简单的协议可能就是用换行符分隔消息。我想你已经解决了,你只需要知道如何实现它

如果您处理网络的方式是阻塞recv(无论是在程序的主循环中,还是在专用于读取套接字的线程的循环中),则内置了对该协议的支持:使用适当的模式调用sock.makefiler加上编码,如果您希望消息使用Unicode字符串,rb如果您想要原始字节),您可以像文件一样使用它—例如,for msg in file:循环,或者在file.readline()上使用while循环,直到您得到异常(表示套接字错误)或空字符串(表示EOF—一个干净的套接字关闭)

如果您的消息中可以有换行符,那么您仍然可以使用它。只需在发送前转义消息(可能使用完整的反斜杠转义,以便它们始终可读,以便于调试,或者只需msg.replace('\\', '\\\\').replace('\n', '\\n')),并在接收时取消转义

在封面下,这与普通文件对象处理磁盘文件的方式相同:当您请求下一行时,如果它在缓冲区中已经有一个完整的行,它只会将它拆分并返回它;如果没有,它读取缓冲区并将它们附加到它所拥有的内容上,直到最后得到一个换行符,然后拆分第一个完整的行并将其返回给您。因此,如果第一个包包含换行符,它将永远不会阻止等待两个包。但它也永远不会给你一个“还没有完整的信息”来处理;它将一直阻塞,直到它读取足够的数据包以获得下一条换行

在某个时候,学习如何从头开始构建这样的东西是值得的,但与此同时,您可以使用已经存在的东西。如果您感兴趣,短版本(没有良好的错误处理和一些有用的优化)如下所示:

def messages(sock):
    buf = b''
    while True:
        data = sock.recv(8192)
        if not data: break
        buf += data
        lines = buf.split('\n')
        for line in lines[:-1]:
            yield line.decode('utf8')
        buf = lines[-1]
    # Should leftover bytes after the last newline be a message, an error, or ignored? Picking arbitrarily...
    if buf: yield buf.decode('utf8')

当然,只调用“makefile”更简单(这样也可以得到错误处理和优化)

相关问题 更多 >