tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Konstantin Preißer <>
Subject RE: 8.0.x / 7.0.x progress
Date Sat, 05 Oct 2013 13:56:31 GMT
Hi Niki,
thank you for your reply.

> -----Original Message-----
> From: Niki Dokovski []
> Sent: Saturday, October 5, 2013 7:47 AM
> To: Tomcat Developers List
> Subject: Re: 8.0.x / 7.0.x progress

> > I have installed Wireshark now and can confirm that Firefox (after it is
> > resumed) still sends data over the TCP connection which Tomcat seems not
> to
> > be able to read.
> >
> Are there ACKs for the TCP packets?
> I'll try to reproduce the case.

Yes, I verified with Wireshark that Tomcat ACKs these packets correctly, but does not seem
to be able to process them. I continued to send data from Firefox (SEQ went up to 61373) to
ensure that the ACKs were not just resulting from buffering somewhere in the Windows or Java
network stack, and I can confirm that Tomcat still ACKed these SEQ numbers.

Note, that when using the BIO connector, everything works fine - the problems only appear
with NIO or APR connector.

> > > > B) For BIO connector:
> > > > I noticed that on Tomcat with BIO connector, when using a
> > > RemoteEndpoint.Async to asynchronously send data over the
> WebSocket
> > > connection, sendText(String, SendHandler) (or similar methods) will
> > block if
> > > the Remote endpoint does not read data from the connection, whereas
> for
> > > NIO and APR connector this method will always return immediately.
> > > > Is it intended that for the BIO connector those methods are blocking?
> > As
> > > the javadoc says, "Initiates the asynchronous transmission of a text
> > message.
> > > This method returns before the message is transmitted.", I would have
> > > expected that e.g. another Thread is used to write in blocking mode, so
> > that
> > > the sendText() method can return immediately.
> > >
> > > You can't do non-blocking IO with the BIO connector. All communication
> > > with BIO is blocking. This is working as designed.
> >
> > OK, but my understanding was that there is a difference between the
> terms
> > "synchronous/asynchronous" and "blocking/non-blocking" (but maybe the
> > meaning differs from programming language to programming language).
> >
> An excellent and detailed explanation on this topic can be found in  "UNIX
> Network Programing"  R. Stevens Vol 1 3td ed. p154-p160

Thank you for that information.
However, currently I don't have that book (and I'm not very familiar with UNIX as I'm a windows
guy ;) ), but please let me try to illustrate my use case with the Snake example.

The SnakeTimer class has a broadcast() method that broadcasts a message to all connected clients
by looping over them and calling snake.sendMessage(...), which in turn  will call session.getBasicRemote().sendText(msg)
to send the message. As the methods of RemoteEndpoint.Basic are blocking, this means that
if some client establishes a Websocket connection and stops to read from it, then after some
time the broadcast() method will hang waiting for sendText() to complete, which will block
until that specific client continues to read data. This means that messages cannot be broadcasted
to all other clients, so for them all snakes stop moving.

A simple way that comes in my mind to solve this would be to use an additional thread for
each connected client. This thread would take message from a Queue and send them to the client
while the SnakeTimer's broadcast() would add a messages to the queue of each client. Then,
if some client's sendText() method blocks (because the client does not read data from the
connection), it does not interfere with the other clients, so they still would see the snakes

However, this is costly because it would require an additional thread per Websocket connection.

So, another way would be to look at RemoteEndpoint.Async that can send messages asynchronously.
The javadoc (from Oracle) of Async#sendText(String, SendHandler) says, "Initiates the asynchronous
transmission of a text message. This method returns before the message is transmitted."
For me, this looks like I can call this method and be sure that it does not block when some
client does not read data from the websocket connection. Therefore, I could change the broadcast()
method to use RemoteEndpoint.Async#sendText(String, SendHandler) to start async sending of
a message, if no message is already on the way to the client (if it is, it would need to buffer
them somewhere). Then, when SendHandler#onResult() callback is called, the code can look if
it needs to send additional messages to the client (and if yes, call sendText(...) again).
This will remove the need from using a separate thread for sending the data.

The problem is now that if Tomcat's implementation of this Async#sendText(...) method is blocking
when using the BIO connector, it will mean that with BIO I get the problem again that the
snakes will stop moving on all clients if one client stops reading data (might be considered
as a DoS), but if I use NIO or APR, everything will be fine. That would mean that I have to
use different implementations of broadcasting data to clients, depending on the underlying
connector that is being used (blocking or non-blocking).

Therefore, I would expect that RemoteEndpoint.Async#sendText(String, SendHandler) behaves
the same regardless of whether BIO or NIO is used. For NIO, Tomcat actually can use a non-blocking
write operation and only acquire a thread for calling the SendHandler#onResult() callback
(but I don't have much knowledge of the internal Tomcat code so I could be wrong here); whereas
for BIO, Tomcat could acquire a thread for the complete duration of the blocking write operation
plus calling the callback (so it would still be an async write operation although the underlying
I/O uses blocking write).

Have I missed something here?


Konstantin Preißer

To unsubscribe, e-mail:
For additional commands, e-mail:

View raw message