6

How to prevent a Java app from binding to a socket that another process is already bound to on Windows?

I have an issue where I have a Java application that is listening on port 80. The application starts fine and reports no exceptions. I couldn't figure out why I couldn't connect on port 80. Other ports worked fine. I checked netstat for other processes listening on 80 and found Skype. I didn't think that was possible, but after some research, I'm guessing Skype is listening with the SO_REUSEADDR option. In this state the accepting application is indeterminate. I would like my Java application to fail in this instance with a bind exception (or other).

It looks like I could use SO_EXCLUSIVEADDRUSE if I had access to that option via Java but I don't think that is possible. There are lots of questions and answers around SO_REUSEADDR but none that I could find that answered my question. This isn't just about Skype (I can turn off the listening part), I want my program to be more robust in this situation.

This is snippet from netstat -abn on a Windows 7 box:

    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]

This is why I'm assuming that Skype is using SO_REUSEADDR

The processes don't appear to be listening on different interfaces.

Here is a snippet of the code. The port is 80:

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

As further information I created a sample program to minimize any side effects or mishandled messages.

    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();
            }
         }

    }

I still don't get an exception when running this sample program (PortTest) and when Skype is running and listening on port 80. And to further test, I executed a second instance PortTest and I do see the port in use message.

    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)
Frank Black
  • 61
  • 1
  • 4
  • 2
    Did you bind your ServerSocket to 0.0.0.0 address? Maybe your Java program is listening on port 80 on one IP and Skype on another, explaining why Java does not complain with PortAlreadyInUse exception – gma May 16 '13 at 15:51
  • gma has it right, just bind on 0.0.0.0 and you should either get a "complete" bind or an exception. – Daniel Moses May 16 '13 at 15:52
  • Can you post the ServerSocket initialization code? – gma May 16 '13 at 16:00
  • Added some code and netstat results – Frank Black May 16 '13 at 18:09
  • Creating a new ServerSocket should throw an IOException if that port is already in use. Are you sure that you're not squashing the exception somewhere down the stack? – Lurk21 May 16 '13 at 19:02
  • I have a try/catch block around the code in question catching `Exceptions`. I put log messages before and after the `accept()` call. I get the before log message but not the after log message. Plus I can see that my Java process is indeed listening on port 80. See the netstat results. – Frank Black May 16 '13 at 19:11
  • So it sounds to me like an exception is being thrown and caught. What are you doing with it? printStackTrace() ? – Lurk21 May 16 '13 at 19:19
  • Yes, I am catching `Exception`s and doing a printStackTrace(). Again, I see that my Java process is listening on port 80 and I don't see any log messages after the accept() call. Not sure why you think an exception is being thrown and caught. – Frank Black May 16 '13 at 19:30
  • Because both the ServerSocket constructor and the accept() method throw IOExceptions if they cannot establish or listen. http://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html#ServerSocket%28int%29 ... Also, throwing an exception would halt code execution and prevent the logging after the accept() call. – Lurk21 May 16 '13 at 19:42
  • Say that were the case, I wouldn't see my Java process listening on port 80. But that's not the case. I'm not suppressing log messages. I'll edit the questions and add a stripped down example program intact. – Frank Black May 16 '13 at 19:47
  • Can you try to write to the socket, and if so does that throw an exception in the case that Skype is silently hogging the port? You could use this as a test to see if you were successful at binding to the port. – Lurk21 May 16 '13 at 20:50
  • I tried connecting to port 80 via my browser and it won't return anything. This is how I initially found this problem. Once I saw that two processes were listening on the same port I followed that path. My Java program PortTest is definitely binding on port 80. My logs show that and netstat shows that. – Frank Black May 17 '13 at 13:53
  • Workaround proposed at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6421091 – fglez May 22 '13 at 14:20
  • I did not think it was possible for more than one process to bind to a given port at a given IP address. Is the use of address 0.0.0.0 the problem? surely you always want a server bind to a particular address, not to a wildcard? – Raedwald Jun 12 '13 at 20:48
  • Perhaps relevant: http://stackoverflow.com/questions/11931175/what-does-wildcard-address-in-inetsocketaddress-mean – Raedwald Jun 12 '13 at 20:51

2 Answers2

2
socket = new ServerSocket(0);

Will automatically select you a free port.

Furthermore, this code will tell you wether a port is avaliable:

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
      }
}

Check the boolean value from portAvaliable in order to identify the port status.

aran
  • 10,978
  • 5
  • 39
  • 69
  • I need to listen on a specific port not a randomly free one. Also, I'm effectively doing this in my code now. However, no exception is thrown, hence my problem. – Frank Black May 16 '13 at 18:13
0

For anyone wondering, Java prior to 7u25 had this problem.

Here's an excerpt from the release note of Java 7u25:

Change in Networking API Implementation on Windows platforms

The implementation of the networking APIs has been changed on Windows to use the SO_EXCLUSIVEADDRUSE socket option by default. This change is necessary to address anomalies that arise when using both IPv4 and IPv6 applications that require to bind to the same port.

The problem is exclusive to Windows because of the funky way[1] SO_REUSEADDR[2] works there.

antak
  • 19,481
  • 9
  • 72
  • 80