Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking for a client disconnect on a Java TCP server - output only

Tags:

java

tcp

sockets

I have a Java TCP server which, when a client connects to it, outputs a message to the client every 30 seconds. It is a strict requirement that the client does not send any messages to the server, and that the server does not send any data other than the 30-second interval messages to the client.

When I disconnect the client, the server will not realise this until the next time it tries to write to the client. So it can take up to 30 seconds for the server to recognise the disconnect.

What I want to do is check for the disconnect every few seconds without having to wait, but I am not sure how to do this given that a) the server does not receive from the client and b) the server cannot send any other data. Would anyone please be able to shed some light on this? Thanks.

like image 300
Myn Avatar asked Dec 28 '22 00:12

Myn


1 Answers

Even though your server doesn't "receive" from the client, a non-blocking read on the client socket will tell you that either there's nothing to be read (as you expect), or that the client has disconnected.

If you're using NIO you can simply use a non-blocking Selector loop (with non-blocking sockets) and only write on your 30 second marks. If a SelectionKey is readable and the read on the SocketChannel returns -1 you know the client has disconnected.

EDIT: Another approach with blocking is simply to select with a 30 second timeout. Any client disconnects will cause the select to return and you'll know which ones those are via the read set. The additional thing you'd need to do there is track how long you were blocked in the select to figure out when to do your writes on the 30 second mark (Setting the timeout for the next select to the delta).

Big Edit: After talking to Myn below, offering complete example:

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

    ServerSocket serverSocket = null;
    try {
        serverSocket = new ServerSocket(4444);
    } catch (IOException e) {
        System.err.println("Could not listen on port: 4444.");
        System.exit(1);
    }

    Socket clientSocket = null;
    try {
        clientSocket = serverSocket.accept();
    } catch (IOException e) {
        System.err.println("Accept failed.");
        System.exit(1);
    }

    // Set a 1 second timeout on the socket
    clientSocket.setSoTimeout(1000);

    PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
    BufferedReader in = new BufferedReader(
            new InputStreamReader(
            clientSocket.getInputStream()));

    long myNextOutputTime = System.currentTimeMillis() + 30000;

    String inputLine = null;
    boolean connected = true;
    while (connected)
    {
        try {
            inputLine = in.readLine();
            if (inputLine == null)
            {
                System.out.println("Client Disconnected!");
                connected = false;
            }
        }
        catch(java.net.SocketTimeoutException e)
        {
            System.out.println("Timed out trying to read from socket");
        }

        if (connected && (System.currentTimeMillis() - myNextOutputTime > 0))
        {
            out.println("My Message to the client");
            myNextOutputTime += 30000;
        }


    }

    out.close();
    in.close();
    clientSocket.close();
    serverSocket.close();
}

Worth noting here is that the PrintWriter really moves you far away from the actual socket, and you're not going to catch the socket disconnect on the write (It will never throw an exception, you have to manually check it with checkError()) You could change to using a BufferedWriter instead (requires using flush() to push the output) and handling it like the BufferedReader to catch a disco on the write.

like image 120
Brian Roach Avatar answered Jan 13 '23 12:01

Brian Roach