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

Reply via email to