Отправить UDP на локальный сервер, но не через петлевой интерфейс - PullRequest
3 голосов
/ 22 апреля 2019

В конечном итоге я пытаюсь протестировать UDP-клиент и хочу убедиться, что он работает при отправке данных , а не через интерфейс обратной связи, чтобы избежать каких-либо тонких проблем, таких как различия в контрольной сумме.проверка ( Плохая контрольная сумма UDP не имеет никакого значения: почему? ).

Однако даже при отправке данных результат socket.gethostbyname(socket.gethostname()), который не равен 127.0.0.1, тогда согласно Wiresharkкажется, что данные передаются через интерфейс обратной связи.

Приведенная ниже программа успешно отправляет и получает b'somedata' и имеет следующий захват из Wireshark.

import asyncio
import socket

async def server():
    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
        sock.setblocking(False)
        sock.bind(('', 4567))
        data = await loop.sock_recv(sock, 512)
        print('Received', data)

async def main():
    local_ip = socket.gethostbyname(socket.gethostname())
    print('Local IP', local_ip)  # Outputs 192.168.0.34

    asyncio.ensure_future(server())
    await asyncio.sleep(0)

    with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
        sock.setblocking(False)
        sock.connect((local_ip, 4567))
        await loop.sock_sendall(sock, b'somedata')
        await asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

enter image description here

Как я могу отправить данные с локально работающего клиента на сервер, работающий локально, но избегая петлевого интерфейса и фактически отправляя данные в сеть?

В идеале ответы будутбыть применимым как к Linux, так и к macOS.

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Чтобы «убедить» сетевой стек физически передавать кадр, используя карту Ethernet (или WiFi), а не обратную связь, используйте широковещательный адрес.

Я успешно отправил и получил пакет UDP таким образом в моей системе Linux. Я подтвердил это с tcpdump. Он показывает один пакет на интерфейсе Ethernet и не выполняет никаких действий по обратной связи.

Я использовал буквальный широковещательный адрес. В документации для сокетов в качестве адреса особого случая упоминается также строка '<broadcast>'. Я не пробовал.

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
    sock.setblocking(False)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    sock.connect(('192.168.0.255', 4567))
    await loop.sock_sendall(sock, b'somedata')
    await asyncio.sleep(1)

Примечания:

  1. другие хосты в той же сети также получат пакет UDP.
  2. убедитесь, что брандмауэр / фильтр пакетов (например, Linux iptables / nftables) не будет блокировать пакет.
  3. относительно setsockopt: Python socket.error: [Errno 13] В доступе отказано
0 голосов
/ 22 апреля 2019

Это, вероятно, потому что ваше имя хоста указывает на адрес обратной петли, следовательно, socket.gethostbyname(socket.gethostname()) даст 127.0.0.1

Что вам нужно сделать, это отменить указание от имени хоста на адрес обратной петли:

  • в Linux отредактируйте /etc/hosts и закомментируйте строку 127.0.0.1 YOUR_HOSTNAME
  • в Windows у вас должен быть c:\windows\system32\drivers\etc\hosts, который похож на Linux

После этого, если вы вызовете socket.gethostbyname(socket.gethostname()), вы получите назначенный DHCP IP-адрес.

Хотя даже в этом случае вызов от yourip до yourip может привести к тому, что сетевой драйвер направит пакет черезпетлевой интерфейс.Альтернативой может быть использование общедоступного IP-адреса вне вашего сетевого маршрутизатора.Вы можете использовать внешний сервис, как описано в этот ответ

...