Skip to content

Commit dfc40a5

Browse files
committed
Implementation for a lost connection checker
Checking lost connections through a timer sending pings to connected websockets Also moving setTCPNODELAY to WebSocketAdapter
1 parent bd2060f commit dfc40a5

File tree

5 files changed

+166
-57
lines changed

5 files changed

+166
-57
lines changed

src/main/java/org/java_websocket/WebSocket.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.java_websocket.drafts.Draft;
88
import org.java_websocket.framing.Framedata;
99
import org.java_websocket.framing.Framedata.Opcode;
10+
import org.java_websocket.framing.FramedataImpl1;
1011

1112
public interface WebSocket {
1213
/**
@@ -90,6 +91,11 @@ public enum READYSTATE {
9091
*/
9192
public abstract void sendFrame( Framedata framedata );
9293

94+
/**
95+
* Send a ping to the other end
96+
* @throws NotYetConnectedException websocket is not yet connected
97+
*/
98+
public void sendPing() throws NotYetConnectedException;
9399
/**
94100
* Allows to send continuous/fragmented frames conveniently. <br>
95101
* For more into on this frame type see http://tools.ietf.org/html/rfc6455#section-5.4<br>

src/main/java/org/java_websocket/WebSocketAdapter.java

Lines changed: 131 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
package org.java_websocket;
22

3-
import java.net.InetSocketAddress;
4-
53
import org.java_websocket.drafts.Draft;
64
import org.java_websocket.exceptions.InvalidDataException;
75
import org.java_websocket.exceptions.InvalidHandshakeException;
6+
import org.java_websocket.framing.CloseFrame;
87
import org.java_websocket.framing.Framedata;
98
import org.java_websocket.framing.Framedata.Opcode;
109
import org.java_websocket.framing.FramedataImpl1;
@@ -13,11 +12,134 @@
1312
import org.java_websocket.handshake.ServerHandshake;
1413
import org.java_websocket.handshake.ServerHandshakeBuilder;
1514

15+
import java.net.InetSocketAddress;
16+
import java.util.Collection;
17+
import java.util.Timer;
18+
import java.util.TimerTask;
19+
1620
/**
1721
* This class default implements all methods of the WebSocketListener that can be overridden optionally when advances functionalities is needed.<br>
1822
**/
1923
public abstract class WebSocketAdapter implements WebSocketListener {
2024

25+
/**
26+
* Attribute which allows you to deactivate the Nagle's algorithm
27+
*/
28+
private boolean tcpNoDelay;
29+
30+
/**
31+
* Attribute for a timer allowing to check for lost connections
32+
*/
33+
private Timer connectionLostTimer;
34+
/**
35+
* Attribute for a timertask allowing to check for lost connections
36+
*/
37+
private TimerTask connectionLostTimerTask;
38+
39+
/**
40+
* Attribute for the lost connection check interval
41+
*/
42+
private int connectionLostTimeout = 60;
43+
44+
/**
45+
* Get the interval checking for lost connections
46+
* Default is 60 seconds
47+
* @return the interval
48+
*/
49+
public int getConnectionLostTimeout() {
50+
return connectionLostTimeout;
51+
}
52+
53+
/**
54+
* Setter for the interval checking for lost connections
55+
* A value >= 0 results in the check to be deactivated
56+
*
57+
* @param connectionLostTimeout the interval in seconds
58+
*/
59+
public void setConnectionLostTimeout( int connectionLostTimeout ) {
60+
this.connectionLostTimeout = connectionLostTimeout;
61+
if (this.connectionLostTimeout <= 0) {
62+
stopConnectionLostTimer();
63+
} else {
64+
startConnectionLostTimer();
65+
}
66+
}
67+
68+
/**
69+
* Stop the connection lost timer
70+
*/
71+
protected void stopConnectionLostTimer() {
72+
if (connectionLostTimer != null ||connectionLostTimerTask != null) {
73+
if( WebSocketImpl.DEBUG )
74+
System.out.println( "Connection lost timer stoped" );
75+
cancelConnectionLostTimer();
76+
}
77+
}
78+
/**
79+
* Start the connection lost timer
80+
*/
81+
protected void startConnectionLostTimer() {
82+
if (this.connectionLostTimeout <= 0) {
83+
if (WebSocketImpl.DEBUG)
84+
System.out.println("Connection lost timer deactivated");
85+
return;
86+
}
87+
if (WebSocketImpl.DEBUG)
88+
System.out.println("Connection lost timer started");
89+
cancelConnectionLostTimer();
90+
connectionLostTimer = new Timer();
91+
connectionLostTimerTask = new TimerTask() {
92+
@Override
93+
public void run() {
94+
for (WebSocket conn: connections()) {
95+
conn.sendPing();
96+
}
97+
}
98+
};
99+
connectionLostTimer.scheduleAtFixedRate( connectionLostTimerTask,connectionLostTimeout * 1000, connectionLostTimeout * 1000 );
100+
}
101+
102+
/**
103+
* Getter to get all the currently available connections
104+
* @return the currently available connections
105+
*/
106+
protected abstract Collection<WebSocket> connections();
107+
108+
/**
109+
* Cancel any running timer for the connection lost detection
110+
*/
111+
private void cancelConnectionLostTimer() {
112+
if( connectionLostTimer != null ) {
113+
connectionLostTimer.cancel();
114+
connectionLostTimer = null;
115+
}
116+
if( connectionLostTimerTask != null ) {
117+
connectionLostTimerTask.cancel();
118+
connectionLostTimerTask = null;
119+
}
120+
}
121+
122+
/**
123+
* Tests if TCP_NODELAY is enabled.
124+
*
125+
* @return a boolean indicating whether or not TCP_NODELAY is enabled for new connections.
126+
*/
127+
public boolean isTcpNoDelay() {
128+
return tcpNoDelay;
129+
}
130+
131+
/**
132+
* Setter for tcpNoDelay
133+
* <p>
134+
* Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) for new connections
135+
*
136+
* @param tcpNoDelay true to enable TCP_NODELAY, false to disable.
137+
*/
138+
public void setTcpNoDelay( boolean tcpNoDelay ) {
139+
this.tcpNoDelay = tcpNoDelay;
140+
}
141+
142+
21143
/**
22144
* This default implementation does not do anything. Go ahead and overwrite it.
23145
*
@@ -75,25 +197,26 @@ public void onWebsocketPong( WebSocket conn, Framedata f ) {
75197
/**
76198
* Gets the XML string that should be returned if a client requests a Flash
77199
* security policy.
78-
*
200+
* <p>
79201
* The default implementation allows access from all remote domains, but
80202
* only on the port that this WebSocketServer is listening on.
81-
*
203+
* <p>
82204
* This is specifically implemented for gitime's WebSocket client for Flash:
83205
* http://github.com/gimite/web-socket-js
84-
*
206+
*
85207
* @return An XML String that comforts to Flash's security policy. You MUST
86-
* not include the null char at the end, it is appended automatically.
208+
* not include the null char at the end, it is appended automatically.
87209
* @throws InvalidDataException thrown when some data that is required to generate the flash-policy like the websocket local port could not be obtained e.g because the websocket is not connected.
88210
*/
89211
@Override
90212
public String getFlashPolicy( WebSocket conn ) throws InvalidDataException {
91213
InetSocketAddress adr = conn.getLocalSocketAddress();
92-
if(null == adr){
214+
if( null == adr ) {
93215
throw new InvalidHandshakeException( "socket not bound" );
94216
}
95217

96-
return "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"" + adr.getPort() +"\" /></cross-domain-policy>\0";
218+
return "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"" + adr.getPort() + "\" /></cross-domain-policy>\0";
97219
}
98220

221+
99222
}

src/main/java/org/java_websocket/WebSocketImpl.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.java_websocket.framing.CloseFrameBuilder;
1212
import org.java_websocket.framing.Framedata;
1313
import org.java_websocket.framing.Framedata.Opcode;
14+
import org.java_websocket.framing.FramedataImpl1;
1415
import org.java_websocket.handshake.*;
1516
import org.java_websocket.server.WebSocketServer.WebSocketWorker;
1617
import org.java_websocket.util.Charsetfunctions;
@@ -613,6 +614,12 @@ public void sendFrame( Framedata framedata ) {
613614
write( draft.createBinaryFrame( framedata ) );
614615
}
615616

617+
public void sendPing() throws NotYetConnectedException {
618+
FramedataImpl1 frame = new FramedataImpl1(Opcode.PING);
619+
frame.setFin(true);
620+
sendFrame(frame);
621+
}
622+
616623
@Override
617624
public boolean hasBufferedData() {
618625
return !this.outQueue.isEmpty();

src/main/java/org/java_websocket/client/WebSocketClient.java

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.net.URI;
1010
import java.nio.ByteBuffer;
1111
import java.nio.channels.NotYetConnectedException;
12+
import java.util.Collection;
13+
import java.util.Collections;
1214
import java.util.Map;
1315
import java.util.concurrent.CountDownLatch;
1416

@@ -21,6 +23,7 @@
2123
import org.java_websocket.framing.CloseFrame;
2224
import org.java_websocket.framing.Framedata;
2325
import org.java_websocket.framing.Framedata.Opcode;
26+
import org.java_websocket.framing.FramedataImpl1;
2427
import org.java_websocket.handshake.HandshakeImpl1Client;
2528
import org.java_websocket.handshake.Handshakedata;
2629
import org.java_websocket.handshake.ServerHandshake;
@@ -58,10 +61,7 @@ public abstract class WebSocketClient extends WebSocketAdapter implements Runnab
5861

5962
private int connectTimeout = 0;
6063

61-
/**
62-
* Attribute which allows you to deactivate the Nagle's algorithm
63-
*/
64-
private boolean tcpNoDelay;
64+
6565

6666
/**
6767
* Constructs a WebSocketClient instance and sets it to the connect to the
@@ -104,7 +104,7 @@ public WebSocketClient( URI serverUri , Draft protocolDraft , Map<String,String>
104104
this.draft = protocolDraft;
105105
this.headers = httpHeaders;
106106
this.connectTimeout = connectTimeout;
107-
this.tcpNoDelay = false;
107+
setTcpNoDelay( false );
108108
this.engine = new WebSocketImpl( this, protocolDraft );
109109
}
110110

@@ -133,24 +133,6 @@ public Socket getSocket() {
133133
return socket;
134134
}
135135

136-
/**
137-
* Tests if TCP_NODELAY is enabled.
138-
* @return a boolean indicating whether or not TCP_NODELAY is enabled for new connections.
139-
*/
140-
public boolean isTcpNoDelay() {
141-
return tcpNoDelay;
142-
}
143-
144-
/**
145-
* Setter for tcpNoDelay
146-
*
147-
* Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) for new connections
148-
* @param tcpNoDelay true to enable TCP_NODELAY, false to disable.
149-
*/
150-
public void setTcpNoDelay( boolean tcpNoDelay ) {
151-
this.tcpNoDelay = tcpNoDelay;
152-
}
153-
154136
/**
155137
* Initiates the websocket connection. This method does not block.
156138
*/
@@ -210,14 +192,23 @@ public void send( byte[] data ) throws NotYetConnectedException {
210192
engine.send( data );
211193
}
212194

195+
protected Collection<WebSocket> connections() {
196+
return Collections.singletonList((WebSocket ) engine );
197+
}
198+
199+
200+
public void sendPing() throws NotYetConnectedException {
201+
engine.sendPing( );
202+
}
203+
213204
public void run() {
214205
try {
215206
if( socket == null ) {
216207
socket = new Socket( proxy );
217208
} else if( socket.isClosed() ) {
218209
throw new IOException();
219210
}
220-
socket.setTcpNoDelay( tcpNoDelay );
211+
socket.setTcpNoDelay( isTcpNoDelay() );
221212
if( !socket.isBound() )
222213
socket.connect( new InetSocketAddress( uri.getHost(), getPort() ), connectTimeout );
223214
istream = socket.getInputStream();
@@ -319,6 +310,7 @@ public void onWebsocketMessageFragment( WebSocket conn, Framedata frame ) {
319310
*/
320311
@Override
321312
public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) {
313+
startConnectionLostTimer();
322314
onOpen( (ServerHandshake) handshake );
323315
connectLatch.countDown();
324316
}
@@ -328,6 +320,7 @@ public final void onWebsocketOpen( WebSocket conn, Handshakedata handshake ) {
328320
*/
329321
@Override
330322
public final void onWebsocketClose( WebSocket conn, int code, String reason, boolean remote ) {
323+
stopConnectionLostTimer();
331324
if( writeThread != null )
332325
writeThread.interrupt();
333326
try {

src/main/java/org/java_websocket/server/WebSocketServer.java

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ public abstract class WebSocketServer extends WebSocketAdapter implements Runnab
8888

8989
private WebSocketServerFactory wsf = new DefaultWebSocketServerFactory();
9090

91-
/**
92-
* Attribute which allows you to deactivate the Nagle's algorithm
93-
*/
94-
private boolean tcpNoDelay;
95-
9691
/**
9792
* Creates a WebSocketServer that will attempt to
9893
* listen on port <var>WebSocket.DEFAULT_PORT</var>.
@@ -186,7 +181,7 @@ public WebSocketServer( InetSocketAddress address , int decodercount , List<Draf
186181

187182
this.address = address;
188183
this.connections = connectionscontainer;
189-
tcpNoDelay = false;
184+
setTcpNoDelay(false);
190185
iqueue = new LinkedList<WebSocketImpl>();
191186

192187
decoders = new ArrayList<WebSocketWorker>( decodercount );
@@ -198,24 +193,6 @@ public WebSocketServer( InetSocketAddress address , int decodercount , List<Draf
198193
}
199194
}
200195

201-
/**
202-
* Tests if TCP_NODELAY is enabled.
203-
* @return a boolean indicating whether or not TCP_NODELAY is enabled for new connections.
204-
*/
205-
public boolean isTcpNoDelay() {
206-
return tcpNoDelay;
207-
}
208-
209-
/**
210-
* Setter for tcpNoDelay
211-
*
212-
* Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) for new connections
213-
* @param tcpNoDelay true to enable TCP_NODELAY, false to disable.
214-
*/
215-
public void setTcpNoDelay( boolean tcpNoDelay ) {
216-
this.tcpNoDelay = tcpNoDelay;
217-
}
218-
219196

220197
/**
221198
* Starts the server selectorthread that binds to the currently set port number and
@@ -325,6 +302,7 @@ public void run() {
325302
socket.bind( address );
326303
selector = Selector.open();
327304
server.register( selector, server.validOps() );
305+
startConnectionLostTimer();
328306
onStart();
329307
} catch ( IOException ex ) {
330308
handleFatal( null, ex );
@@ -360,7 +338,8 @@ public void run() {
360338
}
361339
channel.configureBlocking( false );
362340
Socket socket = channel.socket();
363-
socket.setTcpNoDelay( tcpNoDelay );
341+
socket.setTcpNoDelay( isTcpNoDelay() );
342+
socket.setKeepAlive( true );
364343
WebSocketImpl w = wsf.createWebSocket( this, drafts );
365344
w.key = channel.register( selector, SelectionKey.OP_READ, w );
366345
try {
@@ -452,6 +431,7 @@ public void run() {
452431
// should hopefully never occur
453432
handleFatal( null, e );
454433
} finally {
434+
stopConnectionLostTimer();
455435
if( decoders != null ) {
456436
for( WebSocketWorker w : decoders ) {
457437
w.interrupt();

0 commit comments

Comments
 (0)