【问题标题】:Prevent an app from binding to a socket that is already bound防止应用绑定到已绑定的套接字
【发布时间】:2021-09-11 07:39:05
【问题描述】:

如何防止 Java 应用绑定到另一个进程已在 Windows 上绑定到的套接字?

我有一个 Java 应用程序正在侦听端口 80 的问题。该应用程序启动正常并且没有报告异常。我不知道为什么我无法连接端口 80。其他端口工作正常。我检查了 netstat 是否有其他在 80 上监听的进程,并找到了 Skype。我不认为这是可能的,但经过一些研究,我猜 Skype 正在使用 SO_REUSEADDR 选项进行监听。在这种状态下,接受申请是不确定的。我希望我的 Java 应用程序在此实例中失败并出现绑定异常(或其他)。

如果我可以通过 Java 访问该选项,我似乎可以使用 SO_EXCLUSIVEADDRUSE,但我认为这是不可能的。 SO_REUSEADDR 周围有很多问题和答案,但我找不到回答我的问题的答案。这不仅仅是关于 Skype(我可以关闭监听部分),我希望我的程序在这种情况下更加健壮。

这是来自 Windows 7 机器上的 netstat -abn 的 sn-p:

    Proto  Local Address          Foreign Address        State
    TCP    0.0.0.0:80             0.0.0.0:0              LISTENING [java.exe]
    TCP    0.0.0.0:80             0.0.0.0:0              LISTENING [Skype.exe]

这就是我假设 Skype 使用 SO_REUSEADDR 的原因

这些进程似乎没有在不同的接口上侦听。

这是代码的 sn-p。端口是80:

    myServerSocket = new ServerSocket(myTcpPort);
    while (true) {
        new HTTPSession( myServerSocket.accept(), threadPool );
    }

作为进一步的信息,我创建了一个示例程序,以尽量减少任何副作用或错误处理的消息。

    import java.net.ServerSocket;

    public class PortTest
    {
        public static void main(String[] args)
        {
            System.out.println( "Hit Ctrl-C to stop.\n" );

            try {
                ServerSocket myServerSocket = new ServerSocket(80);
                System.out.println( "Before the accept() call.");
                myServerSocket.accept();
                System.out.println( "After the accept() call." );
            }
            catch (Exception ex ) {
                System.out.println("Error listening.");
                ex.printStackTrace();
            }
         }

    }

在运行此示例程序 (PortTest) 以及 Skype 正在运行并侦听端口 80 时,我仍然没有收到异常。为了进一步测试,我执行了第二个实例 PortTest,我确实看到了正在使用的端口消息.

    Error listening.
    java.net.BindException: Address already in use: JVM_Bind
            at java.net.DualStackPlainSocketImpl.bind0(Native Method)
            at java.net.DualStackPlainSocketImpl.socketBind(Unknown Source)
            at java.net.AbstractPlainSocketImpl.bind(Unknown Source)
            at java.net.PlainSocketImpl.bind(Unknown Source)
            at java.net.ServerSocket.bind(Unknown Source)
            at java.net.ServerSocket.<init>(Unknown Source)
            at java.net.ServerSocket.<init>(Unknown Source)
            at PortTest.main(PortTest.java:10)

【问题讨论】:

  • 你的ServerSocket绑定到0.0.0.0地址了吗?也许您的 Java 程序正在一个 IP 上侦听端口 80,而在另一个 IP 上侦听 Skype,这解释了为什么 Java 不抱怨 PortAlreadyInUse 异常
  • gma 说得对,只需在 0.0.0.0 上绑定,您应该获得“完整”绑定或异常。
  • 能贴一下ServerSocket初始化代码吗?
  • 添加了一些代码和 netstat 结果
  • 如果该端口已在使用中,则创建新的 ServerSocket 应引发 IOException。你确定你没有将异常压缩到堆栈的某个地方吗?

标签: java sockets


【解决方案1】:
socket = new ServerSocket(0);

会自动为你选择一个空闲端口。

此外,此代码将告诉您端口是否可用:

boolean portAvaliable = true;
ServerSocket s = null;
try {
   s = new ServerSocket(yourPort);
}
catch (IOException e) {
   portAvaliable = false;
} 
finally {
   if (s != null)
      try {
        s.close();
      } 
      catch (IOException e) { 
         //handle the exception
      }
}

检查来自portAvaliable 的布尔值以识别端口状态。

【讨论】:

  • 我需要监听一个特定的端口,而不是一个随机空闲的端口。此外,我现在正在我的代码中有效地执行此操作。但是,没有抛出异常,因此是我的问题。
【解决方案2】:

对于任何想知道的人来说,7u25 之前的 Java 都有这个问题。

这是release note of Java 7u25的摘录:

Windows 平台上网络 API 实施的变化

网络 API 的实现在 Windows 上已更改为 默认使用SO_EXCLUSIVEADDRUSE 套接字选项。这种变化是 有必要解决同时使用 IPv4 和 需要绑定到同一端口的 IPv6 应用程序。

这个问题是 Windows 独有的,因为[1]SO_REUSEADDR[2] 在那里工作的方式很奇怪。

【讨论】:

    猜你喜欢
    • 2015-10-24
    • 2015-10-28
    • 1970-01-01
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多