> I have a problem with my java application related to HTTP communication. > > Application description: > > 1. Client – server. Server is running in servlet container. We > use Tomcat. > > Client use java HTTP library to communicate with the server. > > 2. When client establish connection to the server it sends GET > request (keepalive) and server creates AsyncContext for the client > with 10 days timeout. > > 3. After connection established server periodically sends data > to the client using AsyncContext and on the client side there is > special thread which reads and processes the data. > > Client reading thread example: > > private class GetRunnable implements Runnable { > > private final HttpURLConnection httpConn; > private final MyConnection myConn; > > public GetRunnable(final MyConnection myConn) throws > IOException, AppException { > this.myConn = myConn; > final String jSession = myConn.getJSession(); > httpConn = openHttpUrlConnection(false, > httpPostTimeout, jSession); > httpConn.connect(); > final int respCode = httpConn.getResponseCode(); > if (respCode != HttpURLConnection.HTTP_OK) { > String info = "Incorrect response from server, > responseCode:" + respCode + ", message:" + httpConn.getResponseMessage(); > log.error(info); > throw new AppException(info); > } else { > log.trace("GET connected"); > } > } > > @Override > public void run() { > try (final BufferedReader reader = new BufferedReader > (new InputStreamReader(httpConn.getInputStream(), "UTF-8")) ) { > log.info("doGet STARTED"); > final Thread t = Thread.currentThread(); > while (!t.isInterrupted()) { > log.trace("before readline"); > final String line = reader.readLine(); > log.trace("after readline: [" + line + "]"); > > //INFO: DATA PROCESSING HERE > } > } catch (IOException e) { > log.error("Error while doGet"); > } catch (Throwable e) { > log.debug("doGet STOPED..."); > log.error("Error while read input msg, do logoff", e); > logoff(); > throw e; > } > log.debug("doGet STOPED..."); > myCallback.onGetClose(myConn, connInfo); > } > } > > Server side code to push data looks like this: > > protected int sendMsgs(final MyConnection myConn, final String info) { > > int res; > try { > final AsyncContext lAc = _ac; > final ServletRequest req = lAc.getRequest(); > if (!req.isAsyncStarted()) { > log.debug("AsyncStarted=false on begin, uid:" + > uid + ", sid:" + sid); > return -1; > } > > final ServletResponse res = lAc.getResponse(); > ServletOutputStream os = null; > final String text = getData(myConn); > > if (os == null) > os = res.getOutputStream(); > > write2stream(os, text, 0x4000); > os.println(); > > if (os != null) > os.flush(); > } catch(IOException ex) { > log.error("Notify failed"); > } catch (InterruptedException e) { > log.error("Notify failed"); > Thread.currentThread().interrupt(); > } catch (Throwable e) { > log.error("Notify failed"); > logoff(true); > } > > return res; > } > > public static void write2stream(final OutputStream outputStream, > final String text, int bufferSize) throws IOException { > final CharsetEncoder encoder = getHttpEncodingCharset > ().newEncoder(); > > final int fullLen = text.length(); > final int byteBufferSize = Math.min(bufferSize, fullLen); > > final int en = (int)(byteBufferSize * (double) > encoder.maxBytesPerChar()); > final byte[] byteArray = new byte[en]; > > final ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray); > final CharBuffer charBuffer = CharBuffer.wrap(text); > for(int pos=0, len=byteBufferSize; > pos < fullLen; > pos=len, len=Math.min(pos+byteBufferSize, > fullLen)) { > > try { > final CharBuffer window = charBuffer.subSequence(pos, len); > > CoderResult res = encoder.encode(window, > byteBuffer, true); > > if (!res.isUnderflow()) > res.throwException(); > > res = encoder.flush(byteBuffer); > > if (!res.isUnderflow()) > res.throwException(); > > } catch (final CharacterCodingException x) { > throw new Error(x); > } > > outputStream.write(byteArray, 0, byteBuffer.position()); > > byteBuffer.clear(); > encoder.reset(); > } > } > > Usually this code works fine but it there is no data from server to > client for 1 day and after 24 hours (can be 16 or 12) data appears, > server cannot send data to the client. > In the log there is no error. It is written that everything flushed > but client still waiting for data in “final String line = reader.readLine();” > When 2nd portion of data is sent by the server, then during flush I > see the following error > > 2016-01-26 00:00:00,051|INFO |GWNotify-2/50 |ClientAbort > 2016-01-26 00:00:00,051|TRACE|GWNotify-2/50 | > ClientAbortException:java.io.IOException: APR error: -32 > org.apache.catalina.connector.ClientAbortException: > java.io.IOException: APR error: -32 > at org.apache.catalina.connector.OutputBuffer.doFlush > (OutputBuffer.java:353) ~[catalina.jar:8.0.30] > at org.apache.catalina.connector.OutputBuffer.flush > (OutputBuffer.java:317) ~[catalina.jar:8.0.30] > at > org.apache.catalina.connector.CoyoteOutputStream.flush > (CoyoteOutputStream.java:110) ~[catalina.jar:8.0.30] > > Caused by: java.io.IOException: APR error: -32 > at > org.apache.coyote.http11.InternalAprOutputBuffer.writeToSocket > (InternalAprOutputBuffer.java:291) ~[tomcat-coyote.jar:8.0.30] > at > org.apache.coyote.http11.InternalAprOutputBuffer.writeToSocket > (InternalAprOutputBuffer.java:244) ~[tomcat-coyote.jar:8.0.30] > at > org.apache.coyote.http11.InternalAprOutputBuffer.flushBuffer > (InternalAprOutputBuffer.java:213) ~[tomcat-coyote.jar:8.0.30] > at > org.apache.coyote.http11.AbstractOutputBuffer.flush > (AbstractOutputBuffer.java:305) ~[tomcat-coyote.jar:8.0.30] > at > org.apache.coyote.http11.AbstractHttp11Processor.action > (AbstractHttp11Processor.java:765) ~[tomcat-coyote.jar:8.0.30] > at org.apache.coyote.Response.action(Response.java: > 177) ~[tomcat-coyote.jar:8.0.30] > at org.apache.catalina.connector.OutputBuffer.doFlush > (OutputBuffer.java:349) ~[catalina.jar:8.0.30] > ... 6 more > > Finally after I stop server I see that GetRunnable client thread is > still alive and waiting data from the server in “final String line = > reader.readLine();”!!!
Hi, I would send some keep-alive data (e.g. current timestamp) maybe once per day or once every 5 hours. The client can just dismiss that data. This might help in some situations but there is no 100% guarantee that it works always. Regards, Christoph This Email was scanned by Sophos Anti Virus