X

Pavel Bucek's Weblog

  • February 3, 2014

WebSocket Client Reconnect

Another new feature was recently added to Tyrus (Java API for WebSocket Reference Implementation): Client ReconnectHandler. Some client use cases require almost persistent client-to-server connection and don’t really care about lower layer issues, like unstable internet connection.

Tyrus Client now include possibility of registering ReconnectHandler, which can help with these scenarios. Let’s see ReconnectHandler declaration:

1
2
3
4
5
6
7
8
9
10
public class ReconnectHandler {
public boolean onDisconnect(CloseReason closeReason) {
return false;
}
public boolean onConnectFailure(Exception exception) {
return false;
}
}

Method onDisconnect is executed whenever client endpoint @OnClose annotated method is called, method onConnectFailure is executed whenever client has some troubles connecting to remote endpoint (network issues, server returning HTTP Status 500, …). Both methods can return boolean value, which indicates whether client should try to reconnect or not.

It is perfectly fine to wait in any of these methods for some time – I would recommend it for onConnectFailure, because there usually is some reason for network failure and repeating requests without any delay can prolong them or even make them worse. Also I would recommend include some kind of counter, which would set the number of reconnect attempts.

Example implementation could look like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ClientManager client = ClientManager.createClient();
ClientManager.ReconnectHandler reconnectHandler = new ClientManager.ReconnectHandler() {
private int counter = 0;
@Override
public boolean onDisconnect(CloseReason closeReason) {
counter++;
if (counter <= 3) {
System.out.println("### Reconnecting... (reconnect count: " + counter + ")");
return true;
} else {
return false;
}
}
@Override
public boolean onConnectFailure(Exception exception) {
counter++;
if (counter <= 3) {
System.out.println("### Reconnecting... (reconnect count: " + counter + ") " + exception.getMessage());
// Thread.sleep(...) or something other "sleep-like" expression can be put here - you might want
// to do it here to avoid potential DDoS when you don't limit number of reconnects.
return true;
} else {
return false;
}
}
};
client.getProperties().put(ClientManager.RECONNECT_HANDLER, reconnectHandler);
client.connectToServer(...)

Join the discussion

Comments ( 9 )
  • guest Thursday, April 3, 2014

    Hi Pavel

    you said "Method onDisconnect is executed whenever client endpoint @OnClose annotated method is called". But is it also called when the server machine crashes, is it also called when the JVM process on the server crahsed and it it also called when the network crashes (hardware cable or software)? In all these use cases the @OnClose method is not called. I could try the implementation but would also like to know how it "should" work from a specification perspective.

    Regards

    Alain


  • Pavel Thursday, April 3, 2014

    Hi Alain,

    in all of cases mentioned, @OnClose should be called on client side - when TCP connection is broken, client needs to be notified - @OnClose is called and CloseReason should be 1006: Closed abnormally. See https://tools.ietf.org/html/rfc6455#section-7.4.1 for more details.

    If you have any testcase which proves me wrong, please retest it with recent Tyrus version (1.5 or 1.6-SNAPSHOT) and if you can still reproduce it, file a new issue at https://java.net/jira/browse/TYRUS and attach that testcase or at least description which would allow me to recostruct it.

    Thanks!

    Pavel


  • Alan Wednesday, December 17, 2014

    Hi Pavel,

    I got this to work perfectly on the desktop with Tyrus 1.9 and prior versions as is described here: https://tyrus.java.net/documentation/1.9/user-guide.html#d0e1311. On Android 4.4.4 & 5.0 however, nothing happens (its as if there are no handlers). Is there some extra configuration required somewhere?

    Thanks,

    Alan


  • Pavel Monday, January 5, 2015

    Hi Alan,

    seems like you are not the only one who is experiencting this issue - its tracked as https://java.net/jira/browse/TYRUS-391

    Thanks for reporting it!

    Pavel


  • guest Thursday, May 14, 2015

    I am using tyrus-standalone-client-1.10.jar, in my client java websocket application and websocket server runs in tomcat 8 server. I have a use case where I need to use OnClose method at client app for some task, also I need to use this onDisconnect and onConnectFailure methods to reconnect.

    I have 3 questions:

    1. Will this onDisconnect and onConnectFailure methods will connect to same session? Since I need to continue my communication again between different websockets clients.

    2. In websockets to communicate between clients we use map to store the session. Since the server is down and later gets reconnected, this data inside the map is lost. Do we have any approach to keep hold this Map?

    3. Do we have any mechanism to stop triggering OnClose method while reconnection? Why because in my case, this method has some task which should not run on re-connect.

    4. ClientManager.RECONNECT_HANDLER is deprecated. Do we have any alternative for this?


  • Pavel Thursday, May 14, 2015

    Hey Guest,

    1) no. Once the connection is broken, WebSocket session is lost. This cannot be easily solved or fixed, since it is a requirement from RFC 6455 - WebSocket protocol. WebLogic Server does have a feature which stores some data from previous session and puts them into newly created one, you could use that (or implement something similar, it is not that hard to do).

    2) Not sure where the map is and whether you mean WebSocket Session or Servlet Session. Anyway, on server side, @OnClose will be invoked, so you can store your WebSocket Session related data somewhere else and make them accessible to reconnecting client.

    3) No, and we don't even want to do that. @OnClose is tied to RFC 6455 - it signals that WebSocket connection is lost with appropriate CloseCode. That will happen no matter what. You can ignore it if you want, put it into some other cache where it can be picked up when @OnOpen is called again etc..

    4) See javadoc, @deprecated comment:

    /**

    * Property usable in {@link #getProperties()}.

    * <p/>

    * Value must be {@link org.glassfish.tyrus.client.ClientManager.ReconnectHandler} instance.

    *

    * @deprecated please use {@link org.glassfish.tyrus.client.ClientProperties#RECONNECT_HANDLER}.

    */

    @SuppressWarnings("UnusedDeclaration")

    public static final String RECONNECT_HANDLER = ClientProperties.RECONNECT_HANDLER;

    Regards,

    Pavel


  • guest Monday, April 18, 2016

    ClientManager.RECONNECT_HANDLER has Deprecated in tyrus1.12,

    please use ClientProperties.RECONNECT_HANDLER.


  • guest Monday, September 12, 2016

    Hey Pavel,

    great article but I'm still facing disconnect/reconnection problem.

    I'm using tyrus standalone client 1.13 and I created ClientManager same way as you was include connectToServer.

    Problem is that I don't receiving any onDisconnect/onConnectFailure or onClose/onError(in annotated class) in case when I intentionally disconnect computer from network (off course after successful connect).

    Is there any way how to receive those info?


  • Pavel Monday, September 12, 2016

    Hey guest,

    depends on which Client runtime you are using, but I assume you are on the default (grizzly) client - I know that networking often has some platform related differences, maybe that's what you are hitting right now. Can you (just for the test) periodically call Session#isOpen on your client? (+ bonus - try to send a message. It can be an unsolicited pong, see http://docs.oracle.com/javaee/7/api/javax/websocket/RemoteEndpoint.html#sendPong-java.nio.ByteBuffer- .)

    Also, having separate process, which would just open a connection and wait would help (to see whether it will be disconnected).


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha