Python TCP接收到的数据偶尔错位

Python TCP接收到的数据偶尔错位,python,python-3.x,sockets,tcp,buffering,Python,Python 3.x,Sockets,Tcp,Buffering,我正在通过TCP与一台实验室设备通信。设备有一个命令集,并将回复每个命令,确认已收到该命令以及该命令中请求的任何数据。问题在于,例如,当使用socket.recv()或其任何变体在Isend()命令后从设备获取响应时,该方法似乎会在接收到任何数据时返回,而不是在接收到我想要/期望的所有数据时返回。这会导致一些数据不在我期望的recv()调用中,而是出现在下一个调用中 我正在考虑的一个解决方案是,使用重复的recv()调用,将接收到的数据与发送的数据完全分开/异步地处理,并对其进行解析,但如果我认

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

我正在考虑的一个解决方案是,使用重复的
recv()
调用,将接收到的数据与发送的数据完全分开/异步地处理,并对其进行解析,但如果我认为可以用一种简单的方法来使用我所知道的有关接收数据的信息,那么这似乎会带来很大的开销(例如,它总是以回车和换行结束,但我不知道该消息需要多长时间)等待,直到接收到整个消息,不再发送

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

。如果您想要一个消息流,您必须在此基础上定义一个协议,并编写代码来处理该协议中的发送和接收数据

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

如果您处理网络的方式是阻塞
recv
(无论是在程序的主循环中,还是在专用于读取套接字的线程循环中),则此协议具有内置支持:使用适当的模式调用
sock.makefile
r
加上一个编码,如果您想要消息的Unicode字符串,
rb
如果您想要原始字节),您可以像文件一样使用它,例如,文件中的msg的
循环:
循环,或者在
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”更简单(这样您也可以得到错误处理和优化).

根据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就是这样工作的。如果你想要一个消息流,你必须在一个循环(或者异步回调,或者其他什么)上构建它关于
recv
和一个缓冲区和代码,它实现了您选择的一些消息帧协议。我只是好奇是否有一种预构建的方法来处理帧,可能是从一个更高级别的库或我可以安装的包。我不认为我描述的是一个新问题,我只是有点惊讶我没有找到我想要的寻找。有一百万种协议,从简单的netstring或jsonline,到xmlrpc over http。另外还有六种实现协议处理的方法,这取决于您首先从网络读取的方式(使用或不使用线程阻止读取、选择循环、回调、异步…)大多数组合都有一个STDLIB或第三方库来完成大部分的工作,但是首先你必须选择一个。你可以考虑把接收分成一个只产生有效响应的生成器函数。这将允许你的发送和接收代码解耦,并允许更优雅地处理超时和OT。她的问题和例外。