Python:获取用于向特定远程IP地址发送IP数据的本地IP地址

11

我正在使用Python脚本向服务器发送UDP数据包,以便让服务器注册我的脚本,使其能够接收来自服务器的通知。协议要求我发送自己的IP地址和端口,这应该是服务器网络中可达的地址。

解决方案至少应该在Windows XP及以上版本上运行,并最好支持Mac OS X,因为这是我的开发平台。

第一步是获取任何一个接口的IP地址。我目前正在使用常见的方法,即通过解析机器自身的主机名来实现:

def get_local_address():
    return socket.gethostbyname(socket.gethostname())
这只在某些情况下有效。目前它返回的是172.16.249.1,这是VM Ware使用的虚拟接口的地址,所以这是一个问题。
更好的方法是获取默认接口的IP地址,这应该大多数情况下是正确的。
更好的方法是通过面向连接的协议(如TCP)获取用于连接到服务器的实际IP地址。我可以实际获取该地址,但需要尝试打开连接才行。
def get_address_to_connect_to(server_addr):
    non_open_port = 50000
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        s.connect((server_addr, non_open_port))
    except socket.error:
        pass
    else:
        s.close() # just in case

    return s.getsockname()[0]

如果服务器不可达或者中间有恶意防火墙阻止了ICMP数据包,这会一直阻塞直到某个TCP超时时间被触发。是否有更好的方法获取这些信息?

2个回答

12

我曾经遇到过同样的问题,花了相当长时间试图找到更好的方法,但没有成功。 我也开始使用gethostname,但像你一样发现它并不总是返回正确的结果,甚至在主机文件存在问题的情况下可能会抛出异常。

我能找到的唯一可靠跨平台解决方案就是像你现在做的那样尝试连接。 优化你代码的一个方法是使用UDP而不是TCP,即SOCK_DGRAM而不是SOCK_STREAM。 这样可以避免连接超时,函数将立即完成。

如果您要将此代码部署到可能运行防火墙的客户端位置,请确保记录该检查的事实。否则,他们可能会看到针对端口50000的出站连接的防火墙警告,不了解其目的,并认为这是一个错误。

编辑:以下是我使用的大致代码:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

try:
    s.connect((host, 9))
    client = s.getsockname()[0]
except socket.error:
    client = "Unknown IP"
finally:
    del s
return client

我认为你之所以会得到0.0.0.0,是因为在调用getsockname之前关闭了套接字。另一个提示是我使用的端口号是9;它是RFC863 UDP丢弃端口,因此比一些随机高端口略微不同。


啊,真是太好了!如果使用SOCK_DGRAM,那就几乎完美了!但在我的情况下,s.getsockname()返回的是('0.0.0.0', 51770)。我已经尝试过s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.sendto(b'hello', ('1.2.3.4', 50000)); s.getsockname()[0]。如果您能将您的代码添加到您的答案中,那就太好了! - Feuermurmel
那个完美运行了;它甚至使用了我不知道的Discard Protocol!我用with语句替换了del s - Feuermurmel

0

发送方需要知道自己的IP地址吗?

客户端在发送UDP“注册”后,是否可以只监听其尝试订阅的数据(在所有接口上),并让服务器将数据发送到数据报文来源的地址?


异端邪说!那项服务可以做很多事情来让我的生活更轻松。但是在项目中也包含了 SIP 实现,同样出现了相同的问题。它必须在 REGISTER 请求的 Contact 标头中发送自己的 IP 地址。 - Feuermurmel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接