我需要调用TcpListener.Stop()吗?

5

我有一段代码在一个单独的线程(任务)中运行,这个线程会在应用程序启动时首先运行,并且直到应用程序关闭才结束:

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

tcpListener.Start();

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

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

在这种情况下,是否需要调用 tcpListener.Stop()?这个线程在整个应用程序运行期间都在运行,如果我确实需要调用它,那么我应该在哪里调用呢?监听器是本地线程的。我可以使用 while (appRunning) 循环而不是 while (true) 循环,并在 FormClosing 事件中将 appRunning 设置为 false 吗?然后在 while 循环之后,我可以调用 tcpListener.Stop()
然而,在这个时候是否有必要调用 TcpListener.Stop() 呢?因为应用程序已经关闭了,而且由于我正在使用任务,进程也结束了。

可能是停止TcpListener的正确方式的重复问题。 - Kiril
有几个原因是为什么开发人员在.NET应用程序关闭时需要显式停止线程 - 当他们看到其他开发人员这样做时,因为它感觉“干净”,或者因为他们的工作线程直接读/写到在应用程序关闭时被释放的表单/组件/任何数据(因此生成AV / segfault等)。您的侦听器线程本身似乎没有做任何需要显式停止的事情,因此让操作系统在主线程关闭并调用ExitProcess()时执行该操作。 - Martin James
@Jim:如果您能提供一些示例代码来实现取消令牌以解决这个问题,我将非常乐意接受它作为答案 :) - John Smith
@Jim Mischel - 如果原始问题(OP)进行了大幅更改,那么可能需要在其他应用程序中停止它。也许另一个应用程序在关闭之前需要停止它,但那是另一个应用程序的事情。另外,也许我对取消机制有所误解 - 当请求取消时,在阻塞的AcceptTcpClient()调用内部会发生什么?立即返回错误或异常吗?我猜我应该查一下或者试一下。 - Martin James
1
好的,我唯一能想到显式停止该线程的方法是注册一个取消回调函数,该函数通过在“localhost”上打开客户端连接来解除 AcceptTcpClient() 的阻塞调用(假设127.0.0.1在绑定集中),从而导致 AcceptTcpClient() 返回一个实际的连接。然后,监听器可以检查 token.IsCancellationRequested,从而意识到必须终止。这比让操作系统清理要复杂得多,除非绝对必要,否则我不会接近它。操作系统在停止线程方面非常出色 :) - Martin James
或者在取消回调函数中关闭侦听套接字,从而导致侦听线程中的AcceptTcpClient()抛出异常 - 这也应该可以工作,尽管一些开发人员不喜欢对其TCP堆栈进行这样的操作,即使自Windows 95以来一切都运行正常。 - Martin James
1个回答

5
试试这样做:
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){                    
            }
        }
    }
}

TcpListener.Pending()这种方式不是很好,因为你必须使用Thread.Sleep(毫秒数)或者类似的方法 - 这样会在客户端接收之间产生一些延迟(睡眠中的毫秒数),这很糟糕。


2
这个解决方案的问题在于AcceptSocket方法是阻塞的。因此,在等待传入连接时,您无法优雅地停止侦听线程。 - C-F

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接