6

这对我来说看起来很奇怪。我可以在同一个端口上运行多个 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。

谢谢。

4

1 回答 1

3

通常只有一个进程可以侦听 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,则具有未确定的行为,如此所述。在实践中,第二个套接字可能会“窃取”地址,但官方的说法似乎是行为未定义:

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

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

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

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

于 2013-02-02T01:23:52.257 回答