Correct way to close ServerSocket

4.3k views Asked by At

I'm studying to use ServerSocket and get an error when trying to close serverSocket object while method accept() is working. Next I found solution with setSoTimeout() method. But I think ignore exception isn't best practice. So, here's my two classes:

class Server:

public class Server {
    public static final int PORT = 8777;
    private ServerSocket serverSocket;
    private boolean serverRuns;

    Server() {
        try {
            serverSocket = new ServerSocket(PORT);
            serverRuns = true;

            (new Control(this)).start(); // Takes commands while working
            while (serverRuns) {
                try {
                    serverSocket.setSoTimeout(1000);
                    Socket clientSocket = serverSocket.accept();
                }
                catch (SocketTimeoutException e) {
                    // cap
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            try {
                serverSocket.close();
                System.out.println("Server stopped");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public void stopServer() {
        serverRuns = false;
    }
    public boolean isServerRuns() {
        return serverRuns;
    }
    public static void main(String[] Args) {
        Server s = new Server();
    }
}

class Control:

public class Control extends Thread {
    private Server activeServer;

    private Control() {}
    Control(Server activeServer) {
        this.activeServer = activeServer;
    }
    @Override
    public void run() {
        Scanner keyboard = new Scanner(System.in);

        while (activeServer.isServerRuns()) {
            String key = keyboard.nextLine().toLowerCase();

            switch (key) {
                case "close":
                    activeServer.stopServer();
                    break;
            }
        }
    }
}

Is it correct way to terminate ServerSocket work(ignore exception and check serverRuns variable once a second)? Any recommendations are welcome

2

There are 2 answers

1
Xephi On

You need to call the server close from an another Thread, cause serverSocket.accept(); will be blocking

Take a look at: Interrupt accept method and close the server

0
Armali On

Expanding on Xephi's answer:

You need to call the server close from an another Thread

Well, one doesn't need to call the ServerSocket.close() from the other thread, but it certainly is an advisable approach.

That is exactly what he is already doing. – user207421

No, it is not. What the OP is doing is to reset the serverRuns flag in the other thread and to periodically check this flag in the main Server thread with the aid of a socket timeout for the accept() call; I'd call that rather ugly.

With the recommended approach, the Server would be like:

    Server()
    {
        try
        {
            serverSocket = new ServerSocket(PORT);
            (new Control(this)).start(); // Takes commands while working
            while (true)
            {
                Socket clientSocket = serverSocket.accept();
                …
            }
        } catch (SocketException e) { System.out.println("Server stopped"); }
          catch (IOException e)     { e.printStackTrace(); }
    }

    public void stopServer() throws IOException
    {
        serverSocket.close();
    }

and the Control loop can be simplified like:

        while (true)
        {
            String key = keyboard.nextLine().toLowerCase();
            switch (key)
            {
                case "close":
                    activeServer.stopServer();
                    return;
            }
        }