使用非阻塞UDP读取时缺少消息

8

我在使用两台主机之间的udp非阻塞读取时遇到了消息丢失的问题。发送者位于linux上,接收者位于winxp上。以下python示例展示了这个问题。
这里有三个脚本用于展示这个问题。
send.py:

import socket, sys
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
host = sys.argv[1]
s.sendto('A'*10,   (host,8888))
s.sendto('B'*9000, (host,8888))
s.sendto('C'*9000, (host,8888))
s.sendto('D'*10,   (host,8888))
s.sendto('E'*9000, (host,8888))
s.sendto('F'*9000, (host,8888))
s.sendto('G'*10,   (host,8888))

read.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('',8888))
while True:
    data,address = s.recvfrom(10000)
    print "recv:", data[0],"times",len(data) 

read_nb.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(('',8888))
s.setblocking(0)
data =''
address = ''
while True:
    try:
        data,address = s.recvfrom(10000)
    except socket.error:
        pass
    else: 
        print "recv:", data[0],"times",len(data) 

示例1(正常工作):

ubuntu> python send.py
winxp > read.py

read.py返回以下结果:

recv: A次数10
recv: B次数9000
recv: C次数9000
recv: D次数10
recv: E次数9000
recv: F次数9000
recv: G次数10

示例2(消息丢失):
在这种情况下,短消息通常无法被read_nb.py捕获,我提供两个示例。

ubuntu> python send.py
winxp > read_nb.py

read_nb.py返回以下结果:

recv: A次数10
recv: B次数9000
recv: C次数9000
recv: D次数10
recv: E次数9000
recv: F次数9000

上面缺少了最后10个字节的消息

以下是中间缺失的10个字节消息

recv: A次数10
recv: B次数9000
recv: C次数9000
recv: E次数9000
recv: F次数9000
recv: G次数10

我已经在Windows上使用wireshark检查过,每次所有消息都被捕获,因此它们达到了主机接口但未被read_nb.py捕获。这是为什么?

我还尝试过在Linux上使用read_nb.py,然后在Windows上使用send.py,然后它可以正常工作。所以我认为这个问题与winsock2有关。

或者我可能错误地使用了非阻塞的UDP?

2个回答

8

如果数据报文已经到达主机(如您的Wireshark日志所示),则我将首先查看您的套接字接收缓冲区大小,尽可能将其设置为最大,并以最快速度运行。

当然,这在UDP中完全是预期的。您应该假定数据报在任何时间和任何原因都可能被丢弃。还可能会多次收到数据报...

如果您需要可靠性,则需要构建自己的可靠性机制或使用TCP。


这里有+1和正义送给你 :) - Nikolai Fetissov

8

使用UDP丢失消息是正常的 - 传输层不保证数据报的顺序或交付。如果您希望按顺序和/或始终传递它们,请切换到TCP或自己实现排序和/或ack/timeout/retransmission。

就您的示例而言 - 大型消息大于普通以太网MTU 1500减去8个字节的UDP头(除非您使用Jumbo Frames),因此发送方将对其进行分段。这会使发送方和接收方的负载更重,但接收方的负载更大,因为它需要在内核内存中保留片段,直到完整数据报到达。

我怀疑您用36030个字节溢出了接收缓冲区,但我从不在Windows上进行网络处理,所以最好像@Len建议的那样检查接收器上SO_RECVBUF套接字选项的值。

还要检查netstat -s的输出,以查看丢弃的数据包计数。


2
Windows:s.getsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF)=8192字节。 Ubuntu:buf=112640字节。 - lgwest
好的,这就是问题所在。知道了很好。 - Nikolai Fetissov
1
嗯,所以我提出了解决方案,但尼古拉得到了采纳的答案,这太不公平了 :( - Len Holgate
你说得没错,抱歉。但是这个答案对我更有用,因为它提供了使用(几乎)的选项。我之前不知道SO_RCVBUF。 - lgwest
将缓冲区大小设置为大于64k在Windows上无效。 - lgwest
是的,它可以。除非在连接之前进行设置,否则它不会传播到TCP窗口大小,但这适用于所有平台,我们正在讨论的是没有窗口控制的UDP协议。 - user207421

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