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,

-Harri

Reply via email to