【问题标题】:several TCP-servers on the same port同一端口上的多个 TCP 服务器
【发布时间】:2012-11-26 14:55:59
【问题描述】:

对我来说这看起来很奇怪。我可以在同一个端口上运行多个 TCP 服务器。

我使用带有以下代码的 Apache MINA 库:

IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.bind(new InetSocketAddress(80));

端口 80 已被另一个程序使用。但我没有收到异常“地址已在使用中”。使用 netstat 我可以看到以下内容:

C:\>netstat -oan |find /i "LIST"
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       2220
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       904
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       840

有人可以解释一下这种行为吗?

操作系统:Windows 7。

谢谢。

【问题讨论】:

  • 什么是进程2220和904?在您绑定之前,端口 80 是否已被使用?还是您的绑定导致它在关闭之前被两个进程共享?你打电话给setReuseAddress吗?
  • @trashgod 该链接中没有任何内容可以解释这种行为。这不应该发生。
  • 2220 是 javaw(我的应用程序)。 904是skype(之前推出过)。我没有调用 setReuseAddress。
  • 我认为不止一个应用可以监听传入连接,但是当最终有传入连接时,只有一个可以连接。
  • 你找到了圣杯。我们最终可以运行 Web 服务器,而无需为每个证书或带有 SNI 的 TLS 1.2 提供单独的 IP……好吧,说实话,这个输出在我看来是非常错误的。不可能有两个进程在同一主机、端口和协议上侦听。

标签: java tcp apache-mina


【解决方案1】:

通常只有一个进程可以侦听 TCP 端口、Windows 或任何其他操作系统(至少是主要操作系统)。在 Windows 上,如果两个进程共享端口,您可能会收到错误代码 10048。如果进程绑定到不同的接口地址,这将不适用(即使一个绑定到INADDR_ANY,另一个绑定到特定地址,它们也不会冲突)。此外,如果 SO_REUSEADDR 已在第二个套接字上设置,则不适用。

由于这两个进程都绑定到INADDR_ANY,并且您声称您的进程没有设置SO_REUSEADDR,但是,这是一个难题。据我所知,有三种可能:

  1. 基础库中的某些内容默认设置为SO_REUSEADDR
  2. 第二个套接字实际上是稍后打开的,它是指定 SO_REUSEADDR 的那个。
  3. Windows 套接字层中存在允许此操作的错误。

我知道没有软件是完美的,但我真的很犹豫选择第三个选项,特别是如果你可以轻松地复制它。我建议在开始进程之前和之后仔细观察netstat 输出,并查看在此之前是否存在其他侦听器。另外,尝试识别其他进程并查看它是否相关(您可以为此启用任务管理器中的PID列)。

编辑

下面的评论者提醒我,我应该指出SO_REUSEADDR 的行为确实因平台而异。 Windows 允许使用强制绑定到与其他侦听套接字相同的端口的选项的新套接字,如果两个套接字都是 TCP,则具有未确定的行为,如 here 所讨论的。在实践中,第二个套接字可能会“窃取”地址,但官方的说法似乎是行为未定义:

一旦第二个套接字成功绑定,绑定到该端口的所有套接字的行为都是不确定的。例如,如果同一端口上的所有套接字都提供 TCP 服务,则无法保证通过该端口传入的任何 TCP 连接请求都由正确的套接字处理——这种行为是不确定的。

如果旧的 TCP 套接字仍在侦听,Linux(和其他 Unix 变体)将不允许两个 TCP 套接字共享同一个端口。在这种情况下,SO_REUSEADDR 只允许新套接字在旧套接字处于 TIME_WAIT 时绑定(也许 FIN_WAIT 和 CLOSE_WAIT 状态,我必须检查一下)。

顺便说一句,当我第一次在 Windows 中遇到它时,我发现行为上的差异非常令人惊讶,但我自己测试过,当然如果你在两个套接字上都设置了 SO_REUSEADDR,它很有可能成功地绑定到同时使用相同的地址和端口。但是,我还没有对这种情况下的确切行为进行广泛的测试,因为在我的情况下它并不重要。

我不打算进入哪个平台是“正确的”,但 Windows 行为肯定会导致安全问题,这就是为什么他们想出SO_EXCLUSIVEADDRUSE 选项来防止其他套接字强制绑定。我似乎也有人认为,Windows 版本应该被视为一个完全不同的选项,具有不同的行为,只是恰好具有相同的名称。

【讨论】:

  • 4. netstat 中有一个错误显示此错误。您不能将 TCP 套接字绑定到同一个 IP:端口,无论是否使用 SO_REUSEADDR。这是 UDP 的。
  • 至少在 Windows 上,如果您使用 SO_REUSEADDR,即使第一个套接字处于活动状态,您也可以绑定到同一个端口,但没有定义哪个套接字接收连接。查看at this MSDN page 并向下滚动到“使用SO_EXCLUSIVEADDRUSE”部分,查看允许bind() 调用对的表格。文中提到它适用于 XP 和更早版本,但如果您进一步阅读,您会发现这仅仅是因为 Vista 并扩展了对双栈(IPv4 和 IPv6)应用程序的相同检查。
  • 我刚刚链接的页面的附加引用描述了 Windows 下 SO_REUSEADDR 的行为:一旦第二个套接字成功绑定,绑定到该端口的所有套接字的行为是不确定的.例如,如果同一端口上的所有套接字都提供 TCP 服务,则无法保证通过该端口传入的任何 TCP 连接请求都由正确的套接字处理——这种行为是不确定的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-10
  • 2011-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多