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

8 投票
2 回答
13943 浏览
提问于 2025-04-16 05:41

我在使用非阻塞读取UDP消息时遇到了丢失消息的问题,这个问题发生在两个主机之间。发送方在Linux上,接收方在Windows XP上。下面的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得到的结果是:

接收到:A 次 10
接收到:B 次 9000
接收到:C 次 9000
接收到:D 次 10
接收到:E 次 9000
接收到:F 次 9000
接收到:G 次 10

示例2(丢失消息):
在这种情况下,短消息经常会被read_nb.py漏掉。我给出两个例子来说明这个问题。

ubuntu> python send.py
winxp > read_nb.py

从read_nb.py得到的结果是:

接收到:A 次 10
接收到:B 次 9000
接收到:C 次 9000
接收到:D 次 10
接收到:E 次 9000
接收到:F 次 9000

上面是最后一个10字节的消息丢失了

下面是中间的一个10字节的消息丢失了

接收到:A 次 10
接收到:B 次 9000
接收到:C 次 9000
接收到:E 次 9000
接收到:F 次 9000
接收到:G 次 10

我用Wireshark在Windows上检查过,每次所有消息都被捕获,所以它们确实到达了主机接口,但没有被read_nb.py捕获。这是什么原因呢?

我还尝试过在Linux上使用read_nb.py,在Windows上使用send.py,这样就能正常工作。
所以我觉得这个问题可能和winsock2有关。

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

2 个回答

8

如果数据包已经到达主机(就像你在wireshark日志中看到的那样),那么我首先会检查一下你的socket接收缓冲区的大小,尽量把它调大,然后尽可能快地运行。

当然,这在使用UDP时是完全正常的。你应该明白,数据包可能会在任何时候、出于任何原因被丢弃。此外,你可能会收到重复的数据包……

如果你需要可靠性,那你就得自己去实现,或者使用TCP协议。

8

使用UDP时丢失消息是很正常的,因为它的传输层不保证数据包的顺序或送达。如果你需要保证消息的顺序或者一定要送达,那就换成TCP,或者自己实现一些机制,比如排序、确认、超时和重传。

关于你的例子,较大的消息超过了正常以太网的最大传输单元(MTU),这个值是1500字节,减去8字节的UDP头部(除非你使用的是超大帧)。因此,发送方会把这些消息分成小块发送。这会增加发送方和接收方的负担,但接收方的负担更重,因为它需要在内存中保存这些小块,直到完整的数据包到达。

我不太相信你会因为36030字节而导致接收缓冲区溢出,但我自己从来不在Windows上做网络编程,所以你最好按照@Len的建议检查一下接收方的SO_RECVBUF这个套接字选项的值。

另外,检查一下netstat -s的输出,看看丢包的数量。

撰写回答