TcpListener: Accept queued connections after Stop()
When Start() is called on an instance of TcpListener, it begins to queue pending connections, which my application can dequeue (one-by-one) by calling AcceptTcpClient(). Additionally, and this is important, the MSDN documentation for the TcpListener class states that when Stop() is called, it does not do anything with any already-queued but unaccepted connections. I cannot see any way to do the following:
- Call Stop() on the instance of TcpListener so that it will stop listening for and queueing future connections.
- Allow me to AcceptTcpClient() on the instance so that I can handle the internally queued but as-of-yet unaccepted connections before terminating my executable.
- Or, forcefully close the queued, unaccepted connections within my TcpListener instance so that they do not remain open and cause my executable's port to remain bound until the client closes its connection.
I have run into an issue where a very busy executable is shutdown, which correctly causes my code to call Stop() on its TcpListener and cleanly exit the while loop that checked for Pending() connections. However, that TcpListener had queued, unaccepted connections which caused the port to remain bound even after the executable terminated - making it impossible to restart the executable since its port is still bound.
If this is possible using the System.Net.Sockets.Socket class, I'd be happy to switch over, but I don't see anything terribly promising there either.
All Replies
- The fact that you cannot bind to the same port after restarting the app has to do with the TCP state machine. TCP will put the port in a TIME_WAIT state, so that will prevent it being used again for a certain period of time (which is customizable).
One way you can force a bind is to set the SocketOption.ReuseAddress option on the underlying socket. That should allow you to bind to the existing port.
feroze
--
My blog
Instruction on how to create a tracelog with your System.Net application
The fact that you cannot bind to the same port after restarting the app has to do with the TCP state machine. TCP will put the port in a TIME_WAIT state, so that will prevent it being used again for a certain period of time (which is customizable).
One way you can force a bind is to set the SocketOption.ReuseAddress option on the underlying socket. That should allow you to bind to the existing port.
feroze
--
My blog
Instruction on how to create a tracelog with your System.Net application
Setting this option allows my executable to bind on the port, as you said it would. The problem is that now netstat shows that the System process is ALSO bound to the port, and connection attempts to my executable still fail.- I am curious about the statement where you said that the docs say that the queued connections remain around after calling stop...
According to the docs for TcpListener.stop (http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop.aspx)
Stop closes the listener. Any unaccepted connection requests in the queue will be lost. Remote hosts waiting for a connection to be accepted will throw a SocketException . You are responsible for closing your accepted connections separately.
Notes to Callers:
Note: This member outputs trace information when you enable network tracing in your application. For more information, see Network Tracing .
The Stop method also closes the underlying Socket , and creates a new Socket for the TcpListener . If you set any properties on the underlying Socket prior to calling the Stop method, those properties will not carry over to the new Socket .
feroze
--
My blog
Instruction on how to create a tracelog with your System.Net application
- That's true, but look at the documentation for the TcpListener class in general and you'll read:
Note: The Stop method does not close any accepted connections. You are responsible for closing these separately.
However, I found the issue to the problem. My issue was that the server - in the original case above - was the parent/server of the clients attempting to talk to it. That is, the parent/server had started them all using the System.Diagnostics.Process class.
It turns out that the implementation of that class starts processes and sets the "allow children to inherit handles" to true, which means that when the parent/server stopped, all the children had references to the parent/server's socket handles, which was causing the parent/server's ports to remain bound. I swapped out System.Diagnostics.Process for P/Invoking CreateProcess in kernel32.dll, and set the bInheritHandles bool to false. After that, I was able to start & stop the parent/server process successfully.
Please reference this thread , which is what tipped me off.- Proposed As Answer byFeroze Daud Friday, October 30, 2009 3:18 PM


