【问题标题】:why can't bind to 0.0.0.0:80 and 192.168.1.1:80 simultaneously?为什么不能同时绑定到 0.0.0.0:80 和 192.168.1.1:80?
【发布时间】:2013-06-22 20:48:01
【问题描述】:

我的python测试代码:

import socket

s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s1.bind(('192.168.1.1', 80)) 
s1.listen(5)

s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s2.bind(('0.0.0.0', 80)) 
s2.listen(5)

我收到了这个错误:

fpemud-workstation test # ./test.py
Traceback (most recent call last):
  File "./test.py", line 11, in <module>
    s2.bind(('0.0.0.0', 80)) 
  File "/usr/lib64/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 98] Address already in use

192.168.1.1是我的eth0接口的ip地址。
我认为 0.0.0.0:80 和 192.168.1.1:80 应该能够共存。
具有 dst-addr 192.168.1.1 的数据包进入套接字 s1,具有其他 dst-addr 的数据包进入套接字 s2。

【问题讨论】:

  • 因为在你运行这个的网络中,这台电脑的ip是192.168.1.1,和0.0.0.0和127.0.0.1一样,其实ip地址没关系,端口号有问题
  • 尽管已经发布了答案,尝试绑定通配符套接字首先,然后绑定另一个

标签: python linux sockets


【解决方案1】:

您不能同时绑定到 0.0.0.0:80 和端口 80 上的任何其他 IP,因为 0.0.0.0 涵盖了机器上存在的每个 IP,包括您的 192.168.1.1 地址。这并不表示“任何其他目标地址”,而是表示“此框上的所有接口”。

【讨论】:

  • 你的意思是它是一个规范。为什么操作系统(linux)使用这个规范? server-socket 0.0.0.0:80 和 192.168.0.1:80 共存有什么问题吗?
  • 我要运行 2 个 samba 服务器,服务器 A 用于接口 br0,服务器 B 用于所有其他接口(包括动态创建的接口)。由于这个规范的限制,现在我做不到。
  • @fpemud 如果0.0.0.0 的意思是“在此端口上未绑定的所有地址, that would be nondeterministic. If I say 0.0.0.0, I expect *every* address this computer responds to to reach me at that port. What happens in your example if the 192.168.0.1` 进程出现故障?0.0.0.0 进程是否应该接管该绑定?我不希望在代码中出现这种情况,C 代码不应该自己做那样的事情。但如果没有,绑定到0.0.0.0 是无用的,因为它不是每个端口或每个可用端口,它是在该系统调用时碰巧打开的每个端口。
  • @fpemud 恐怕没有办法解决它 - 这就是网络的工作方式。任何行为不同的 TCP 堆栈都从根本上被破坏,因此您不会找到任何实现您想要的功能。对不起!
  • @Ben 很可能让它工作,我已经在多个操作系统上这样做了。有关如何在 Linux(3.9 或更高版本)上执行此操作的说明,请参阅 my answer
【解决方案2】:

因为这在术语上是矛盾的。 0.0.0.0 表示“接受来自任何本地 IP 地址的连接”。 192.168.1.1 表示“仅接受寻址到 192.168.1.1 的连接”。如果有人连接到 192.168.1.1,您到底希望发生什么?

【讨论】:

    【解决方案3】:

    尽管其他答案已经说过,这应该是可能的 - 只是 bind 的工作方式取决于实现。

    例如,在 Windows 上,您的代码可能会按原样正常工作。在某些 *nix 操作系统上,我相信您可以通过设置 SO_REUSEADDR 套接字选项来使其工作。在 Linux 上,我已经能够使用 SO_REUSEPORT 套接字选项让它工作,但仅限于内核版本 3.9 或更高版本。

    不幸的是,当前版本的python不直接支持SO_REUSEPORT属性,所以我们必须手动定义它。

    基本上你的代码应该是这样的:

    # This adds support for the SO_REUSEPORT constant if not already defined.
    if not hasattr(socket, 'SO_REUSEPORT'):
      socket.SO_REUSEPORT = 15
    
    s1 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s1.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    s1.bind(('192.168.1.1', 80)) 
    s1.listen(5)
    
    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s2.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    s2.bind(('0.0.0.0', 80)) 
    s2.listen(5)
    

    【讨论】:

    • SO_REUSEADDR 仅用于 TIME_WAIT。顺序切换无效。我也不认为 Windows 有不同的行为。
    • 查看this StackOverflow answer 以获取有关SO_REUSEADDR 不同实现的更多信息。我在 Windows(至少是 Windows Vista)上测试了您的原始代码,所以我知道这是可行的。我刚刚有机会在 Linux 上进行测试,但我无法让它工作,但这可能与版本有关。我现在没有时间再研究它,但如果我发现其他任何东西,我会在以后更新。
    • @fpemud 我有机会进行更多研究,并且能够使用 SO_REUSEPORT 使其工作,但仅限于内核版本 3.9 或更高版本。更新了我的答案中的代码。
    猜你喜欢
    • 2018-08-10
    • 2017-08-05
    • 2016-11-11
    • 1970-01-01
    • 2017-01-27
    • 2012-04-26
    • 1970-01-01
    • 2014-01-02
    相关资源
    最近更新 更多