tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Konstantin Preißer <kpreis...@apache.org>
Subject RE: Some Websocket (Async) questions
Date Wed, 23 Oct 2013 13:09:03 GMT
Hi,
some more questions/issues added below:

> -----Original Message-----
> From: Konstantin Preißer [mailto:kpreisser@apache.org]
> Sent: Wednesday, October 23, 2013 2:09 AM
> To: 'Tomcat Users List'
> Subject: Some Websocket (Async) questions
> 
> Hi all,
> 
> while working on the Drawboard Websocket example that is included in
> Tomcat, I found some things in Tomcat's implementation / the Websocket
> Spec which I'm not clear about.
> 
> 
> 1) How to asynchronously close / abort a Websocket session?
> 
> For the Drawboard, I'm using the RemoteEndpoint.Async to send messages
> asynchronously (that is, the sendText(String, SendHandler) will return
> immediately and when sending is finished, SendHandler.onResult() will be
> called).
> 
> Now, there may be a situation where I want to abort a Websocket session
> while async send has not yet finished, or asynchronously close it so that a
> close() method does not block.
> The only way to close a websocket normally (not aborting) seems to be by
> calling Session.close(). However, this method will block if the remote
> endpoint does not read data, and it seems there is no async close() method
> that returns immediately (just like Async.sendText()) even if the closing
> handshake is waiting to be sent on the TCP connection.
> 
> The problem with the Drawboard example is that it needs to be able to
> abort/close Websocket sessions if the client does not read data from the TCP
> connection, to avoid buffering outgoing messages endlessly. Currently, I'm
> just calling session.close() (while async sending has not yet finished), but as
> this method may block, it temporarily stops processing of messages from
> other clients.
> 
> (For example, in .Net there is a CloseAsync()/CloseOutputAsync  method of
> the WebSocket class that asynchronously sends the closing handshake, and
> an Abort() method that immediately aborts the Websocket/TCP connection
> and cancels all pending I/O operations. Ideally, I would need something like
> this Abort() method for immediately aborting the Websocket session).
> 
> 
> 2) It seems that when using RemoteEndpoint.Basic to send data
> synchronously and the remote endpoint does not read from the TCP
> connection, then a TimeoutException (wrapped in IOException) is thrown
> several seconds after calling send(). However, when using
> RemoteEndpoint.Async to send data asynchronously, this is not the case -
> the SendHandler is not called until the remote endpoint continues to read
> data. Is there some configuration to set a Timeout for async sending?
> 
> 
> 3) It seems that session.close() will call the onClose method of the Endpoint,
> even if this was called inside of another event handling method like
> onMessage.
> E.g. if you do something like this in an Endpoint subclass:
> 
>         @Override
>         public void onMessage(String message, boolean last) {
>             session.close();
>         }
> 
> then Endpoint.onClose(..) will be called directly from session.close() (it will be
> called before onMessage(...) returns). This can be a bit confusing, because
> e.g. in Javascript, if you call ws.close() inside of the onmessage handler,  then
> the onclose handler will be called after the onmessage handler returns.
> 
> Is this behavior required by the spec? (E.g. JSR 356 spec says at point 4.5
> @OnClose:
> "The annotation defines that the decorated method be called whenever a
> remote peer
> is about to be disconnected from this endpoint, whether that process is
> initiated by the remote peer, by
> the local container or by a call to session.close(). The container notifies the
> method before the connection
> is brought down [WSC-4.5-1]."
> If the last sentence is to be interpreted that the Websocket closing
> handshake must not be sent until onClose() was called, this would explain it,
> although that would seem a bit strange to me.


4) Tomcat's current implementation of RemoteEndpoint.Async#sendText(String, SendHandler) can
cause StackOverflowErrors (and seems to contradict Oracle's JavaDoc).

In Tomcat, Async#sendText(...) seems to implemented so that when it could send all of the
data immediately, then it directly calls SendHandler#onResult(...); whereas when it couldn't
send the data immediately, the SendHandler will be called from another thread.

Oracle's javadoc for RemoteEndpoint.Async says:

"The completion handlers for the asynchronous methods are always called with a different thread
from that which initiated the send."

Now, imagine the case that you want to send 10000 very small text messages to a client (very
unlikely, but possibly could happen). With synchronous I/O (RemoteEndpoint.Basic), you would
do this in this way:

        RemoteEndpoint.Basic basic = session.getBasicRemote();
        for (int i = 0; i < 10000; i++) {
            basic.sendText("Hi, this is a very short text message, Count: " + i);
        }

In this case, there is no problem.

Now imagine you want to do this asynchronously (using a Callback to be informed when sending
is completed), then you could do it this way:

        final AtomicInteger aint = new AtomicInteger();
        final RemoteEndpoint.Async async = session.getAsyncRemote();
        
        final SendHandler handler = new SendHandler() {
            @Override
            public void onResult(SendResult result) {
                int nextVal = aint.incrementAndGet();
                if (nextVal < 10000) {
                    async.sendText("Hi, this is a very short text message, Count: " + nextVal,
handler);
                }
            }
        };
        async.sendText("Hi, this is a very short text message, Count: " + aint.get(), handler);

The problem here is that because the messages are very short, Tomcat will be able to send
them immediately, calling the SendHandler#onResult() directly from Async.sendText(), which
will eventually cause a StackOverflowError (I could reproduce it).
I think if SendHandler#onResult() was always called from a different thread than the one which
calls Async.send... (like the Javadoc says), then although the performance would probably
be worse, StackOverflowErrors shouldn't occur.

Can you give me some insight about this?


5) Thread safety for Endpoint

The javadoc of class javax.websocket.Endpoint says:

"Each instance of a websocket endpoint is guaranteed not to be called by more than one thread
at a time per active connection. "

As I'm not a native english speaker, I'm not sure if I understand exactly what this means.

I think the following is true:
a) Although instance methods of an Endpoint (onOpen, onError, onMessage of a MessageHandler,
onClose ...) may be called from different threads, it is guaranteed that each instance method
will have to return until another thread can call another instance method (so that instance
methods aren't called by multiple threads at the same time).
b) As soon as onOpen() is called on a Endpoint instance, following methods like onError, onMessage
(in the corresponding message handler), onClose etc. will be called for the same Websocket
Session for which onOpen was called (e.g. after onOpen is called for Session A, it can not
happen that on the same Endpoint Instance onMessage is called for Session B).

However, what about after onClose() is called, which means that the Websocket Session will
not be used anymore - does this mean this particular Endpoint instance will be thrown away,
or could it be re-used for another Websocket session? (I would expect the former)


Thanks!

Regards,
Konstantin Preißer


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message