从套接字读取:是否保证至少获得x个字节?

时间:2009-08-09 13:35:12

标签: python sockets network-protocols

我有一个罕见的错误,似乎是在读取套接字。

看来,在读取数据时,有时我只得到1-3个大于此数据包的字节。

正如我从管道编程中学到的那样,只要发送方提供足够的数据,我总会得到至少512个字节。

此外,我的发送者在传输任何内容的时候至少发送> = 4字节 - 所以我想在传输的开始(!!)中一次只能接收到4个字节。

在99.9%的情况下,我的假设似乎有......但是当收到的字节少于4个字节时,确实很少见。在我看来,为什么网络系统应该这样做呢?

有人知道更多吗?

这是我使用的阅读代码:

mySock, addr = masterSock.accept()
mySock.settimeout(10.0)
result = mySock.recv(BUFSIZE)
# 4 bytes are needed here ...
...
# read remainder of datagram
...

发件人通过一次发送电话发送完整的数据报。

编辑:整个过程都在使用localhost - 因此不涉及复杂的网络应用程序(路由器等)。 BUFSIZE至少为512,发送方至少发送4个字节。

8 个答案:

答案 0 :(得分:13)

我假设您正在使用TCP。 TCP是一种基于流的协议,不知道数据包或消息边界。

这意味着当您执行读取操作时,您可能获得的字节数少于您请求的字节数。例如,如果您的数据是128k,那么您在第一次读取时可能只需要24k,这需要您再次阅读以获取其余数据。

以C:

为例
int read_data(int sock, int size, unsigned char *buf) {
   int bytes_read = 0, len = 0;
   while (bytes_read < size && 
         ((len = recv(sock, buf + bytes_read,size-bytes_read, 0)) > 0)) {
       bytes_read += len;
   }
   if (len == 0 || len < 0) doerror();
   return bytes_read;
}

答案 1 :(得分:9)

据我所知,这种行为是完全合理的。套接字可能会在发送数据时 对数据进行分段。您应该准备好通过适当的缓冲技术来处理这种情况。

另一方面,如果您在localhost上传输数据,而您确实只获得了4个字节,则可能意味着您的代码中的其他位置存在错误。

编辑:一个想法 - 尝试启动数据包嗅探器并查看传输的数据包是否已满;这可能会在您的客户端或服务器中出现错误时提供一些见解。

答案 2 :(得分:5)

您的问题的简单答案,“从套接字读取:是否保证至少获得x个字节?”,。查看这些套接字方法的doc字符串:

>>> import socket
>>> s = socket.socket()
>>> print s.recv.__doc__
recv(buffersize[, flags]) -> data

Receive up to buffersize bytes from the socket.  For the optional flags
argument, see the Unix manual.  When no data is available, block until
at least one byte is available or until the remote end is closed.  When
the remote end is closed and all data is read, return the empty string.
>>> 
>>> print s.settimeout.__doc__
settimeout(timeout)

Set a timeout on socket operations.  'timeout' can be a float,
giving in seconds, or None.  Setting a timeout of None disables
the timeout feature and is equivalent to setblocking(1).
Setting a timeout of zero is the same as setblocking(0).
>>> 
>>> print s.setblocking.__doc__
setblocking(flag)

Set the socket to blocking (flag is true) or non-blocking (false).
setblocking(True) is equivalent to settimeout(None);
setblocking(False) is equivalent to settimeout(0.0).

很明显,recv()不需要返回您要求的字节数。此外,由于您正在调用settimeout(10.0),因此有可能在recv()的到期时间附近收到一些(但不是全部)数据。在这种情况下,recv()将返回它已读取的内容 - 这将比您要求的少(但是一致的&lt; 4字节似乎不太可能)。

您在问题中提到datagram,这意味着您正在使用(无连接)UDP套接字(而不是TCP)。区别是described here。发布的代码不显示套接字创建,所以我们只能在这里猜测,但是,这个细节可能很重要。如果您可以发布更完整的代码示例,可能会有所帮助。

如果问题是可重现的,您可以禁用超时(顺便提一下,您似乎没有处理),看看是否能解决问题。

答案 3 :(得分:3)

这就是TCP的工作方式。您不会立即获得所有数据。发送方和接收方之间存在太多的时序问题,包括发送方操作系统,NIC,路由器,交换机,电线本身,接收方NIC,OS等。硬件和操作系统中都有缓冲区。

您不能假设TCP网络与OS管道相同。使用管道,它是所有软件,因此对于大多数消息,一次性传递整个消息是免费的。通过网络,您必须假设即使在简单的网络中也会出现计时问题。

这就是为什么recv()无法一次性提供所有数据,即使一切正常,它也可能无法使用。通常,您将调用recv()并捕获输出。这应该告诉你你收到了多少字节。如果它低于您的预期,您需要继续调用recv()(如建议的那样),直到获得正确的字节数。请注意,在大多数情况下,recv()在出错时返回-1,因此请检查并检查文档中的ERRNO值。特别是EAGAIN似乎会引起人们的问题。您可以在互联网上阅读有关详细信息,但如果我记得,这意味着目前没有数据可用,您应该再试一次。

另外,从您的帖子中可以看出,您确定发件人正在发送您需要发送的数据,但为了完成,请检查以下内容: http://beej.us/guide/bgnet/output/html/multipage/advanced.html#sendall

你应该在recv()端做类似的事情来处理部分接收。如果您有一个固定的数据包大小,您应该阅读,直到您获得预期的数据量。如果你有一个可变的数据包大小,你应该阅读,直到你有一个标题告诉你发送了多少数据(),然后读取更多的数据。

答案 4 :(得分:1)

来自recv http://linux.about.com/library/cmd/blcmdl2_recv.htm的Linux手册页:

  

接收电话通常会返回任何电话   数据可用,直到请求   金额,而不是等待   收到所要求的全额。

因此,如果您的发送方仍在发送字节,则该呼叫将仅提供到目前为止已发送的内容。

答案 5 :(得分:1)

如果发送方发送515个字节,并且您的BUFSIZE为512,那么第一个recv将返回512个字节,下一个将返回3个字节​​......这可能是发生了什么?

(这只是众多中的一个案例,会导致较大发送的3字节recv ...)

答案 6 :(得分:1)

如果您仍然感兴趣,可以使用以下模式:

# 4 bytes are needed here ......
# read remainder of datagram...

可能会造成愚蠢的窗口。

检查this

答案 7 :(得分:0)

使用recv_into(...)模块中的socket方法。

Robert S. Barnes用C语言编写了示例。

但是您可以将Python 2.x与标准python库一起使用:

def readReliably(s,n):
    buf = bytearray(n)
    view = memoryview(buf)
    sz = s.recv_into(view,n)
    return sz,buf

while True:
  sk,skfrom = s.accept()
  sz,buf = io.readReliably(sk,4)
  a = struct.unpack("4B",buf)
  print repr(a)
  ...

请注意,sz函数返回的readReliably()可能大于n