将 UDP 发送到本地服务器,但不通过环回接口

Send UDP to local server, but not via loopback interface

我最终要测试一个 UDP 客户端,并希望确保它在通过环回接口 而不是 发送数据时正常工作,以避免由此引入的任何细微问题,例如校验和验证的差异 (Bad UDP checksum has no effect: why?)。

然而,即使将数据发送到 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()

如何从本地客户端 运行 向本地服务器 运行 发送数据,同时避免环回接口并将数据实际发送到网络中?

理想情况下答案适用于 Linux 和 macOS。

这可能是因为您的主机名指向环回地址,因此 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 也可能导致网络驱动程序通过环回接口路由包。替代方法是在网络路由器外部使用 public IP。您可以使用 this answer

中描述的外部服务

要'convince'网络堆栈使用以太网(或 WiFi)卡而不是环回物理传输帧,请使用广播地址。

我已经在我的 Linux 系统上以这种方式成功地发送和接收了一个 UDP 数据包。我用 tcpdump 验证了它。它在以太网接口上显示了一个数据包,在环回上没有 activity。

我用的是文字广播地址。 socket module 文档还提到字符串 '<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. 确保 firewall/packet 过滤器(例如 Linux iptables/nftables)不会阻止数据包。
  3. 关于 setsockoptPython socket.error: [Errno 13] Permission denied