简体   繁体   English

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

[英]Send UDP to local server, but not via loopback interface

I'm ultimately trying to test a UDP client, and want to make sure that it works when sending data not through the loopback interface, to avoid any subtle issues this introduces, such as differences in checksum validation ( Bad UDP checksum has no effect: why? ). 我最终试图测试一个UDP客户端,并希望确保它在通过环回接口发送数据时有效,以避免引入任何细微问题,例如校验和验证的差异( 坏UDP校验和没有效果:为什么? )。

However, even when sending data to the result of socket.gethostbyname(socket.gethostname()) , which is not 127.0.0.1 , then according to Wireshark, the data seems to go via the loopback interface. 但是,即使将数据发送到socket.gethostbyname(socket.gethostname())的结果socket.gethostbyname(socket.gethostname())不是127.0.0.1 ,然后根据Wireshark,数据似乎通过环回接口。

The below program sends and receives b'somedata' successfully, and has the below capture from 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()

在此输入图像描述

How can I send data from a client running locally, to a server running locally, but avoiding the loopback interface and actually sending data out into the network? 如何将数据从本地运行的客户端发送到本地运行的服务器,但是避免环回接口并实际将数据发送到网络中?

Ideally answers would be applicable to both Linux and macOS. 理想情况下,答案适用于Linux和macOS。

To 'convince' the networking stack to physically transmit the frame using the Ethernet (or WiFi) card rather than the loopback, use a broadcast address. 为了“说服”网络堆栈使用以太网(或WiFi)卡而不是环回来物理传输帧,请使用广播地址。

I have successfully sent and received an UDP packet this way on my Linux system. 我在Linux系统上以这种方式成功发送和接收了UDP数据包。 I verified it with tcpdump . 我用tcpdump验证了它。 It shows one packet on Ethernet interface and no activity on loopback. 它在以太网接口上显示一个数据包,在环回时没有活动。

I have used literal broadcast address. 我使用了文字广播地址。 The socket module documentation mentions also the string '<broadcast>' as a special case address. 套接字模块文档还提到字符串'<broadcast>'作为特殊案例地址。 I did not tried it. 我没试过。

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)

Notes: 笔记:

  1. other hosts on the same network will receive the UDP packet as well. 同一网络上的其他主机也将接收UDP数据包。
  2. make sure the firewall/packet filter (eg Linux iptables/nftables) will not block the packet. 确保防火墙/数据包过滤器(例如Linux iptables / nftables)不会阻止数据包。
  3. regarding the setsockopt : Python socket.error: [Errno 13] Permission denied 关于setsockoptPython socket.error:[Errno 13]权限被拒绝

That's probably because your hostname is pointing to the loopback address hence socket.gethostbyname(socket.gethostname()) will yield 127.0.0.1 这可能是因为你的主机名指向环回地址,因此socket.gethostbyname(socket.gethostname())将产生127.0.0.1

What you need to do is cancel that pointing from the hostname to the loopback address: 您需要做的是取消从主机名指向环回地址:

  • in Linux edit the /etc/hosts and comment out the line 127.0.0.1 YOUR_HOSTNAME 在Linux中编辑/etc/hosts并注释掉127.0.0.1 YOUR_HOSTNAME
  • in Windows you should have c:\\windows\\system32\\drivers\\etc\\hosts which looks similar to the Linux one 在Windows中你应该有c:\\windows\\system32\\drivers\\etc\\hosts ,它看起来与Linux类似

After this if you call the socket.gethostbyname(socket.gethostname()) it will yield your DHCP assigned IP. 在此之后如果调用socket.gethostbyname(socket.gethostname()) ,它将产生DHCP分配的IP。

Though even in this case, calling from yourip to yourip might result in the network driver to route the package through the loopback interface. 虽然在这种情况下,从yourip调用yourip可能会导致网络驱动程序通过环回接口路由包。 Alternative would be to use the public IP, outside your network router. 替代方案是在网络路由器之外使用公共IP。 You could use an external service as described in this answer 您可以使用本答案中描述的外部服务

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM