73

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?

Christian P.
  • 4,784
  • 7
  • 53
  • 70

9 Answers9

69

These are two quick fixes you can use, given the code and what I presume is your design:

1. Thread.Abort()

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.

2. TcpListener.Pending()

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();
}

Asynchronous Rewrite

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:

  1. to bootstrap everything;
  2. to be the target to call EndAcceptTcpClient(), kick off the TcpClient to it's own thread and then call itself again;
  3. to process the client request and close it when finished.

(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.)

Massimiliano Kraus
  • 3,638
  • 5
  • 27
  • 47
Peter Oehlert
  • 16,368
  • 6
  • 44
  • 48
  • 3
    I just read your answer, but for me it´s not clear how to implement correctly what you described. Can you provide some code to clarify it. Thanks – Jehof Feb 21 '11 at 13:38
  • @Jehof: Is it the asynchronous part that you want clarification on? Can you be more specific about what is not clear to you? – Peter Oehlert Mar 08 '11 at 21:55
  • 5
    In my tests it would seem that Thread.Abort doesn't work as expected when the thread is blocked waiting on a connection see my question here: http://stackoverflow.com/questions/5778267/acceptsocket-does-not-respect-a-thread-abort-request – Ralph Shillington Apr 25 '11 at 12:21
  • 1
    Are you sure it is possible to Close the client while the clientThread is using the client.GetStream()? If I leave in the client.Close() call, I can't create a StreamReader on the NetworkStream in the HandleConnection method. – comecme May 17 '11 at 20:21
  • 5
    Why is this marked as solution ? First 3 points seem to be wrong for one reason or another and last one could use some code. – IvanP May 19 '13 at 15:52
  • 1
    TcpClient doesn't implement `IDisposable`. This answer has several bad points to it and shouldn't be accepted. – xxbbcc Nov 20 '14 at 17:19
  • 1
    TCPClient does implement IDisposable but you have to recast it. TCPListener does not. (.NET 4.0, may have been different when this was originally answered.) – Denise Skidmore Dec 19 '14 at 19:06
  • 1
    Looks like 47 people never tried Thread.Abort() in this context. – 15ee8f99-57ff-4f92-890c-b56153 Dec 22 '15 at 18:49
  • 1
    @xxbbcc [`TcpClient` does implement `IDisposable`](https://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient(v=vs.110).aspx) though I agree that this answer really needs some work. – jrh Oct 25 '17 at 14:11
  • What worked for me is to close/Dispose TcpListener.Server socket which is disposable. – Michal Dobrodenka Feb 18 '21 at 11:16
50

listener.Server.Close() from another thread breaks the blocking call.

A blocking operation was interrupted by a call to WSACancelBlockingCall
Jason Plank
  • 2,336
  • 5
  • 31
  • 40
zproxy
  • 3,509
  • 3
  • 39
  • 45
  • While I like Peter Oehlert's answer for completeness and best practice, I have a simple task didn't justify the complexity of a full async rewrite, and I found that Thread.Abort() didn't work for me. This however did the trick for me very quickly and effectively. – Matt Connolly May 27 '11 at 05:23
  • 2
    I prefer this answer, as if you're going to rewrite it you should probably use WCF, it addresses so many issues I had with TCP/IP sockets in .net – NibblyPig Dec 12 '11 at 14:50
  • You could even add a some lines. In your SocketException catch handler. `if ((e.SocketErrorCode == SocketError.Interrupted)) Console.WriteLine("A blocking listen has been cancelled");` So you are sure you made a terminate while it was waiting for a client to connect. – WagoL Dec 09 '15 at 08:57
  • 1
    Why not doing *listener.Stop();* ? – John Aug 22 '16 at 10:02
  • 1
    I agree with @John ; why call `listener.Server.Close()` instead of `listener.Stop()`? If you look at the [Reference Source for `TcpListener.Stop()`](https://referencesource.microsoft.com/#System/net/System/Net/Sockets/TCPListener.cs,204), does the equivalent of `listener.Server.Close()` for you (among other things like clearing out old connection requests). – jrh Oct 25 '17 at 14:23
3

See my answer here https://stackoverflow.com/a/17816763/2548170 TcpListener.Pending() is not good solution

Community
  • 1
  • 1
Andriy Vandych
  • 144
  • 1
  • 6
3

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.

Mike Scott
  • 12,274
  • 8
  • 40
  • 53
  • Can you provide an example? – omJohn8372 Jul 15 '20 at 22:56
  • @omJohn8372 there's nothing much more to add to what I wrote. You have a flag somewhere that your callback method checks. You provide that callback when calling BeginAcceptTcpClient. For an example of a callback, see the [Microsoft docs](https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener.beginaccepttcpclient?view=netframework-4.8). So when you want to stop the listener, set the flag and Close() on it. In the callback, you first check if the flag is set and if so, you know it's been closed and exit. – Mike Scott Aug 09 '20 at 00:45
3

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.

Dzmitry Huba
  • 4,493
  • 20
  • 19
  • The documentation specifically states that closing TcpClient does NOT close the underlying stream. – Christian P. Dec 13 '08 at 21:20
  • Yes, the documentation says so, but implementation closes the stream... Check it TcpClient tcpClient = new TcpClient(); tcpClient.Connect("www.google.com", 80); NetworkStream networkStream = tcpClient.GetStream(); tcpClient.Close(); byte[] bytes = new byte[1024]; networkStream.Read(bytes, 0, 1024); – Dzmitry Huba Dec 15 '08 at 21:41
  • There is no `TcpClient.Stop()` method. – Qwertie Apr 26 '11 at 21:12
1

Just to add even more reason to use the asynchronous approach, I'm pretty sure Thread.Abort won't work because the call is blocked in the OS level TCP stack.

Also... if you are calling BeginAcceptTCPClient in the callback to listen for every connection but the first, be careful to make sure that the thread that executed the initial BeginAccept doesn't terminate or else the listener will automatically get disposed by the framework. I suppose that's a feature, but in practice it's very annoying. In desktop apps it's not usually a problem, but on the web you might want to use the thread pool since those threads don't ever really terminate.

Eric Nicholson
  • 4,053
  • 1
  • 28
  • 29
0

Already mentioned above, use BeginAcceptTcpClient instead, it's much easier to manage asynchronously.

Here is some sample code :

        ServerSocket = new TcpListener(endpoint);
        try
        {
            ServerSocket.Start();
            ServerSocket.BeginAcceptTcpClient(OnClientConnect, null);
            ServerStarted = true;

            Console.WriteLine("Server has successfully started.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Server was unable to start : {ex.Message}");
            return false;
        }
Peter Suwara
  • 781
  • 10
  • 16
-1

Probably best to use the asynchronous BeginAcceptTcpClient function. Then you can just call Stop() on the listener as it won't be blocking.

Paul
  • 3,125
  • 2
  • 24
  • 21
-2

Some changes to make the Peter Oehlert anwer perfect. Because before 500 miliseconds the listener bloking again. To correct 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
       }
       else // Enter here only if have pending clients
       {
          TcpClient client = listener.AcceptTcpClient();
          Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
          clientThread.Start(client.GetStream());
          client.Close();
       }
   }
  • 5
    Elimar, the only change I see between this code and my own is the else block. Because continue will basically jump to the top of the while loop the else isn't strictly necessary. It could be argued on the merit of cleanliness or coding style but is not required. Am I missing something else? – Peter Oehlert Jul 23 '10 at 01:25