Detecting Client Disconnections Using Java Sockets

java sockets detecting client side disconnections

Java Sockets make networked programming a breeze. The java.net package offers developers a wide range of tools to create networked applications. The Socket class provides access to a TCP connection between hosts. Unfortunately, it doesn’t provide an API for detecting a dropped connection from the remote system.

A Simple Client-Server Application Model

Java sockets can facilitate both TCP and UDP type protocols. While UDP connections are non-persistent, TCP connections often support a dynamic back-and-forth exchange of information between two network end systems. Let’s imagine this as a simple client:server model that does the following:

  1. The server opens a socket and begins listening for connections;
  2. A client opens a socket and connects to the server;
  3. The client sends a message to the server;
  4. The server receives the client’s message and sends a response;
  5. The client disconnects.

This exchange could represent the core interaction of a simple messaging application, a video game chat room, or perhaps a commenting system. The TCP protocol dictates that one member of a network communication session plays the role of the server (host) while multiple others can be a client. Typically, a server remains running such that many clients can connect, disconnect, reconnect, and perhaps even interact.

What happens when a client disconnects though?

Detecting Socket Connections

Java’s Socket API does not provide an API for detecting a clientside disconnection. Let’s consider how a simple socket-based server is created using Java’s ServerSocket and Socket classes.

class TestServer {
    
    public static void main(String[] args) throws IOException {
        
        // Create server socket on port 1234
        ServerSocket serverSocket = new ServerSocket(1234);
        
        // Listen for incoming connections
        while(!serverSocket.isClosed()){
            
            // Listen
            
        }
    }
}

In this example, a ServerSocket is created and begins listening for connections on port 12345. This means a remote host would attempt to connect to this server on that port and likely be refused if attempting to connect anywhere else. The key logic in this example is the while(!serverSocket.isClosed()) loop.

This is the equivalent of saying “listen for incoming connections as long as the server’s socket isn’t closed.” The loop isn’t doing anything, however, and the above code will likely run until there’s a keyboard interrupt or power outage—it needs logic to handle incoming connections.

Accepting Socket Connections

Once the ServerSocket has been established, effectively binding it to a port, the server can start listening for incoming client connections. Per the official JDK 11 docs:

[The ServerSocket] Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.

This is done by the following code:

Socket socket = serverSocket.accept();

Much like we ensured the Server would listen for incoming connections until its ServerSocket was closed, we also want to ensures that the server listens for incoming client data for as long as a client connection is established. This is accomplished by adding a while(!(socket.isClosed()) loop, within which we’ll analyze client messages and act accordingly:

// Open socket for incoming connections
Socket socket = serverSocket.accept();

// Continue to listen while client connected
while(!(socket.isClosed())){

    // Listen for and validate client incoming message
    String input = inputStream.readLine();
    if (input != null && !input.trim().equals("")){
         
        // Stream to console
        System.out.println("message received: " + input);

    }
}

Note: For a more complete tutorial, check out the Oracle KnockKnockServer series.

The previous code is essentially saying “listen to what this client has to say as long as the socket is connected.” To accomplish this, it uses the socket.getInputStream() method’s return value wrapped in the BufferedReader object to extract a String representation of client data.

The line if (input != null && !input.trim().equals("")) is a minimalist check to ensure the server is responding only to valid client input, at which point it streams the received message to the console.

One problem that becomes apparent quickly is that the socket has no explicit way of detecting when a client disconnects.

Detecting Client Disconnections

The call to socket.isClosed() refers to the server-side socket—which is still open. Essentially, the above code has created an infinite loop until we explicitly set conditions under which the socket will be closed. This is great for outputting client messages to the console, but what happens when that client disconnects?

Currently, the server-side socket never detects a disconnect and remains open—thus never satisfying the termination condition for the while loop. To add this functionality, we need to manually check if the socket is connected in Java. This can be accomplished in several ways:

  • socket.getInputStream().read() == -1
  • socket.getInputStream().readLine() == null
  • socket.getOutputStream().write(0) throws an IOException

This approach is similar to how TCP handles congestion control when many duplicate ACKS or timeouts are received; sending (or in this case trying to also receive) very small bytes of data to detect a favorable client-side signal. Like knocking on a door to see if anyone is home.  This can be implemented into the current TestServer class as such:

// Probe client stream, detect invalid response
if(inputStream.read() == -1){

    // Notify via console, close socket connection
    System.out.println("client disconnected. socket closing...");
    socket.close();
}

This code uses the inputStream.read() method to, effectively, check that the client is still connected. The read() method, an extension of the Reader class, will return a -1 when it reaches the EOF or, in this case, an inputStream without any input!

Putting Everything Together

We’ve seen how to create a basic server-client application using the ServerSocket and Socket class provided in the java.net package. We’ve seen how to create a ServerSocket instance, bind that instance to a port and start listening for incoming client connections, and how to detect client-side disconnections. The only thing left to do is put it all together and add in a little bit of house-keeping at the end:

class TestServer {

    public static void main(String[] args) throws IOException {

        // Create server socket, bind to port 1234
        ServerSocket serverSocket = new ServerSocket(1234);

        // Listen for incoming connections
        while(!serverSocket.isClosed()){

            // Open socket for incoming connections
            Socket socket = serverSocket.accept();
            
            // Buffer input stream
            BufferedReader inputStream = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));

            // Listen for input from connection
            while(!(socket.isClosed())){
                
                // Check for client disconnection
                if(inputStream.read() == -1){

                    // Notify via terminal, close connection
                    System.out.println("client disconnected. Socket closing...");
                    socket.close();
                }

                // Listen for user input
                String input = inputStream.readLine();
                if (input != null && !input.trim().equals("")){
                    
                    // Stream valid input to console
                    System.out.println("new message: " + input);
                }
            }
            
            // Close socket input stream
            inputStream.close();
        }
    }
}

Final Thoughts

It’s not readily apparent how to tell if a socket connection in Java has been closed. Either side of the server-client application here only has direct API access to sockets they have created.  This example has demonstrated a very basic way of probing the remote side of a socket connection to determine if that connection has been dropped. The example here is only meant to be an exercise and by no means warranted as robust, accurate, or even meritorious beyond a conceptual discussion.

Zαck West
Full-Stack Software Engineer with 10+ years of experience. Expertise in developing distributed systems, implementing object-oriented models with a focus on semantic clarity, driving development with TDD, enhancing interfaces through thoughtful visual design, and developing deep learning agents.