Application layer keepalives to detect dead TCP connections.

DuplexOutgoingSession flushes its output stream if it's idle for a
transport-defined interval, causing an empty frame to be sent. The TCP
and Tor plugins use a socket timeout equal to twice the idle interval to
detect dead connections.

See bugs #27, #46 and #60.
This commit is contained in:
akwizgran
2014-12-13 12:00:40 +00:00
parent 3a70aa7653
commit d4fa656dbb
19 changed files with 95 additions and 26 deletions

View File

@@ -17,8 +17,9 @@ class LanTcpPlugin extends TcpPlugin {
static final TransportId ID = new TransportId("lan");
LanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
int maxFrameLength, long maxLatency, long pollingInterval) {
super(ioExecutor, callback, maxFrameLength, maxLatency,
int maxFrameLength, long maxLatency, long maxIdleTime,
long pollingInterval) {
super(ioExecutor, callback, maxFrameLength, maxLatency, maxIdleTime,
pollingInterval);
}

View File

@@ -11,6 +11,7 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_FRAME_LENGTH = 1024;
private static final long MAX_LATENCY = 60 * 1000; // 1 minute
private static final long MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final long POLLING_INTERVAL = 60 * 1000; // 1 minute
private final Executor ioExecutor;
@@ -25,6 +26,6 @@ public class LanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
return new LanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH,
MAX_LATENCY, POLLING_INTERVAL);
MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL);
}
}

View File

@@ -37,8 +37,8 @@ abstract class TcpPlugin implements DuplexPlugin {
protected final Executor ioExecutor;
protected final DuplexPluginCallback callback;
protected final int maxFrameLength;
protected final long maxLatency, pollingInterval;
protected final int maxFrameLength, socketTimeout;
protected final long maxLatency, maxIdleTime, pollingInterval;
protected volatile boolean running = false;
protected volatile ServerSocket socket = null;
@@ -53,12 +53,17 @@ abstract class TcpPlugin implements DuplexPlugin {
protected abstract boolean isConnectable(InetSocketAddress remote);
protected TcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
int maxFrameLength, long maxLatency, long pollingInterval) {
int maxFrameLength, long maxLatency, long maxIdleTime,
long pollingInterval) {
this.ioExecutor = ioExecutor;
this.callback = callback;
this.maxFrameLength = maxFrameLength;
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
this.pollingInterval = pollingInterval;
if(2 * maxIdleTime > Integer.MAX_VALUE)
socketTimeout = Integer.MAX_VALUE;
else socketTimeout = (int) (2 * maxIdleTime);
}
public int getMaxFrameLength() {
@@ -69,6 +74,10 @@ abstract class TcpPlugin implements DuplexPlugin {
return maxLatency;
}
public long getMaxIdleTime() {
return maxIdleTime;
}
public boolean start() {
running = true;
bind();
@@ -136,6 +145,7 @@ abstract class TcpPlugin implements DuplexPlugin {
Socket s;
try {
s = socket.accept();
s.setSoTimeout(socketTimeout);
} catch(IOException e) {
// This is expected when the socket is closed
if(LOG.isLoggable(INFO)) LOG.info(e.toString());
@@ -195,6 +205,7 @@ abstract class TcpPlugin implements DuplexPlugin {
try {
if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote);
s.connect(remote);
s.setSoTimeout(socketTimeout);
if(LOG.isLoggable(INFO)) LOG.info("Connected to " + remote);
return new TcpTransportConnection(this, s);
} catch(IOException e) {

View File

@@ -21,9 +21,9 @@ class WanTcpPlugin extends TcpPlugin {
private volatile MappingResult mappingResult;
WanTcpPlugin(Executor ioExecutor, DuplexPluginCallback callback,
int maxFrameLength, long maxLatency, long pollingInterval,
PortMapper portMapper) {
super(ioExecutor, callback, maxFrameLength, maxLatency,
int maxFrameLength, long maxLatency, long maxIdleTime,
long pollingInterval, PortMapper portMapper) {
super(ioExecutor, callback, maxFrameLength, maxLatency, maxIdleTime,
pollingInterval);
this.portMapper = portMapper;
}

View File

@@ -12,6 +12,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
private static final int MAX_FRAME_LENGTH = 1024;
private static final long MAX_LATENCY = 60 * 1000; // 1 minute
private static final long MAX_IDLE_TIME = 30 * 1000; // 30 seconds
private static final long POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes
private final Executor ioExecutor;
@@ -29,7 +30,7 @@ public class WanTcpPluginFactory implements DuplexPluginFactory {
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
return new WanTcpPlugin(ioExecutor, callback, MAX_FRAME_LENGTH,
MAX_LATENCY, POLLING_INTERVAL,
MAX_LATENCY, MAX_IDLE_TIME, POLLING_INTERVAL,
new PortMapperImpl(shutdownManager));
}
}