Python套接字接收-传入的数据包总是有不同的大小 [英] Python socket receive - incoming packets always have a different size

查看:30
本文介绍了Python套接字接收-传入的数据包总是有不同的大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm using the SocketServer module for a TCP server. I'm experiencing some issue here with the recv() function, because the incoming packets always have a different size, so if I specify recv(1024) (I tried with a bigger value, and smaller), it gets stuck after 2 or 3 requests because the packet length will be smaller (I think), and then the server gets stuck until a timeout.

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()

如果客户端通过同一个源端口发送多个请求,但服务器卡住了,任何帮助将不胜感激,谢谢!

If the client sends multiples requests over the same source port, but the server gets stuck, any help would be very appreciated, thank !

推荐答案

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

The network is always unpredictable. TCP makes a lot of this random behavior go away for you. One wonderful thing TCP does: it guarantees that the bytes will arrive in the same order. But! It does not guarantee that they will arrive chopped up in the same way. You simply cannot assume that every send() from one end of the connection will result in exactly one recv() on the far end with exactly the same number of bytes.

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

When you say socket.recv(x), you're saying 'don't return until you've read x bytes from the socket'. This is called "blocking I/O": you will block (wait) until your request has been filled. If every message in your protocol was exactly 1024 bytes, calling socket.recv(1024) would work great. But it sounds like that's not true. If your messages are a fixed number of bytes, just pass that number in to socket.recv() and you're done.

但是如果您的消息可以有不同的长度怎么办?您需要做的第一件事是:停止使用明确的数字调用 socket.recv().改变这个:

But what if your messages can be of different lengths? The first thing you need to do: stop calling socket.recv() with an explicit number. Changing this:

data = self.request.recv(1024)

为此:

data = self.request.recv()

表示 recv() 每次获取新数据时都会返回.

means recv() will always return whenever it gets new data.

但是现在你有一个新问题:你怎么知道发件人什么时候给你发送了一条完整的消息?答案是:你没有.您将不得不使消息的长度成为协议的明确部分.这是最好的方法:为每条消息添加一个长度前缀,可以是固定大小的整数(使用 socket.ntohs()socket.ntohl() 请!)或作为字符串后跟一些分隔符(如123:").第二种方法通常效率较低,但在 Python 中更容易.

But now you have a new problem: how do you know when the sender has sent you a complete message? The answer is: you don't. You're going to have to make the length of the message an explicit part of your protocol. Here's the best way: prefix every message with a length, either as a fixed-size integer (converted to network byte order using socket.ntohs() or socket.ntohl() please!) or as a string followed by some delimiter (like '123:'). This second approach often less efficient, but it's easier in Python.

一旦您将其添加到您的协议中,您需要更改您的代码以随时处理 recv() 返回任意数量的数据.下面是如何执行此操作的示例.我试着把它写成伪代码,或者用注释来告诉你该怎么做,但不是很清楚.所以我已经明确地使用长度前缀作为以冒号终止的数字字符串来编写它.给你:

Once you've added that to your protocol, you need to change your code to handle recv() returning arbitrary amounts of data at any time. Here's an example of how to do this. I tried writing it as pseudo-code, or with comments to tell you what to do, but it wasn't very clear. So I've written it explicitly using the length prefix as a string of digits terminated by a colon. Here you go:

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

这篇关于Python套接字接收-传入的数据包总是有不同的大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