5

I have this code in a separate thread (Task) that runs first when the application starts and should not end until the application closes:

TcpListener tcpListener = new TcpListener(IPAddress.Any, port);

tcpListener.Start();

while (true)
{
    TcpClient client = tcpListener.AcceptTcpClient();

    Task.Factory.StartNew(HandleClientCommunication, client);
}

In this case is it necessary to call tcpListener.Stop()? This thread runs for the entire duration of the application and if I did need to call it, where would I do so? The listener is local to this thread. Instead of having a while (true) loop would I have a while (appRunning) loop and set appRunning to false in the FormClosing event? Then after the while loop I could call tcpListener.Stop().

However, is it even necessary to call TcpListener.Stop() because the application has already closed at that point and since I'm using Tasks the process ends as well?

John Smith
  • 8,567
  • 13
  • 51
  • 74
  • possible duplicate of [Proper way to stop TcpListener](http://stackoverflow.com/questions/365370/proper-way-to-stop-tcplistener) – Kiril Sep 19 '11 at 19:35
  • There are a few reasons why developers find it necessary to stop threads explictly on .NET app close - when they see that other devleopers do it, because it feels 'clean' or because their work threads read/write directly to form/component/whatever data that is deallocated during app lose as the forms are closed, (so generating AV/segfault etc.). Your listener thread, in itself, does not seem to do anything to require explicit stopping, so let the OS do it when the main thread closes and calls ExitProcess(). – Martin James Sep 19 '11 at 19:42
  • @Jim: If you would like to provide some example code on how to implement a cancellation token in this situation to solve this problem I'd gladly accept that as the answer :) – John Smith Sep 19 '11 at 20:40
  • @Jim Mischel - if the OP changes it substantially, then, yes, maybe it might need stopping in some other app. maybe another app needs it stopping before app close, but that's another app. Also, maybe I misunderstand the cancellation mechanism - what happens inside the blocking AcceptTcpClient() call when cancellation is requested? Returns immediately with an error or exception? I guess I shouldlook it up or try it. – Martin James Sep 19 '11 at 20:42
  • 1
    OK, the only way I can see to explicitly stop this thread using the cancellation mechanism is to register a cancellation callback that unblocks the AcceptTcpClient() call by opening a client connection on 'localhost', (assuming 127.0.0.1 is in the set of bindings), so causing the AcceptTcpClient() to return with an actual connection. The listener can then check the token.IsCancellationRequested and so realise it has to terminate. This is so much messier than letting the OS clean up that I would not go near it unless absolutely necessary. The OS is very, very good at stopping threads:) – Martin James Sep 20 '11 at 09:34
  • .. or close the listening socket from the cancellation callback, so causing the AcceptTcpClient() to throw an exception in the listening thread - that should work too, though some developers don't like to do such things to their TCP stacks, even though it's worked fine on all Windows since W95. – Martin James Sep 20 '11 at 09:48

1 Answers1

5

Try something like that:

public class Listener
{
    private readonly TcpListener m_Listener = new TcpListener(IPAddress.Any, IPEndPoint.MinPort);
    private CancellationTokenSource m_Cts;
    private Thread m_Thread;
    private readonly object m_SyncObject = new object();

    public void Start()
    {
        lock (m_SyncObject){
            if (m_Thread == null || !m_Thread.IsAlive){
                m_Cts = new CancellationTokenSource();
                m_Thread = new Thread(() => Listen(m_Cts.Token))
                    {
                        IsBackground = true
                    };
                m_Thread.Start();
            }
        }
    }

    public void Stop()
    {
        lock (m_SyncObject){
            m_Cts.Cancel();
            m_Listener.Stop();
        }
    }

    private void Listen(CancellationToken token)
    {
        m_Listener.Start();
        while (!token.IsCancellationRequested)
        {
            try{
                var socket = m_Listener.AcceptSocket();
                //do something with socket
            }
            catch (SocketException){                    
            }
        }
    }
}

Approach with TcpListener.Pending() is not so good because you must use Thread.Sleep(miliseconds) or something like that - there will be some delay between clients accept(miliseconds in sleep), thats bad.

Andriy Vandych
  • 144
  • 1
  • 6
  • 2
    The problem with this solution is that the AcceptSocket method is blocking. So you have no way to gracefully stop your listening thread while it is waiting for an incoming connection. – C-F Aug 12 '16 at 19:24