Search code examples
javatimeoutserversocket

java closing a serversocket and opening up the port


I'm creating a Java based server.

I'm using a server socket to accept incoming messages.

However at some point within my program I want the server socket to listen to another port.

I close the server socket. And start a new one with my new port. Everything is fine.

However when I change the server socket to the previous port again, it gives me an error.

I've read things that the server socket stays a while in a timed-out state after I closed it.

So here is my question: Can I circumvent this timed-out state of the server socket and make my port available again after I closed it and want to listen to the same port again?


EDIT: my function to make and listen to a server socket & my function to invalidate a server socket and create a new one right after

public void makeServerSocketWithPort(int portnr) throws IOException, Exception
{
    server = new ServerSocket(portnr);
    server.setReuseAddress(true);
    while(!portchanged)
    {
        Socket sock = server.accept();
        System.out.println(server.getLocalPort());
        System.out.println(sock.getLocalPort());
        handler = new Requesthandler(sock); //should be in a thread
        System.out.println(server.getLocalPort());
        System.out.println(sock.getLocalPort());
    }
    
}

public void invalidateRequestHandler(int newPort)
{   
    if(server != null)
    {
       portchanged = true; 
        try {
            server.close();
        } catch (IOException ex) {
            Logger.getLogger(Controlserver.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    portchanged = false;
    makeServerSocketWithPort(newPort);
}

Error StackTrace:

Exception in thread "main" java.net.SocketException: Socket closed
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
at java.net.ServerSocket.implAccept(ServerSocket.java:462)
at java.net.ServerSocket.accept(ServerSocket.java:430)
at stuff.Controlserver.makeServerSocketWithPort(Controlserver.java:63)
at stuff.Main.main(Main.java:44)

EDIT: a second try to fix it to no avail:

public void makeServerSocketWithPort(int portnr, boolean invalidated) throws IOException, Exception
{
    if(!invalidated)
    {
        server = new ServerSocket();
        server.setReuseAddress(true);
        server.bind(new InetSocketAddress(portnr));

        portchanged = false;
    }
    else
    {
        //TODO: invalidate the old requestHandler
        if(server != null)
        { 
            try 
            {
                server.close();
                server = null;
            } 
            catch (IOException ex) 
            {
                Logger.getLogger(Controlserver.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        if(server.isClosed())
        {
            System.out.println("closed biatch!");
        }
        else
        {
            System.out.println("surprise moddafakkaaaaa!!!");
        }

        //---------------------------------------------
        //then make new requestHandler with new port
        portchanged = true;
    }
    
    while(!portchanged)
    {
        if(server != null && !server.isClosed() && !invalidated)
        {
            Socket sock = server.accept();
            System.out.println(server.getLocalPort());
            System.out.println(sock.getLocalPort());
            System.out.println("test");
            handler = new Requesthandler(sock); //should be in a thread
            handler.start();
            System.out.println("ja harm");
            System.out.println(server.getLocalPort());
            System.out.println(sock.getLocalPort());
        }
        else
        {
            portchanged = true;
        }
    }
    
    if(portchanged)
    {
        portchanged = false;
        makeServerSocketWithPort(portnr, false);
    }
}

Again this works fine normally. I can navigate through my html pages. When I change my port number via one of the webpages it is properly stored and changed in my storage xml files. But when I changed my socket and navigate immediately to a page through that socket, it says it is closed and is not working until i restart my application.

I'm still looking for a way to circumvent this restart.


Well I solved the mystery. Thing was I just needed to reconstruct my classes a bit to support the threading a bit better. Instead of closing the socket and then making a new thread I started a new thread and then closed the socket. After a bit of fiddling it appeared to work just fine.


Solution

  • This is the normal Server socket behavior by OS. The OS keeps the port open in WAIT_TIMEOUT state. To get around this, try using ServerSocket.setReuseAddress(boolean on). This will enable/disable the SO_REUSEADDR socket option. Check here for Documentation.

    Quoting the javadoc of method setReuseAddress

    When a TCP connection is closed the connection may remain in a timeout state for a period of time after the connection is closed (typically known as the TIME_WAIT state or 2MSL wait state). For applications using a well known socket address or port it may not be possible to bind a socket to the required SocketAddress if there is a connection in the timeout state involving the socket address or port.

    Enabling SO_REUSEADDR prior to binding the socket using bind(SocketAddress) allows the socket to be bound even though a previous connection is in a timeout state.