Skip to content

Commit 08dd1d8

Browse files
authored
Merge pull request TooTallNate#480 from marci4/lostconnection
Lostconnection
2 parents 1a7bbdd + 047a3c1 commit 08dd1d8

6 files changed

Lines changed: 213 additions & 67 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package org.java_websocket;
2+
3+
import org.java_websocket.framing.CloseFrame;
4+
5+
import java.util.Collection;
6+
import java.util.Timer;
7+
import java.util.TimerTask;
8+
9+
10+
/**
11+
* Base class for additional implementations for the server as well as the client
12+
*/
13+
public abstract class AbstractWebSocket extends WebSocketAdapter {
14+
15+
/**
16+
* Attribute which allows you to deactivate the Nagle's algorithm
17+
*/
18+
private boolean tcpNoDelay;
19+
20+
/**
21+
* Attribute for a timer allowing to check for lost connections
22+
*/
23+
private Timer connectionLostTimer;
24+
/**
25+
* Attribute for a timertask allowing to check for lost connections
26+
*/
27+
private TimerTask connectionLostTimerTask;
28+
29+
/**
30+
* Attribute for the lost connection check interval
31+
*/
32+
private int connectionLostTimeout = 60;
33+
34+
/**
35+
* Get the interval checking for lost connections
36+
* Default is 60 seconds
37+
* @return the interval
38+
*/
39+
public int getConnectionLostTimeout() {
40+
return connectionLostTimeout;
41+
}
42+
43+
/**
44+
* Setter for the interval checking for lost connections
45+
* A value >= 0 results in the check to be deactivated
46+
*
47+
* @param connectionLostTimeout the interval in seconds
48+
*/
49+
public void setConnectionLostTimeout( int connectionLostTimeout ) {
50+
this.connectionLostTimeout = connectionLostTimeout;
51+
if (this.connectionLostTimeout <= 0) {
52+
stopConnectionLostTimer();
53+
} else {
54+
startConnectionLostTimer();
55+
}
56+
}
57+
58+
/**
59+
* Stop the connection lost timer
60+
*/
61+
protected void stopConnectionLostTimer() {
62+
if (connectionLostTimer != null ||connectionLostTimerTask != null) {
63+
if( WebSocketImpl.DEBUG )
64+
System.out.println( "Connection lost timer stoped" );
65+
cancelConnectionLostTimer();
66+
}
67+
}
68+
/**
69+
* Start the connection lost timer
70+
*/
71+
protected void startConnectionLostTimer() {
72+
if (this.connectionLostTimeout <= 0) {
73+
if (WebSocketImpl.DEBUG)
74+
System.out.println("Connection lost timer deactivated");
75+
return;
76+
}
77+
if (WebSocketImpl.DEBUG)
78+
System.out.println("Connection lost timer started");
79+
cancelConnectionLostTimer();
80+
connectionLostTimer = new Timer();
81+
connectionLostTimerTask = new TimerTask() {
82+
@Override
83+
public void run() {
84+
Collection<WebSocket> con = connections();
85+
synchronized ( con ) {
86+
long current = (System.currentTimeMillis()-(connectionLostTimeout * 1500));
87+
for( WebSocket conn : con ) {
88+
if (conn instanceof WebSocketImpl) {
89+
if( ((WebSocketImpl)conn).getLastPong() < current ) {
90+
if (WebSocketImpl.DEBUG)
91+
System.out.println("Closing connection due to no pong received: " + conn.toString());
92+
conn.close( CloseFrame.ABNORMAL_CLOSE );
93+
} else {
94+
conn.sendPing();
95+
}
96+
}
97+
}
98+
}
99+
}
100+
};
101+
connectionLostTimer.scheduleAtFixedRate( connectionLostTimerTask,connectionLostTimeout * 1000, connectionLostTimeout * 1000 );
102+
}
103+
104+
/**
105+
* Getter to get all the currently available connections
106+
* @return the currently available connections
107+
*/
108+
protected abstract Collection<WebSocket> connections();
109+
110+
/**
111+
* Cancel any running timer for the connection lost detection
112+
*/
113+
private void cancelConnectionLostTimer() {
114+
if( connectionLostTimer != null ) {
115+
connectionLostTimer.cancel();
116+
connectionLostTimer = null;
117+
}
118+
if( connectionLostTimerTask != null ) {
119+
connectionLostTimerTask.cancel();
120+
connectionLostTimerTask = null;
121+
}
122+
}
123+
124+
/**
125+
* Tests if TCP_NODELAY is enabled.
126+
*
127+
* @return a boolean indicating whether or not TCP_NODELAY is enabled for new connections.
128+
*/
129+
public boolean isTcpNoDelay() {
130+
return tcpNoDelay;
131+
}
132+
133+
/**
134+
* Setter for tcpNoDelay
135+
* <p>
136+
* Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) for new connections
137+
*
138+
* @param tcpNoDelay true to enable TCP_NODELAY, false to disable.
139+
*/
140+
public void setTcpNoDelay( boolean tcpNoDelay ) {
141+
this.tcpNoDelay = tcpNoDelay;
142+
}
143+
144+
}

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

