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]
如果服务器无法访问或中间的邪恶防火墙阻止 ICMP 数据包,这将阻塞,直到达到某个 TCP 超时。有没有更好的方法来获取这些信息?
I am using a Python script to send UDP packets to a server which will register my script to receive notifications from the server. The protocol requires that I send my own IP-Address and port on which I want to receive these notifications, so this should be an address that is reachable from the server's network.
Solutions should at least work on Windows XP and above and preferably Mac OS X, as this is my development platform.
The first step is to get the IP-Address of any of my interfaces. I am currently using the commonly suggested approach of resolving the machines own host name:
def get_local_address():
return socket.gethostbyname(socket.gethostname())
This only works sometimes. Currently it returns 172.16.249.1
, which is the address of a virtual interface used by VM Ware, so this is a problem.
Much better would be a way to get the IP-Address of the default interface, which should be the right one most of the time.
Even better would be a way to get the actual IP-Address used to connect to the server by a connection-oriented protocol like TCP. I can actually get that address, but not without attempting to open the connection:
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]
This will block until some TCP timeout has been reached in the case that the server is unreachable or an evil firewall in between is blocking ICMP packets. Is there a better way to get that information?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我曾经不得不解决同样的问题,并花了相当多的时间试图找到更好的方法,但没有成功。我也从 gethostname 开始,但像您一样发现它并不总是返回正确的结果,甚至在主机文件存在问题的情况下抛出异常。
我能找到的跨平台可靠工作的唯一解决方案是尝试连接,就像您所做的那样。对代码的一项改进是使用 UDP 而不是 TCP,即 SOCK_DGRAM 而不是 SOCK_STREAM。这可以避免连接超时,并且该函数会立即完成。
如果您将此代码部署到可能运行防火墙的客户端位置,请确保记录它执行此检查的事实。否则,他们可能会看到针对端口 50000 的出站连接的防火墙警告,但不了解其用途,并认为这是一个错误。
编辑:这里大致是我使用的代码:
我认为你得到 0.0.0.0 因为你在调用 getsockname 之前关闭了套接字。另一个技巧是我对端口 9 的使用;它是 RFC863 UDP 丢弃端口,因此比一些随机的高编号端口稍微不那么奇怪。
I had to solve the same problem once, and spent considerable time trying to find a better way, without success. I also started with gethostname, but discovered, as you did, that it doesn't always return the right result, and can even throw an exception in cases where there is a problem with the hosts file.
The only solution I could find that works reliably across platforms is to try the connection, as you're doing. One improvement on your code is to use UDP instead of TCP, i.e. SOCK_DGRAM instead of SOCK_STREAM. That avoids the connect timeout, and the function just completes immediately.
If you're deploying this code to client locations that might be running firewalls, make sure you document the fact that it does this check. Otherwise they may see a firewall warning for an outbound connection to port 50000, not understand its purpose, and consider it a bug.
Edit: here's roughly the code I used:
I think you're getting 0.0.0.0 because you're closing the socket before calling getsockname. Another tip is my use of port 9; it's the RFC863 UDP discard port, and therefore slightly less weird than some random high-numbered port.
发送方需要知道自己的IP地址吗?
客户端不能在发送 UDP“注册”后监听其尝试订阅的数据(在所有接口上),并让服务器开始将数据发送到数据报的来源地址吗?
Does the sending side need to know its IP address?
Can't the client just listen for for the data its trying to subscribe to (on all interfaces) after sending the UDP 'register' and have the server just start sending the data to the address from which the datagram originated?