Python套接字接收-传入的数据包总是具有不同的大小

2024-03-28 23:53:05 发布

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

我使用SocketServer模块作为TCP服务器。 我在这里遇到了recv()函数的一些问题,因为传入的数据包总是有不同的大小,所以如果我指定recv(1024)(我尝试使用一个更大的值和更小的值),它会在2或3个请求后卡住,因为数据包长度会更小(我想),然后服务器会卡住,直到超时。

class Test(SocketServer.BaseRequestHandler):

def handle(self):

   print "From:", self.client_address

   while True:    

     data = self.request.recv(1024)
     if not data: break

     if data[4] == "\x20":              
       self.request.sendall("hello")
     if data[4] == "\x21":
       self.request.sendall("bye")
     else:
       print "unknow packet"
   self.request.close()
   print "Disconnected", self.client_address

launch = SocketServer.ThreadingTCPServer(('', int(sys.argv[1])),Test)

launch.allow_reuse_address= True;

launch.serve_forever()

如果客户机通过同一个源端口发送多个请求,但服务器被卡住,任何帮助都将非常感谢,谢谢!


Tags: testself服务器clienttruedataifaddress
3条回答

拉里·黑斯廷斯(Larry Hastings)的回答对sockets给出了一些很好的一般性建议,但是在Python socket模块中,recv(bufsize)方法的工作方式方面有一些错误。

因此,为了澄清,因为这可能会让其他寻求帮助的人感到困惑:

  1. recv(bufsize)方法的bufsize参数不是可选的。如果调用recv()(不带参数),将得到一个错误。
  2. recv(bufsize)中的bufferlen是一个最大大小。如果可用的字节更少,recv将愉快地返回更少的字节。

有关详细信息,请参见the documentation

现在,如果您从客户机接收数据,并想知道何时接收到所有数据,您可能需要将其添加到您的协议中——正如拉里建议的那样。有关确定消息结尾的策略,请参见this recipe

正如配方所指出的,对于某些协议,客户机在发送完数据后只需断开连接。在这种情况下,您的while True循环应该可以正常工作。如果客户机没有断开连接,则需要找到某种方法来表示内容长度、限定消息或实现超时。

如果你能发布你的客户机代码和测试协议的描述,我很乐意提供进一步的帮助。

网络总是不可预测的。TCP会让很多这种随机行为消失。TCP做了一件很好的事情:它保证字节以相同的顺序到达。但是!它不能保证它们会以同样的方式被切碎。您不能简单地假设连接一端的每个send()都会在远端产生一个recv(),字节数完全相同。

当你说socket.recv(x)时,你是说“在从套接字读取x字节之前不要返回”。这称为“阻塞I/O”:您将阻塞(等待)直到您的请求被填满。如果协议中的每条消息都是1024字节,那么调用socket.recv(1024)将非常有效。但听起来不是这样的。如果您的消息是固定字节数,只需将该数字传递给socket.recv(),就完成了。

但是如果你的信息长度不同呢?您需要做的第一件事是:停止使用显式数字调用socket.recv()。更改此:

data = self.request.recv(1024)

对此:

data = self.request.recv()

意味着recv()在获得新数据时总是返回。

但现在你又遇到了一个新问题:你如何知道发件人何时向你发送了完整的邮件?答案是:你不需要。你必须将消息的长度作为协议的一个明确部分。最好的方法是:在每条消息前面加上一个长度前缀,可以是一个固定大小的整数(使用socket.ntohs()socket.ntohl()转换为网络字节顺序,请!)或作为字符串后跟某个分隔符(如“123:”)。第二种方法通常效率较低,但在Python中更容易实现。

一旦将其添加到协议中,就需要更改代码来处理随时返回任意数量数据的recv()。下面是一个如何做到这一点的例子。我试着把它写成伪代码,或者用注释告诉你该怎么做,但不是很清楚。所以我用长度前缀作为以冒号结尾的数字字符串显式地写了它。给你:

length = None
buffer = ""
while True:
  data += self.request.recv()
  if not data:
    break
  buffer += data
  while True:
    if length is None:
      if ':' not in buffer:
        break
      # remove the length bytes from the front of buffer
      # leave any remaining bytes in the buffer!
      length_str, ignored, buffer = buffer.partition(':')
      length = int(length_str)

    if len(buffer) < length:
      break
    # split off the full message from the remaining bytes
    # leave any remaining bytes in the buffer!
    message = buffer[:length]
    buffer = buffer[length:]
    length = None
    # PROCESS MESSAGE HERE

您也可以使用recv(x_bytes, socket.MSG_WAITALL),它似乎只在Unix上工作,并将准确返回x_bytes

相关问题 更多 >