Hi Niki,
thank you for your reply.

> -----Original Message-----
> From: Niki Dokovski [mailto:nick...@gmail.com]
> 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 moving.

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?

Thanks!


Regards,
Konstantin Preißer


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to