13
13
14
14
import javax .net .ssl .SSLException ;
15
15
16
- import org .java_websocket .client .WebSocketClient ;
17
16
import org .java_websocket .handshake .ServerHandshake ;
18
17
import org .slf4j .Logger ;
19
18
import org .slf4j .LoggerFactory ;
@@ -31,7 +30,9 @@ public class WebSocketConnection implements InternalConnection, WebSocketListene
31
30
private static final Gson GSON = new Gson ();
32
31
33
32
private static final String INTERNAL_EVENT_PREFIX = "pusher:" ;
34
- static final String PING_EVENT_SERIALIZED = "{\" event\" : \" pusher:ping\" }" ;
33
+ private static final String PING_EVENT_SERIALIZED = "{\" event\" : \" pusher:ping\" }" ;
34
+ private static final int MAX_RECONNECTION_ATTEMPTS = 6 ; //Taken from the Swift lib
35
+ private static final int MAX_RECONNECT_GAP_IN_SECONDS = 30 ;
35
36
36
37
private final Factory factory ;
37
38
private final ActivityTimer activityTimer ;
@@ -42,6 +43,8 @@ public class WebSocketConnection implements InternalConnection, WebSocketListene
42
43
private volatile ConnectionState state = ConnectionState .DISCONNECTED ;
43
44
private WebSocketClientWrapper underlyingConnection ;
44
45
private String socketId ;
46
+ private int reconnectAttempts = 0 ;
47
+
45
48
46
49
public WebSocketConnection (
47
50
final String url ,
@@ -68,20 +71,24 @@ public void connect() {
68
71
@ Override
69
72
public void run () {
70
73
if (state == ConnectionState .DISCONNECTED ) {
71
- try {
72
- underlyingConnection = factory
73
- .newWebSocketClientWrapper (webSocketUri , proxy , WebSocketConnection .this );
74
- updateState (ConnectionState .CONNECTING );
75
- underlyingConnection .connect ();
76
- }
77
- catch (final SSLException e ) {
78
- sendErrorToAllListeners ("Error connecting over SSL" , null , e );
79
- }
74
+ tryConnecting ();
80
75
}
81
76
}
82
77
});
83
78
}
84
79
80
+ private void tryConnecting (){
81
+ try {
82
+ underlyingConnection = factory
83
+ .newWebSocketClientWrapper (webSocketUri , proxy , WebSocketConnection .this );
84
+ updateState (ConnectionState .CONNECTING );
85
+ underlyingConnection .connect ();
86
+ }
87
+ catch (final SSLException e ) {
88
+ sendErrorToAllListeners ("Error connecting over SSL" , null , e );
89
+ }
90
+ }
91
+
85
92
@ Override
86
93
public void disconnect () {
87
94
factory .queueOnEventThread (new Runnable () {
@@ -185,6 +192,7 @@ private void handleConnectionMessage(final String message) {
185
192
socketId = (String )dataMap .get ("socket_id" );
186
193
187
194
updateState (ConnectionState .CONNECTED );
195
+ reconnectAttempts = 0 ;
188
196
}
189
197
190
198
@ SuppressWarnings ("rawtypes" )
@@ -251,12 +259,45 @@ public void run() {
251
259
252
260
@ Override
253
261
public void onClose (final int code , final String reason , final boolean remote ) {
254
- if (state == ConnectionState .DISCONNECTED ) {
255
- log .error ("Received close from underlying socket when already disconnected. " + "Close code ["
262
+ if (state == ConnectionState .DISCONNECTED || state == ConnectionState . RECONNECTING ) {
263
+ log .error ("Received close from underlying socket when already disconnected." + "Close code ["
256
264
+ code + "], Reason [" + reason + "], Remote [" + remote + "]" );
257
265
return ;
258
266
}
259
267
268
+ //Reconnection logic
269
+ if (state == ConnectionState .CONNECTED || state == ConnectionState .CONNECTING ){
270
+
271
+ if (reconnectAttempts < MAX_RECONNECTION_ATTEMPTS ){
272
+ tryReconnecting ();
273
+ }
274
+ else {
275
+ updateState (ConnectionState .DISCONNECTING );
276
+ cancelTimeoutsAndTransitonToDisconnected ();
277
+ }
278
+ return ;
279
+ }
280
+
281
+ if (state == ConnectionState .DISCONNECTING ){
282
+ cancelTimeoutsAndTransitonToDisconnected ();
283
+ }
284
+ }
285
+
286
+ private void tryReconnecting () {
287
+ reconnectAttempts ++;
288
+ updateState (ConnectionState .RECONNECTING );
289
+ long reconnectInterval = Math .min (MAX_RECONNECT_GAP_IN_SECONDS , reconnectAttempts * reconnectAttempts );
290
+
291
+ factory .getTimers ().schedule (new Runnable () {
292
+ @ Override
293
+ public void run () {
294
+ underlyingConnection .removeWebSocketListener ();
295
+ tryConnecting ();
296
+ }
297
+ }, reconnectInterval , TimeUnit .SECONDS );
298
+ }
299
+
300
+ private void cancelTimeoutsAndTransitonToDisconnected () {
260
301
activityTimer .cancelTimeouts ();
261
302
262
303
factory .queueOnEventThread (new Runnable () {
@@ -290,7 +331,7 @@ private class ActivityTimer {
290
331
private Future <?> pingTimer ;
291
332
private Future <?> pongTimer ;
292
333
293
- public ActivityTimer (final long activityTimeout , final long pongTimeout ) {
334
+ ActivityTimer (final long activityTimeout , final long pongTimeout ) {
294
335
this .activityTimeout = activityTimeout ;
295
336
this .pongTimeout = pongTimeout ;
296
337
}
@@ -299,7 +340,7 @@ public ActivityTimer(final long activityTimeout, final long pongTimeout) {
299
340
* On any activity from the server - Cancel pong timeout - Cancel
300
341
* currently ping timeout and re-schedule
301
342
*/
302
- public synchronized void activity () {
343
+ synchronized void activity () {
303
344
if (pongTimer != null ) {
304
345
pongTimer .cancel (true );
305
346
}
@@ -320,7 +361,7 @@ public void run() {
320
361
/**
321
362
* Cancel any pending timeouts, for example because we are disconnected.
322
363
*/
323
- public synchronized void cancelTimeouts () {
364
+ synchronized void cancelTimeouts () {
324
365
if (pingTimer != null ) {
325
366
pingTimer .cancel (false );
326
367
}
0 commit comments