163

In my main thread I have a while(listening) loop which calls accept() on my ServerSocket object, then starts a new client thread and adds it to a Collection when a new client is accepted.

I also have an Admin thread which I want to use to issue commands, like 'exit', which will cause all the client threads to be shut down, shut itself down, and shut down the main thread, by turning listening to false.

However, the accept() call in the while(listening) loop blocks, and there doesn't seem to be any way to interrupt it, so the while condition cannot be checked again and the program cannot exit!

Is there a better way to do this? Or some way to interrupt the blocking method?

Neuron
  • 5,141
  • 5
  • 38
  • 59
lukeo05
  • 1,833
  • 2
  • 14
  • 13

9 Answers9

177

You can call close() from another thread, and the accept() call will throw a SocketException.

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Simon Groenewolt
  • 10,607
  • 1
  • 36
  • 64
  • 2
    Thanks, so obvious, didn't even occur to me! I was calling close() after I exited the loop. – lukeo05 Jun 06 '10 at 10:35
  • 4
    Strange, that there is no this info in docs: http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#accept%28%29 method is not marked as throwing SocketException. It is only mentioned here http://download.oracle.com/javase/1.4.2/docs/api/java/net/ServerSocket.html#close%28%29 – Vladislav Rastrusny Apr 07 '11 at 12:57
  • 1
    Is it okay to call `close()`, I mean it'll throw the exception on doing it, so is there any other way (which doesn't throw exception, also not based on timeout) to stop listening for requests? – Kushal Jun 07 '12 at 13:13
  • 3
    And what to do if the connection was already accepted and the thread is waiting for some data: while((s=in.readLine()) != null) ? – Alex Fedulov Aug 24 '12 at 10:56
  • Does it really help? when I use the close() method to break the accept under the linux env, it does not work? – Djvu Jan 10 '15 at 09:15
  • The links don't exist now. Can you please update them ? – gonephishing Nov 02 '15 at 22:20
  • 1
    @AlexFedulov Shutdown that socket for input. `readLine()` will then return null and the normal closure operations that the thread should already have in place will occur. – user207421 Jun 11 '17 at 22:51
  • ServerSocket.accept() throws an IOException which has to be dealt with, and that implicitly deals with the SocketException as well because SocketException inherits from IOException. Perhaps that is why it was not mentioned, because dealing with the IOException from accept() will also catch the SocketException from close(). – leisheng Feb 19 '21 at 02:56
33

Set timeout on accept(), then the call will timeout the blocking after specified time:

http://docs.oracle.com/javase/7/docs/api/java/net/SocketOptions.html#SO_TIMEOUT

Set a timeout on blocking Socket operations:

ServerSocket.accept();
SocketInputStream.read();
DatagramSocket.receive();

The option must be set prior to entering a blocking operation to take effect. If the timeout expires and the operation would continue to block, java.io.InterruptedIOException is raised. The Socket is not closed in this case.

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Juha Syrjälä
  • 33,425
  • 31
  • 131
  • 183
11

Is calling close() on the ServerSocket an option?

http://java.sun.com/j2se/6/docs/api/java/net/ServerSocket.html#close%28%29

Closes this socket. Any thread currently blocked in accept() will throw a SocketException.

Ronan Boiteau
  • 9,608
  • 6
  • 34
  • 56
Lauri Lehtinen
  • 10,647
  • 2
  • 31
  • 29
4

You can just create "void" socket for break serversocket.accept()

Server side

private static final byte END_WAITING = 66;
private static final byte CONNECT_REQUEST = 1;

while (true) {
      Socket clientSock = serverSocket.accept();
      int code = clientSock.getInputStream().read();
      if (code == END_WAITING
           /*&& clientSock.getInetAddress().getHostAddress().equals(myIp)*/) {
             // End waiting clients code detected
             break;
       } else if (code == CONNECT_REQUEST) { // other action
           // ...
       }
  }

Method for break server cycle

void acceptClients() {
     try {
          Socket s = new Socket(myIp, PORT);
          s.getOutputStream().write(END_WAITING);
          s.getOutputStream().flush();
          s.close();
     } catch (IOException e) {
     }
}
Nickolay Savchenko
  • 1,474
  • 16
  • 28
3

The reason ServerSocket.close() throws an exception is because you have an outputstream or an inputstream attached to that socket. You can avoid this exception safely by first closing the input and output streams. Then try closing the ServerSocket. Here is an example:

void closeServer() throws IOException {
  try {
    if (outputstream != null)
      outputstream.close();
    if (inputstream != null)
      inputstream.close();
  } catch (IOException e1) {
    e1.printStackTrace();
  }
  if (!serversock.isClosed())
    serversock.close();
  }
}

You can call this method to close any socket from anywhere without getting an exception.

DwB
  • 37,124
  • 11
  • 56
  • 82
  • 9
    Input and output streams are not attached to the `ServerSocket` but to a `Socket` and we're talking about closing the `ServerSocket` not the `Socket`, so the `ServerSocket` can be closed without closing a `Socket`'s streams. – icza Sep 03 '14 at 05:42
