UDP服务是否必须从连接的IP地址响应?

1 投票
2 回答
2892 浏览
提问于 2025-04-15 13:46

Pyzor 使用 UDP/IP 作为通信协议。我们最近把公共服务器换到了新机器上,结果收到很多超时的报告。我发现如果把查询的 IP 从 eth0:1 改成 eth0,就能解决这个问题。

我可以用一个简单的例子来重现这个问题:

这是服务器的代码:

#! /usr/bin/env python

import SocketServer

class RequestHandler(SocketServer.DatagramRequestHandler):
    def handle(self):
        print self.packet
        self.wfile.write("Pong")

s = SocketServer.UDPServer(("0.0.0.0", 24440), RequestHandler)
s.serve_forever()

这是客户端的代码(188.40.77.206eth0188.40.77.236 是同一台服务器,但它是 eth0:1):

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.sendto('ping', 0, ("188.40.77.206", 24440))
4
>>> s.recvfrom(1024)
('Pong', ('188.40.77.206', 24440))
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.sendto('ping', 0, ("188.40.77.236", 24440))
4
>>> s.recvfrom(1024)
[never gets anything]

服务器在这两种情况下都能收到“ping”数据包(因此也会发送“pong”数据包)。

奇怪的是,这在某些地方是可以正常工作的(也就是说,我能从两个 IP 收到响应)。例如,从 188.40.37.137(同一网络/数据中心,不同服务器)就能正常工作,但从 89.18.189.160(不同数据中心)也能正常工作。在这些情况下,recvfrom 的响应显示的是 eth0 的 IP,而不是连接的那个 IP。

这只是 UDP 的一个规则吗?这是 PythonUDPServer 类的问题/限制吗?还是说我做错了什么?有没有其他方法可以让它工作,而不是简单地连接到 eth0 的 IP(或者监听特定的 IP,而不是 0.0.0.0)?

2 个回答

1

这只是UDP的一个规则吗?

不是的。

这是Python的UDPServer类的问题/限制吗?

不太可能。

是我做错了什么吗?

你的程序看起来是对的。

有很多原因可能导致数据包没有到达服务器。UDP是无连接的,所以你的客户端只是把数据包发送出去,根本不知道有没有人收到。

看看你是否可以绑定到那个地址。有一个很不错的小程序叫做netcat,它在低级网络访问方面非常好用。虽然并不是每个系统都有,但下载和编译起来很简单。

nc -l -s 188.40.77.236 -p 24440 -u

如果你像之前一样运行你的客户端程序,你应该能在终端上看到“Ping”的输出。(你可以输入Pong,然后再发回给客户端。玩起来挺有趣的。)如果你收到了ping,说明网络问题不是原因,可能是Python服务器程序或库出了问题。如果没有收到ping,那就说明无法建立连接。“请联系你的网络管理员寻求帮助。”

需要检查的内容包括……

  1. 防火墙问题?
  2. 别名网络接口的配置问题。
  3. 用户权限问题。
3

我遇到了一些关于TFTP服务器的问题。我的服务器有两个IP地址都连接到同一个网络。因为UDP是无连接的,所以在这种情况下,IP地址可能会出现设置不正确的问题。我的情况是这样的:

  1. 客户端向服务器的某个特定IP地址发送了初始数据包。
  2. 服务器从收到的数据包中读取客户端的源地址,并发送一个响应。
    1. 但是,在这个响应中,服务器的“源地址”是根据路由表设置的,结果被设置成了另一个IP地址。
    2. 我们无法控制服务器的“源”IP地址,因为操作系统没有告诉我们请求是通过哪个IP地址进来的。
  3. 客户端收到了来自“另一个”IP地址的响应,并拒绝了它。

在我的情况下,解决办法是将TFTP服务器特定绑定到我想要监听的IP地址,而不是绑定到所有接口。

我找到了一些可能相关的内容,在Linux的tftpd手册页中。内容如下:

 Unfortunately, on multi-homed systems, it is impossible for tftpd to
 determine the address on which a packet was received. As a result, tftpd
 uses two different mechanisms to guess the best source address to use for
 replies. If the socket that inetd(8) passed to tftpd is bound to a par‐
 ticular address, tftpd uses that address for replies. Otherwise, tftpd
 uses ‘‘UDP connect’’ to let the kernel choose the reply address based on
 the destination of the replies and the routing tables. This means that
 most setups will work transparently, while in cases where the reply
 address must be fixed, the virtual hosting feature of inetd(8) can be
 used to ensure that replies go out from the correct address.  These con‐
 siderations are important, because most tftp clients will reject reply
 packets that appear to come from an unexpected address.

可以参考这个回答,它显示在Linux上确实可以读取到进入的UDP数据包的本地地址,并将其设置为发出的数据包。用C语言是可以做到的,不过我不确定Python是否也可以。

撰写回答