python - 带有神秘缓冲区的套接字

标签 python sockets recv

我正在构建一个基于 python 的接口(interface),用于通过 TCP 从仪器中提取数据。数据流作为特定事件出现,并且时间不稳定:我得到数据突发,然后是缓慢的时期。它们是小数据包,因此为简单起见,假设它们是完整的数据包。

这是我从套接字得到的行为:

  • 发送事件 #1:socket.recv 返回事件 #1
  • 发送事件 #2:socket.recv 返回事件 #2
  • 快速发送事件 #3-50:socket.recv 仅返回事件 #3-30(返回 27 次)
  • 缓慢发送事件 #51:socket returns.recv 事件 #31
  • 缓慢发送事件 #52:socket returns.recv 事件 #32

没有数据丢失。但显然某处有一个缓冲区已满,套接字现在正在返回旧数据。但是 recv 不应该一直返回直到缓冲区为空吗?相反,它只在收到新数据包时才返回,尽管已经建立了数据包缓冲区。奇怪!

这是代码的本质(这是非阻塞的,我也只用 recv 完成了阻塞——同样的结果)。为了简单起见,我剥离了所有数据包重组的东西。我已经仔细地追溯到 socket ,所以我知道这不是罪魁祸首。

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)

为什么我不能让套接字将所有数据转储到它的缓冲区中?我永远追不上!这个太奇葩了。有人指点一下吗?

我是一个业余爱好者,但我真的在这方面做了功课,但我完全不知所措。

最佳答案

packet = self.socket.recv(4096)
self.recvqueue.put_nowait(packet)

TCP 是一种基于流的协议(protocol),而不是基于消息的协议(protocol)。它不保留消息边界。这意味着您不能指望每条消息调用一次 recv()。如果你突发发送数据,Nagle's algorithm将数据组合成一个 TCP 数据包。

您的代码假定每个 recv() 调用返回一个“数据包”,并且解析线程打印每个“数据包”的第一个数字。但是 recv() 不返回数据包,它从 TCP 流返回数据 block 。这些 block 可以包含一条消息或多条消息甚至部分消息。无法保证前两个字节始终是事件编号。

通常,从 TCP 连接读取数据涉及多次调用 recv() 并将获得的数据存储在缓冲区中。收到完整消息后,您便会从缓冲区中删除适当数量的字节并进行处理。

如果您有可变长度的消息,那么您需要自己跟踪消息边界。 TCP 不像 UDP 那样为您做这件事。这意味着在每条消息的前面添加一个包含消息长度的 header 。

try:
   xmitmsg = self.sendqueue.get_nowait()
except Queue.Empty:
   pass
else:
   if writable:
       self.socket.send(xmitmsg)

另一方面,这段代码似乎有一个错误。无论套接字是否可写,它都会从发送队列中删除消息。如果套接字不可写,它会默默地丢弃消息。

关于python - 带有神秘缓冲区的套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19738484/

相关文章:

C++ Winsock recv() 缓冲区垃圾

c - 为什么 recv() 改变了不相关的变量

c - 通过send() recv()发送多条消息,Socket编程,C

python - 为什么 numba 在这个简单的求和上要快得多?

python - operator.itemgetter() 和 sort() 是如何工作的?

javascript - Node.js 中的 TCP 套接字和文件操作

c# - 使用ZeroMQ通过Internet进行客户端/服务器通信

python - 如果值为 0,则隐藏 matplot 注释

python - 使用 python 添加新列,具体增加四分之一

linux - Linux 中非常奇怪的连接时间