1

Use serverSocket.setSoTimeout(timeoutInMillis).

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
1

OK, I got this working in a way that addresses the OP's question more directly.

Keep reading past the short answer for a Thread example of how I use this.

Short answer:

ServerSocket myServer;
Socket clientSocket;

  try {    
      myServer = new ServerSocket(port)
      myServer.setSoTimeout(2000); 
      //YOU MUST DO THIS ANYTIME TO ASSIGN new ServerSocket() to myServer‼!
      clientSocket = myServer.accept();
      //In this case, after 2 seconds the below interruption will be thrown
  }

  catch (java.io.InterruptedIOException e) {
      /*  This is where you handle the timeout. THIS WILL NOT stop
      the running of your code unless you issue a break; so you
      can do whatever you need to do here to handle whatever you
      want to happen when the timeout occurs.
      */
}

Real world example:

In this example, I have a ServerSocket waiting for a connection inside a Thread. When I close the app, I want to shut down the thread (more specifically, the socket) in a clean manner before I let the app close, so I use the .setSoTimeout() on the ServerSocket then I use the interrupt that is thrown after the timeout to check and see if the parent is trying to shut down the thread. If so, then I set close the socket, then set a flag indicating that the thread is done, then I break out of the Threads loop which returns a null.

package MyServer;

import javafx.concurrent.Task;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

import javafx.concurrent.Task;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

public class Server {

public Server (int port) {this.port = port;}

private boolean      threadDone        = false;
private boolean      threadInterrupted = false;
private boolean      threadRunning     = false;
private ServerSocket myServer          = null;
private Socket       clientSocket      = null;
private Thread       serverThread      = null;;
private int          port;
private static final int SO_TIMEOUT    = 5000; //5 seconds

public void startServer() {
    if (!threadRunning) {
        serverThread = new Thread(thisServerTask);
        serverThread.setDaemon(true);
        serverThread.start();
    }
}

public void stopServer() {
    if (threadRunning) {
        threadInterrupted = true;
        while (!threadDone) {
            //We are just waiting for the timeout to exception happen
        }
        if (threadDone) {threadRunning = false;}
    }
}

public boolean isRunning() {return threadRunning;}


private Task<Void> thisServerTask = new Task <Void>() {
    @Override public Void call() throws InterruptedException {

        threadRunning = true;
        try {
            myServer = new ServerSocket(port);
            myServer.setSoTimeout(SO_TIMEOUT);
            clientSocket = new Socket();
        } catch (IOException e) {
            e.printStackTrace();
        }
        while(true) {
            try {
                clientSocket = myServer.accept();
            }
            catch (java.io.InterruptedIOException e) {
                if (threadInterrupted) {
                    try { clientSocket.close(); } //This is the clean exit I'm after.
                    catch (IOException e1) { e1.printStackTrace(); }
                    threadDone = true;
                    break;
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
};

}

Then, in my Controller class ... (I will only show relevant code, massage it into your own code as needed)

public class Controller {

    Server server = null;
    private static final int port = 10000;

    private void stopTheServer() {
        server.stopServer();
        while (server.isRunning() {
        //We just wait for the server service to stop.
        }
    }

    @FXML private void initialize() {
        Platform.runLater(()-> {
            server = new Server(port);
            server.startServer();
            Stage stage = (Stage) serverStatusLabel.getScene().getWindow();
            stage.setOnCloseRequest(event->stopTheServer());
        });
    }

}

I hope this helps someone down the road.

Michael Sims
  • 2,360
  • 1
  • 16
  • 29
0

Another thing you can try which is cleaner, is to check a flag in the accept loop, and then when your admin thread wants to kill the thread blocking on the accept, set the flag (make it thread safe) and then make a client socket connection to the listening socket. The accept will stop blocking and return the new socket. You can work out some simple protocol thing telling the listening thread to exit the thread cleanly. And then close the socket on the client side. No exceptions, much cleaner.

stu
  • 8,461
  • 18
  • 74
  • 112
  • this makes no sense ... when you have code like this socket = serverSocket.accept(); at that moment, blocking starts and the loop is not part of our code, so there is way to have that blocking code look for a flag that I set ... at least I have not been able to find a way to do this ... if you have working code, please share? – Michael Sims Mar 18 '18 at 22:34
  • Yeah, it would seem I left out one crucial bit of information which would make it not make much sense. You'd need to make the accept socket non blocking and only block for some period of time before the loop repeated, in which area you would then check for the exit flag. – stu Mar 19 '18 at 23:59
  • How, exactly, do you make the accept socket, non-blocking? – Michael Sims Mar 30 '18 at 00:12
  • long on = 1L; if (ioctl(socket, (int)FIONBIO, (char *)&on)) – stu Mar 30 '18 at 16:17
  • @stu This question is for Java's Sockets. – Kröw Jul 23 '19 at 03:10
-1

You can simply pass the timeout limit (milli seconds) as a parameter while calling accept function.

eg serverSocket.accept(1000); automatically close the request after 1 sec