1

I am working on a Java client/server application that involves P2P communication over TCP. I'm trying to implement TCP Hole Punching as described here: http://www.brynosaurus.com/pub/net/p2pnat/#sec-tcp. This requires simultaneously listening on and attempting to establish an outgoing connection using the same local TCP port. Apparently, this should work if the SO_REUSEADDR socket option is used, which I am setting via the setReuseAddress() method in Java. However, this is not working as I expected. Here is some test code:

import java.io.IOException;
import java.net.*;

   public class Test {
    public static void main(String args[]) {        
        new Thread() {
                public void run() {
                    try {
                        ServerSocket ss = new ServerSocket();
                        ss.setReuseAddress(true);
                        ss.bind(new InetSocketAddress(7077));
                        ss.accept();
                    } catch (Exception e) {
                        System.out.println("ServerSocket exception: " + e.getMessage());
                    }
                }
            }.start();

        Socket s;

        while (true) {
            s = new Socket();
            try {
                s.setReuseAddress(true);
                s.bind(new InetSocketAddress(7077));
                s.connect(new InetSocketAddress("192.168.0.103", 7077));
                break;
            } catch (Exception e) {
                System.out.println("Socket exception: " + e.getMessage());
                try { s.close(); } catch (IOException e1) { }
                try { Thread.sleep(1000); } catch (InterruptedException e1) { }
            }

        }

    }

}

This works as expected in Windows 7: the ServerSocket listens on port 7077 in its own thread and the Socket repeatedly attempts to connect to 192.168.0.103:7077. However, under Linux (Ubuntu) only the first Socket connection attempt works, and subsequent attempts get the "Address already in use" BindException. Shouldn't I be able to establish an outgoing connection from a TCP source port that I'm also listening on simultaneously, and to reuse the local port number immediately after closing the socket, since I have the SO_REUSEADDR option enabled?

Jonathan
  • 183
  • 1
  • 4
  • 9
  • 1
    Not sure if there is an answer to your question in here: http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t but it's definitely worth reading. – zapl Sep 12 '13 at 19:29

2 Answers2

1

In Linux, both sockets need to set SO_REUSEADDR socket option. Thus, if we want two sockets, sock1 and sock2 to be bound ot the same port, then s2 would be able to reuse the port/address only if both sock1 and sock2 set SO_REUSEADDR.

Manoj Pandey
  • 4,528
  • 1
  • 17
  • 18
0

You are never closing your client socket, unless there is an exception, making the point of SO_REUSEADDR a no-op.

....
s = new Socket();
try {
    // ...
} catch (Exception e) {
    System.out.println("Socket exception: " + e.getMessage());
    // remove try block from here
    try { Thread.sleep(1000); } catch (InterruptedException e1) { }
} finally {
    try { s.close(); } catch (IOException e1) { }
}
....

In the above, I moved the closing of the socket to a newly created finally block so it is always executed, even if you break out the global while loop.

Since the socket is now closed under all conditions, the SO_REUSEADDR will use correctly now.

Ferrybig
  • 18,194
  • 6
  • 57
  • 79