diff --git a/src/dorkbox/network/connection/ConnectionImpl.java b/src/dorkbox/network/connection/ConnectionImpl.java index 61117ef6..26c90a28 100644 --- a/src/dorkbox/network/connection/ConnectionImpl.java +++ b/src/dorkbox/network/connection/ConnectionImpl.java @@ -690,6 +690,11 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements CryptoConne closeLatch.countDown(); } + final void + forceClose() { + this.channelWrapper.close(this, this.sessionManager, true); + } + /** * Closes the connection, but does not remove any listeners */ @@ -757,7 +762,7 @@ class ConnectionImpl extends ChannelInboundHandlerAdapter implements CryptoConne synchronized (this.channelIsClosed) { if (!this.channelIsClosed.get()) { // this will have netty call "channelInactive()" - this.channelWrapper.close(this, this.sessionManager); + this.channelWrapper.close(this, this.sessionManager, false); // want to wait for the "channelInactive()" method to FINISH ALL TYPES before allowing our current thread to continue! try { diff --git a/src/dorkbox/network/connection/wrapper/ChannelLocalWrapper.java b/src/dorkbox/network/connection/wrapper/ChannelLocalWrapper.java index 98b83bcf..5d15d92d 100644 --- a/src/dorkbox/network/connection/wrapper/ChannelLocalWrapper.java +++ b/src/dorkbox/network/connection/wrapper/ChannelLocalWrapper.java @@ -122,7 +122,7 @@ class ChannelLocalWrapper implements ChannelWrapper, ConnectionPoint { @Override public - void close(ConnectionImpl connection, ISessionManager sessionManager) { + void close(ConnectionImpl connection, ISessionManager sessionManager, boolean hintedClose) { long maxShutdownWaitTimeInMilliSeconds = EndPoint.maxShutdownWaitTimeInMilliSeconds; this.shouldFlush.set(false); diff --git a/src/dorkbox/network/connection/wrapper/ChannelNetwork.java b/src/dorkbox/network/connection/wrapper/ChannelNetwork.java index 30d14ea9..be265628 100644 --- a/src/dorkbox/network/connection/wrapper/ChannelNetwork.java +++ b/src/dorkbox/network/connection/wrapper/ChannelNetwork.java @@ -15,6 +15,7 @@ */ package dorkbox.network.connection.wrapper; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import dorkbox.network.connection.ConnectionPoint; @@ -69,12 +70,26 @@ class ChannelNetwork implements ConnectionPoint { return channel.eventLoop().newPromise(); } - public - void close(long maxShutdownWaitTimeInMilliSeconds) { + void close(final int delay, final long maxShutdownWaitTimeInMilliSeconds) { shouldFlush.set(false); if (channel.isActive()) { - channel.close() - .awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds); + if (delay > 0) { + // for UDP, we send a hint to the other connection that we should close. While not always 100% successful, this helps + // clean up connections on the remote end. So we want to wait a short amount of time for this to be successful + channel.eventLoop() + .schedule(new Runnable() { + @Override + public + void run() { + channel.close() + .awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds); + } + }, delay, TimeUnit.MILLISECONDS); + } + else { + channel.close() + .awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds); + } } } } diff --git a/src/dorkbox/network/connection/wrapper/ChannelNetworkWrapper.java b/src/dorkbox/network/connection/wrapper/ChannelNetworkWrapper.java index ed0d45b6..effe011b 100644 --- a/src/dorkbox/network/connection/wrapper/ChannelNetworkWrapper.java +++ b/src/dorkbox/network/connection/wrapper/ChannelNetworkWrapper.java @@ -154,24 +154,29 @@ class ChannelNetworkWrapper implements ChannelWrapper { @Override public - void close(final ConnectionImpl connection, final ISessionManager sessionManager) { + void close(final ConnectionImpl connection, final ISessionManager sessionManager, boolean hintedClose) { long maxShutdownWaitTimeInMilliSeconds = EndPoint.maxShutdownWaitTimeInMilliSeconds; if (this.tcp != null) { - this.tcp.close(maxShutdownWaitTimeInMilliSeconds); + this.tcp.close(0, maxShutdownWaitTimeInMilliSeconds); } if (this.udp != null) { - // send a hint to the other connection that we should close. While not always 100% successful, this helps clean up connections - // on the remote end - try { - this.udp.write(new DatagramCloseMessage()); - this.udp.flush(); - Thread.yield(); - } catch (Exception e) { - e.printStackTrace(); + if (hintedClose) { + // we already hinted that we should close this channel... don't do it again! + this.udp.close(0, maxShutdownWaitTimeInMilliSeconds); + } + else { + // send a hint to the other connection that we should close. While not always 100% successful, this helps clean up connections + // on the remote end + try { + this.udp.write(new DatagramCloseMessage()); + this.udp.flush(); + } catch (Exception e) { + e.printStackTrace(); + } + this.udp.close(200, maxShutdownWaitTimeInMilliSeconds); } - this.udp.close(maxShutdownWaitTimeInMilliSeconds); } // we need to yield the thread here, so that the socket has a chance to close diff --git a/src/dorkbox/network/connection/wrapper/ChannelWrapper.java b/src/dorkbox/network/connection/wrapper/ChannelWrapper.java index 6c27c5ba..c18fb5b3 100644 --- a/src/dorkbox/network/connection/wrapper/ChannelWrapper.java +++ b/src/dorkbox/network/connection/wrapper/ChannelWrapper.java @@ -53,7 +53,7 @@ interface ChannelWrapper { */ String getRemoteHost(); - void close(ConnectionImpl connection, ISessionManager sessionManager); + void close(ConnectionImpl connection, ISessionManager sessionManager, boolean hintedClose); int id();