Python原始套接字监听UDP数据包;只收到一半的数据包

时间:2023-02-09 23:39:32

I am trying to create a raw socket in Python that listens for UDP packets only:

我试图在Python中创建一个仅监听UDP数据包的原始套接字:

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
s.bind(('0.0.0.0', 1337))
while True:
    print s.recvfrom(65535)

This needs to be run as root, and creates a raw socket on port 1337, which listens for UDP packets and prints them whenever they are received; no problems there.

这需要以root用户身份运行,并在端口1337上创建一个原始套接字,它接收UDP数据包并在收到它们时打印它们;没有问题。

Now let's make a little client to test if this works:

现在让我们做一个小客户端来测试它是否有效:

import socket
c = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
c.connect(('127.0.0.1', 1337))
c.send('message 1')
c.send('message 2')
c.send('message 3')
c.send('message 4')
c.send('message 5')
c.send('message 6')

Consistently, only the first, third, and fifth message (message 1, message 3 and message 5) will get through and be printed in the server output. The second, fourth, and sixth messages do not show up on the server output, and instead the client gets an exception:

始终如一,只有第一条,第三条和第五条消息(消息1,消息3和消息5)将通过并打印在服务器输出中。第二,第四和第六条消息未显示在服务器输出上,而是客户端获得异​​常:

>>> c.send('message 2')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
socket.error: [Errno 111] Connection refused

Running this in Wireshark shows that it is getting an ICMP reply for "Destination unreachable". I have been able to reproduce this on 3 distinct machines (all running Linux though). Am I missing something? Is this expected behaviour for UDP to consistently drop packets, since protocols using it are supposed to be tolerant of packet loss? Even so, why would packets be dropped when sent on the local interface?

在Wireshark中运行它表明它正在获取“目标无法访问”的ICMP回复。我已经能够在3台不同的机器上重现这一点(所有机器都在运行Linux)。我错过了什么吗? UDP的这种预期行为是否始终丢弃数据包,因为使用它的协议应该容忍数据包丢失?即便如此,为什么在本地接口上发送数据包会被丢弃?

Binding the server to 127.0.0.1 instead of 0.0.0.0 has the same result.

将服务器绑定到127.0.0.1而不是0.0.0.0具有相同的结果。

1 个解决方案

#1


7  

Solved it in kind of a silly manner; please let me know if there is another way, and I will change the accepted answer.

以某种愚蠢的方式解决了它;如果有其他方法,请告诉我,我会更改已接受的答案。

The solution is simply to use two sockets bound on the same port; one raw, one not raw:

解决方案只是使用绑定在同一端口上的两个套接字;一个原始的,一个不是原始的:

import socket, select
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s1.bind(('0.0.0.0', 1337))
s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
s2.bind(('0.0.0.0', 1337))
while True:
    r, w, x = select.select([s1, s2], [], [])
    for i in r:
        print i, i.recvfrom(131072)

This makes the "Destination unreachable" ICMP packets go away and makes all packets go through fine. I think the operating system wants a non-raw socket listening on the port for things to go well, and then any raw sockets listening on that same port will receive copies of the packets.

这使得“目的地不可达”ICMP数据包消失,并使所有数据包都能正常运行。我认为操作系统希望非原始套接字监听端口以便事情顺利进行,然后在同一端口上侦听的任何原始套接字都将接收数据包的副本。

#1


7  

Solved it in kind of a silly manner; please let me know if there is another way, and I will change the accepted answer.

以某种愚蠢的方式解决了它;如果有其他方法,请告诉我,我会更改已接受的答案。

The solution is simply to use two sockets bound on the same port; one raw, one not raw:

解决方案只是使用绑定在同一端口上的两个套接字;一个原始的,一个不是原始的:

import socket, select
s1 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s1.bind(('0.0.0.0', 1337))
s2 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
s2.bind(('0.0.0.0', 1337))
while True:
    r, w, x = select.select([s1, s2], [], [])
    for i in r:
        print i, i.recvfrom(131072)

This makes the "Destination unreachable" ICMP packets go away and makes all packets go through fine. I think the operating system wants a non-raw socket listening on the port for things to go well, and then any raw sockets listening on that same port will receive copies of the packets.

这使得“目的地不可达”ICMP数据包消失,并使所有数据包都能正常运行。我认为操作系统希望非原始套接字监听端口以便事情顺利进行,然后在同一端口上侦听的任何原始套接字都将接收数据包的副本。