6

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

看来,在读取数据的过程中,有时我只得到比这更大的数据包的 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
...

发送者通过一次 send 调用发送完整的数据报。

编辑:整个事情都在本地主机上运行——因此不涉及复杂的网络应用程序(路由器等)。BUFSIZE 至少为 512,发送方发送至少 4 个字节。

4

8 回答 8

15

我假设您正在使用 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;
}
于 2009-08-09T14:43:25.513 回答
9

据我所知,这种行为是完全合理的。套接字可能并且可能在传输数据时将您的数据分段。您应该准备好通过应用适当的缓冲技术来处理此类情况。

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

编辑:一个想法-尝试启动数据包嗅探器并查看传输的数据包何时已满;每当您的错误在您的客户端或服务器中时,这可能会给您一些洞察力。

于 2009-08-09T13:45:38.213 回答
5

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

>>> 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()将返回它已读取的内容 - 这将比您要求的要少(但一致 < 4 字节似乎不太可能)。

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

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

于 2009-08-10T04:39:06.937 回答
3

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

您不能假设 TCP 网络与操作系统管道相同。使用管道,它是所有软件,因此对于大多数消息一次传递整个消息是没有成本的。对于网络,您必须假设会有时间问题,即使在简单的网络中也是如此。

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

另外,从您的帖子中听起来您确定发件人正在发送您需要发送的数据,但为了完整起见,请检查: http ://beej.us/guide/bgnet/output/html/multipage/高级.html#sendall

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

于 2009-08-09T14:55:53.100 回答
1

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

接收调用通常会返回任何可用的数据,直到请求的数量,而不是等待接收请求的全部数量。

因此,如果您的发件人仍在传输字节,则该调用只会给出到目前为止已传输的内容。

于 2009-08-09T13:45:40.773 回答
1

如果发送者发送了 515 个字节,而你的 BUFSIZE 是 512,那么第一个 recv 将返回 512 个字节,下一个将返回 3 个字节......这是怎么回事?

(这只是众多案例中的一种,它将导致来自较大发送的 3 字节接收......)

于 2009-08-09T13:47:29.477 回答
1

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

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

可能会创建愚蠢的窗口的东西。

看看这个_

于 2009-09-27T22:15:33.980 回答
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

于 2019-03-31T23:21:44.200 回答