On 07/03/17 08:28, Pesonen, Harri wrote: > Hello, we have a problem that Tomcat WebSocket does not always send > asynchronous messages. This problem is random, and it has been reproduced in > Tomcat 8.5.6 and 8.5.11. Synchronized operations work fine, and also the > asynchronous operations work except in one special case. When there is a big > message that we want to send to client, we split it into 16 kB packets for > technical reasons, and then we send them very quickly after each other using > > /** > * Initiates the asynchronous transmission of a binary message. This method > returns before the message > * is transmitted. Developers provide a callback to be notified when the > message has been > * transmitted. Errors in transmission are given to the developer in the > SendResult object. > * > * @param data the data being sent, must not be {@code null}. > * @param handler the handler that will be notified of progress, must not be > {@code null}. > * @throws IllegalArgumentException if either the data or the handler are > {@code null}. > */ > void sendBinary(ByteBuffer data, SendHandler handler); > > Because there can be only one ongoing write to socket, we use Semaphore that > is released on the SendHandler callback: > > public void onResult(javax.websocket.SendResult result) { > semaphore.release(); > > So the code to send is actually: > > semaphore.acquireUninterruptibly(); > async.sendBinary(buf, asyncHandler); > > This works fine in most cases. But when we send one 16 kB packet and then > immediately one smaller packet (4 kB), then randomly the smaller packet is > not actually sent, but only after we call > > async.sendPing(new byte[0]) > > in another thread. sendPing() is called every 20 seconds to keep the > WebSocket connection alive. This means that the last packet gets extra delay > on client, which varies between 0 - 20 seconds. > > We have an easy workaround to the problem. If we call flushBatch() after each > sendBinary(), then it works great, but this means that the sending is not > actually asynchronous, because flushBatch() is synchronous. > Also we should not be forced to call flushBatch(), because we are not > enabling batching. Instead we make sure that it is disabled: > > if (async.getBatchingAllowed()) { > async.setBatchingAllowed(false); > > So the working code is: > > semaphore.acquireUninterruptibly(); > async.sendBinary(buf, asyncHandler); > async.flushBatch(); > > Normally the code works fine without flushBatch(), if there is delay between > the messages, but when we send the messages right after each other, then the > last small message is not always sent immediately. > I looked at the Apache WebSocket code, but it was not clear to me what is > happening there. > Any ideas what is going on here? Any ideas how I could troubleshoot this more?
Thanks for providing such a clear description of the problem you are seeing. It sounds like there is a race condition somewhere in the WebSocket code. With the detail you have provided, I think there is a reasonable chance of finding via code inspection. This is next (and currently last) on the list of things I want to look at before starting the release process for 9.0.x and 8.5.x. Mark --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org For additional commands, e-mail: users-h...@tomcat.apache.org