Python中的socket.recv()方法如何知道消息已经到达结尾?

26

假设我将客户端套接字的缓冲区大小设置为1024:

recv(1024)

假设服务器想要发送给我一个由2024个字节组成的消息。我的套接字只能接收1024个字节。其他1000个字节会发生什么?

  1. recv方法会等待一定的时间(比如2秒钟)来接收更多的数据,超过这个时间后就会停止工作吗?(即使剩余的数据在3秒钟后到达,也不会被套接字接收到?)

或者

  1. recv方法一旦接收到1024个字节的数据就会立即停止工作吗?(即另外的1000个字节会被丢弃?)

如果1.)是正确的... 是否有一种方法可以确定recv方法等待返回的时间或是否由系统决定?(例如,我能告诉套接字在停止等待更多数据之前等待5秒钟吗?)

更新:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((sys.argv[1], port))
    s.send('Hello, world')
    data = s.recv(1024)
    print("received: {}".format(data))
    s.close()

假设服务器发送的数据大小大于1024个字节。我能确定变量"data"将包含所有数据(包括超过第1024个字节的数据)吗? 如果不能确定,我该如何更改代码,以便始终可以确保变量"data"将包含从服务器发送的所有数据(在一个或多个步骤中)?


2
你告诉计算机接收1024字节的数据,它就会准确地执行。它并不关心是否有更多的数据需要读取。 - ForceBru
2
@ForceBru - 你说它确切地接收1024字节,但事实并非如此。我试图澄清这一点。 - tdelaney
1
假设这是一个TCP套接字,没有数据被丢弃。您可以接收最多1024个字节的内容,其余部分要么尚未发送,要么已缓存在内核中等待您请求它。 - tdelaney
1
如果数据<=1024,则您只知道套接字已接收到部分数据,不能假设它是所有数据。 - tdelaney
1
如果您正在使用TCP,则没有消息。只有一串字节流。 - melpomene
显示剩余16条评论
1个回答

27
这取决于协议。一些像UDP这样的协议发送消息,每个recv只返回一个消息。假设你特别是在谈论TCP,那么涉及到几个因素。TCP是面向流的,由于当前未处理的发送/接收数据量、线路上丢失/重排序的数据包、数据的延迟确认以及Nagle算法(它会将一些小的发送延迟数百毫秒),随着客户端和服务器之间的对话进行,其行为可能会发生微妙的变化。
所有接收方知道的是它正在接收一系列字节。在任何接收中,它都可以得到从1到完全请求的缓冲区大小的任何内容。在一侧进行的发送调用与另一侧的接收调用之间没有一一对应关系。
如果您需要确定消息边界,那么由更高级别的协议来解决。以HTTP为例。它以\r\n分隔的头开始,然后有一个计数器,指示客户端应该期望接收多少剩余字节。客户端知道如何读取标题,因为有\r\n,然后知道接下来要传输的确切字节数。RESTful协议的魅力部分在于它们基于HTTP,并且其他人已经解决了这些问题!
有些协议使用NUL来分隔消息。其他的可能有一个固定长度的二进制头,其中包括任何可变数据的计数。我喜欢zeromq,它在TCP之上具有强大的消息传递系统。
更多关于接收过程的细节...
当您执行recv(1024)时,会有6种可能性。
  1. 没有接收到数据。 recv 将一直等待接收数据。您可以通过设置超时来更改。

  2. 部分接收到数据。您将立即获得该部分。其余部分可能已缓冲或尚未发送,您只需再次进行接收以获取更多数据(相同的规则适用)。

  3. 有超过1024字节可用。您将获得1024个数据,并且剩余的数据将在内核中缓冲等待另一个接收。

  4. 对方已关闭套接字。您将获得0字节的数据。 0表示您将永远无法在该套接字上获取更多数据。但是,如果您继续请求数据,您将继续获得0字节。

  5. 对方已重置套接字。您将收到异常。

  6. 发生了其他奇怪的事情,您将因此收到异常。


我理解你的回答是:在TCP层面上,我无法确定recv()的行为(即它是否会在接收到一批数据后返回,或者是否会等待更多数据但在x秒后停止等待)。也就是说,确定何时停止等待/读取的策略只能在更高的层面上进行配置。......我的理解正确吗? - Tommy
1
是的,基本上就是这样。在TCP之上的高层协议通常需要知道如何处理数据。 - tdelaney
4
如果你使用UDP(或Unix数据报)套接字,当你调用recv()函数时,任何比缓冲区更长的数据都将被丢弃。而TCP(或流)套接字将会保留额外的数据供下一次recv()调用使用。 - Ralph Bolton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接