How to properly detect a client disconnect in servlet spec 3?
Asked Answered
C

2

7

I have tried writing to the response, because there is no proper disconnect callback:

private boolean write(byte[] output, AsyncContext context) {
    try {
        OutputStream stream  = context.getResponse().getOutputStream();
        stream.write(output);
        stream.flush();
        return true;
    } catch (IOException ex) {
        //client disconnected
        log.error(ex);
        removeAsyncContext(context);
        return false;
    }

}

But this didn't seem to the trick. When the client is disconnected, writing and flushing the buffer did not throw an exception.

The weird thing is, the second time you try to write to the output stream (after the disconnect), the write does throw an exception. It looks like the first time you write/flush it,some internal state is set to error, without notifying.

I have tried on both Jetty 8 and Tomcat 7 and I see the same behavior.

Is there a solution to find out whether the message is received by the client? Am I missing something?

Claiborn answered 19/8, 2011 at 16:19 Comment(4)
I noticed that writing two blocks, or after the flush write an extra byte, then flush it again, solves it. Although I find that a bit 'hacky'.Claiborn
See #2962696: TCP/IP doesn't provide means for a callback in case the client disconnected - you'll have to write data. You can create an abstraction over the hackiness though.Shelving
The problem is that writing data doesn't not throw an exception while the client has disconnected. I found out that writing extra bytes does not always throw an exception.Claiborn
Also a proper TCP connection termination (FIN & ACK) is not picked up.Claiborn
C
1

I recognize you are looking for a proper way of detecting disconnects, but for those who don't mind a kludge:

Note: This method periodically sends space characters to the client that must be trimmed before interpreting the results. This is the kludgey part.

  1. Start a thread that has access to the writer/outputstream of the servlet response. This thread sends space characters periodically (I used 1 second intervals) to the client. Wrap in a IOException try/catch block that sets your abort flag.

  2. If the connection is closed, most servlets will throw a flavor of EOFException (which is an IOException) when data cannot be delivered to the client. You are catching this exception and setting your abort flag.

  3. When the abort flag is caught, you have options. You can nuke the executing thread, have all your processing periodically check for the abort flag, push an exception into the executing thread, or do any number of things (not detailed here).

  4. Should the process finish successfully, you will get your results prefixed by a number of spaces. Again, remember to trim these on your client.

Clevey answered 22/7, 2015 at 18:16 Comment(0)
A
0

In my experience when a client disconnects from a servlet there is an exception referring to a Broken Pipe.

For example: Broken Pipe when writing bytes in ServletOutputStream

I would suggest catching java.net.SocketException and looking at the exception details to verify if it is a broken pipe (as a starting point):

Caused by: ClientAbortException:  java.net.SocketException: Broken pipe
    at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:358)
    at org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:354)
    at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:381)
    at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:370)
    at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:89)
Albertype answered 8/4, 2013 at 12:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.