Python:获取用于发送IP数据到特定远程IP的本地IP地址
我正在用一个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]
这会一直阻塞,直到达到某个TCP超时,如果服务器无法访问或者中间有个坏掉的防火墙在阻止ICMP数据包。那么有没有更好的方法来获取这些信息呢?
2 个回答
发送方需要知道自己的IP地址吗?
客户端难道不能在发送UDP的“注册”信息后,监听自己想要接收的数据(在所有网络接口上),然后让服务器直接把数据发送到这个数据包发出的地址吗?
我之前也遇到过同样的问题,花了不少时间想找个更好的解决办法,但没成功。我一开始也是用的gethostname,但像你发现的那样,它并不总是能返回正确的结果,有时候还会因为hosts文件出问题而抛出异常。
我找到的唯一一个在不同平台上都能可靠工作的解决方案就是尝试连接,就像你现在做的那样。你代码中的一个改进是使用UDP而不是TCP,也就是用SOCK_DGRAM代替SOCK_STREAM。这样可以避免连接超时,函数会立即完成。
如果你把这段代码部署到可能有防火墙的客户端,记得要说明这个检查的存在。否则,他们可能会看到一个关于连接到50000端口的防火墙警告,不明白这是干嘛的,可能会认为这是个bug。
补充一下:这是我用的代码大致样子:
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之前就关闭了socket。还有一个小建议是我用的9号端口;这是RFC863的UDP丢弃端口,所以比一些随机的高端口要稍微正常一点。