Lines changed: 11 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
/**
@@ -30,6 +31,11 @@ public enum READYSTATE {
3031
*/
3132
public static final int DEFAULT_PORT = 80;
3233

34+
/**
35+
* The default wss port of WebSockets, as defined in the spec. If the nullary
36+
* constructor is used, DEFAULT_WSS_PORT will be the port the WebSocketServer
37+
* is binded to. Note that ports under 1024 usually require root permissions.
38+
*/
3339
public static final int DEFAULT_WSS_PORT = 443;
3440

3541
/**
@@ -90,6 +96,11 @@ public enum READYSTATE {
9096
*/
9197
public abstract void sendFrame( Framedata framedata );
9298

99+
/**
100+
* Send a ping to the other end
101+
* @throws NotYetConnectedException websocket is not yet connected
102+
*/
103+
public void sendPing() throws NotYetConnectedException;
93104
/**
94105
* Allows to send continuous/fragmented frames conveniently. <br>
95106
* 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: 12 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,6 +12,11 @@
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
**/
@@ -75,25 +79,25 @@ public void onWebsocketPong( WebSocket conn, Framedata f ) {
7579
/**
7680
* Gets the XML string that should be returned if a client requests a Flash
7781
* security policy.
78-
*
82+
* <p>
7983
* The default implementation allows access from all remote domains, but
8084
* only on the port that this WebSocketServer is listening on.
81-
*
85+
* <p>
8286
* This is specifically implemented for gitime's WebSocket client for Flash:
8387
* http://github.com/gimite/web-socket-js
84-
*
88+
*
8589
* @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.
90+
* not include the null char at the end, it is appended automatically.
8791
* @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.
8892
*/
8993
@Override
9094
public String getFlashPolicy( WebSocket conn ) throws InvalidDataException {
9195
InetSocketAddress adr = conn.getLocalSocketAddress();
92-
if(null == adr){
96+
if( null == adr ) {
9397
throw new InvalidHandshakeException( "socket not bound" );
9498
}
9599

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

99103
}

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

Lines changed: 21 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;
@@ -93,6 +94,11 @@ public class WebSocketImpl implements WebSocket {
9394

9495
private String resourceDescriptor = null;
9596

97+
/**
98+
* Attribute, when the last pong was recieved
99+
*/
100+
private long lastPong = System.currentTimeMillis();
101+
96102
/**
97103
* Creates a websocket with server role
98104
*
@@ -347,6 +353,7 @@ private void decodeFrames( ByteBuffer socketBuffer ) {
347353
wsl.onWebsocketPing( this, f );
348354
continue;
349355
} else if( curop == Opcode.PONG ) {
356+
lastPong = System.currentTimeMillis();
350357
wsl.onWebsocketPong( this, f );
351358
continue;
352359
} else if( !fin || curop == Opcode.CONTINUOUS ) {
@@ -612,6 +619,12 @@ public void sendFrame( Framedata framedata ) {
612619
write( draft.createBinaryFrame( framedata ) );
613620
}
614621

622+
public void sendPing() throws NotYetConnectedException {
623+
FramedataImpl1 frame = new FramedataImpl1(Opcode.PING);
624+
frame.setFin(true);
625+
sendFrame(frame);
626+
}
627+
615628
@Override
616629
public boolean hasBufferedData() {
617630
return !this.outQueue.isEmpty();
@@ -757,4 +770,12 @@ public void close() {
757770
public String getResourceDescriptor() {
758771
return resourceDescriptor;
759772
}
773+
774+
/**
775+
* Getter for the last pong recieved
776+
* @return the timestamp for the last recieved pong
777+
*/
778+
long getLastPong() {
779+
return lastPong;
780+
}
760781
}

0 commit comments

Comments
 (0)