我正在构建一个基于python的界面,用于从仪器上通过TCP提取数据。数据流作为特定事件发生,并且时间不稳定:我获得数据突发然后缓慢。它们是小数据包,因此为简单起见假设它们是完整的数据包。
以下是我从套接字获得的行为:
没有数据丢失。但是显然有一个填充的缓冲区,套接字现在返回旧数据。但是不应该recv只是继续返回,直到缓冲区为空?相反,它只在收到新数据包时返回,尽管已经建立了数据包缓冲区。怪异!
这是代码的本质(这是针对非阻塞的,我也使用recv进行了阻塞 - 相同的结果)。为简单起见,我删除了所有数据包重组的东西。我已经仔细地将它追溯到插座,所以我知道这不应该受到责备。
class mysocket:
def __init__(self,ip,port):
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.connect((ip,port))
self.keepConn = True
self.socket.setblocking(0)
threading.Thread(target = self.rcvThread).start()
threading.Thread(target = self.parseThread).start()
def rcvThread(self):
while self.keepConn:
readable,writable,inError = select([self.socket],[self.socket],[],.1)
if readable:
packet = self.socket.recv(4096)
self.recvqueue.put_nowait(packet)
try:
xmitmsg = self.sendqueue.get_nowait()
except Queue.Empty:
pass
else:
if writable:
self.socket.send(xmitmsg)
def parseThread(self,rest = .1):
while self.keepConn:
try:
output = self.recvqueue.get_nowait()
eventnumber = struct.unpack('<H',output[:2]
print eventnumber
except Queue.Empty:
sleep(rest)
为什么我不能让套接字将所有数据转储到它的缓冲区中?我永远无法赶上!这个太奇怪了。有人有指针吗?
我是一个业余爱好者,但我真的完成了我的作业,我完全感到困惑。
答案 0 :(得分:3)
packet = self.socket.recv(4096)
self.recvqueue.put_nowait(packet)
TCP是基于流的协议,而不是基于消息的协议。它不保留消息边界。这意味着您不能指望每条消息都有一个recv()
次呼叫。如果以突发方式发送数据,Nagle's algorithm会将数据合并为一个TCP数据包。
您的代码假定每个recv()
调用返回一个“数据包”,并且解析线程打印每个“数据包”中的第一个数字。但recv()
不返回数据包,它返回TCP流中的数据块。这些块可以包含一条消息或多条消息,甚至包含部分消息。无法保证前两个字节始终是事件编号。
通常,从TCP连接读取数据涉及多次调用recv()
并将您获得的数据存储在缓冲区中。收到整条消息后,从缓冲区中删除适当数量的字节并处理它们。
如果您有可变长度的消息,那么您需要自己跟踪消息边界。 TCP不像UDP那样为你做这件事。这意味着将包含消息长度的标头添加到每条消息的前面。
try:
xmitmsg = self.sendqueue.get_nowait()
except Queue.Empty:
pass
else:
if writable:
self.socket.send(xmitmsg)
另一方面,看起来这段代码有一个bug。无论套接字是否可写,它都会从sendqueue中删除消息。如果套接字不可写,它将默默地丢弃消息。