Прослушивание сокетов Python для пакетов UDP; только половина полученных пакетов - PullRequest
7 голосов
/ 02 апреля 2012

Я пытаюсь создать сокет raw в 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)

Это необходимо запустить как root и создать необработанный сокет на порту1337, который прослушивает пакеты UDP и печатает их всякий раз, когда они принимаются;проблем нет.

Теперь давайте создадим небольшого клиента, чтобы проверить, работает ли он:

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')

Последовательно, только первое, третье и пятое сообщение (message 1, message 3и message 5) пройдёт и будет напечатан на сервере.Второе, четвертое и шестое сообщения не отображаются на выходе сервера, и вместо этого клиент получает исключение:

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

Выполнение этого в Wireshark показывает, что он получает ICMP-ответ для «Назначение недоступно»».Я смог воспроизвести это на 3 разных машинах (хотя все работают под Linux).Я что-то пропустил?Это ожидаемое поведение для UDP, чтобы последовательно отбрасывать пакеты, так как протоколы, использующие его, должны быть терпимы к потере пакетов?Тем не менее, почему пакеты будут отбрасываться при отправке на локальный интерфейс?

Привязка сервера к 127.0.0.1 вместо 0.0.0.0 дает тот же результат.

1 Ответ

9 голосов
/ 02 апреля 2012

Решил это глупо; пожалуйста, дайте мне знать, если есть другой способ, и я изменю принятый ответ.

Решение состоит в том, чтобы просто использовать два сокета, привязанных к одному и тому же порту; один сырой, один не сырой:

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)

Это приводит к тому, что ICMP-пакеты "пункт назначения недоступен", и все пакеты проходят нормально. Я думаю, что операционная система хочет, чтобы не-необработанный сокет прослушивал порт, чтобы все шло хорошо, и тогда любые необработанные сокеты, прослушивающие этот же порт, получат копии пакетов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...