为什么两个 Java 进程可以绑定到 macOS 中的同一个套接字?

Nat*_*ski 2 java sockets macos networking serversocket

我有一些生成套接字绑定的 Java 代码。很难提供一个最小的示例,因为这是 Web 框架的一部分,但它在某些时候有效地执行了此检查。

  private static boolean portInUse(int port) {
    // try to bind to this port, if it succeeds the port is not in use
    try (ServerSocket socket = new ServerSocket(port)) {
      socket.setReuseAddress(true);
      return false;
    } catch (IOException e) {
      return true;
    }
  }
Run Code Online (Sandbox Code Playgroud)

我可以看到,如果我使用同一端口运行两个不同的 Java 进程,它们都会落入第一个条件并返回false,因此两者都能够绑定到同一端口。我已经阅读了一些相关的套接字问题和解释,例如 this,但它们似乎听起来像是我指定的选项不可能实现这一点。看看setReuseAddress它的实现似乎只SO_REUSEADDR在套接字上设置。

我可以看到一个进程最终有一个套接字,就像ServerSocket[addr=0.0.0.0/0.0.0.0,localport=56674]在调试器中一样。如果我运行类似的命令,sudo lsof -n -i | grep -e LISTEN -e ESTABLISHED | grep 56674我可以看到两个进程绑定到同一端口:

java      68863    natdempk 1256u  IPv4 0xbbac93fff9a6e677      0t0  TCP *:56674 (LISTEN)
java      68998    natdempk  985u  IPv6 0xbbac93fff2f84daf      0t0  TCP *:56674 (LISTEN)
Run Code Online (Sandbox Code Playgroud)

我还可以看到 gRPC 和 Node 等其他一些项目提到了在问题跟踪器中通过其服务器观察到的这种行为,但他们从未解释为什么这是可能的。不同的进程如何绑定到 macOS 上的同一个套接字?

我正在运行 macOS 11.6.3 (20G415),如果这有帮助的话。如果有人有什么我应该在这里添加的内容,也很乐意提供更多调试信息。

Jör*_*tag 7

它们没有绑定到同一端口。一种是绑定到 IPv6 之上的 TCP,另一种是绑定到 IPv4 之上的 TCP。

稍微扩展一下 Java 细节:new ServerSocket(port)在 Java 中使用,InetAddress.anyLocalAddress()因为没有InetAddress传入。InetAddress.anyLocalAddress()可以返回 IPv4 或 IPv6 地址,这意味着尽管传递了相同的端口,但不能保证跨 JVM 绑定到相同的值在。