I am currently using TcpListener to address incoming connections, each of which are given a thread for handling the communication and then shutdown that single connection. Code looks as follows:
TcpListener listener = new TcpListener(IPAddress.Any, Port);
System.Console.WriteLine("Server Initialized, listening for incoming connections");
listener.Start();
while (listen)
{
// Step 0: Client connection
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
The listen
variable is a boolean that is a field on the class. Now, when the program shuts down I want it to stop listening for clients. Setting listen to false
will prevent it from taking on more connections, but since AcceptTcpClient
is a blocking call, it will at minimum take the next client and THEN exit. Is there any way to force it to simply break out and stop, right then and there? What effect does calling listener.Stop() have while the other blocking call is running?
These are two quick fixes you can use, given the code and what I presume is your design:
If you have started this TcpListener
thread from another, you can simply call Abort()
on the thread, which will cause a ThreadAbortException
within the blocking call and walk up the stack.
The second low cost fix is to use the listener.Pending()
method to implement a polling model. You then use a Thread.Sleep()
to wait before seeing if a new connection is pending. Once you have a pending connection, you call AcceptTcpClient()
and that releases the pending connection. The code would look something like this:
while (listen) {
// Step 0: Client connection
if (!listener.Pending()) {
Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
continue; // skip to next iteration of loop
}
TcpClient client = listener.AcceptTcpClient();
Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
clientThread.Start(client.GetStream());
client.Close();
}
However, you should really move to a non-blocking methodology for your application. Under the covers the framework will use overlapped I/O and I/O completion ports to implement non-blocking I/O from your asynchronous calls. It's not terribly difficult either, it just requires thinking about your code a little differently.
Basically you would start your code with the BeginAcceptTcpClient()
method and keep track of the IAsyncResult
that you are returned. You point that at a method whose responsible for getting the TcpClient
and passing it off NOT to a new thread but to a thread off of the ThreadPool.QueueUserWorkerItem
, so you're not spinning up and closing a new thread for each client request (Note: you may need to use your own thread pool if you have particularly long lived requests, because the thread pool is shared and if you monopolize all the threads other parts of your application implemented by the system may be starved). Once the listener method has kicked off your new TcpClient
to its own ThreadPool
request, it calls BeginAcceptTcpClient()
again and points the delegate back at itself.
Effectively you're just breaking up your current method into 3 different methods that will then get called by the various parts:
EndAcceptTcpClient()
, kick off the TcpClient
to it's own thread and then call itself again;(Note: you should enclose your TcpClient
call in a using(){}
block to ensure that TcpClient.Dispose()
or TcpClient.Close()
methods are called even in the event of an exception. Alternately you can put this in the finally
block of a try {} finally {}
block.)
listener.Server.Close()
from another thread breaks the blocking call.
A blocking operation was interrupted by a call to WSACancelBlockingCall
Don't use a loop. Instead, call BeginAcceptTcpClient() without a loop. In the callback, just issue another call to BeginAcceptTcpClient(), if your listen flag is still set.
To stop the listener, since you've not blocked, your code can just call Close() on it.
Sockets provide powerful asynchronous capabilities. Take a look at Using an Asynchronous Server Socket
Here are couple of notes on the code.
Using manually created threads in this case may be an overhead.
The code below is subject to race conditions - TcpClient.Close() closes network stream you get through TcpClient.GetStream(). Consider closing client where you can definitely say that it is no longer needed.
clientThread.Start(client.GetStream());
client.Close();
TcpClient.Stop() closes underlying socket. TcpCliet.AcceptTcpClient() uses Socket.Accept() method on underlying socket which will throw SocketException once it is closed. You can call it from a different thread.
Anyway I recommend asynchronous sockets.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With