【问题标题】:How to stop async TcpListener properly?如何正确停止异步 TcpListener?
【发布时间】:2019-10-23 17:01:56
【问题描述】:

我的窗口上有两个Button 连接到两个ICommands

public ICommand Start { get; set; }
public ICommand Stop { get; set; }

Constructor中初始化如下:

Start = new Command(StartServer, (o) => !IsListening);
Stop = new Command(StopServer, (o) => IsListening);

然后做这些:

async void StartServer(object obj)
{
    server.Start();
    IsListening = true;
    Status = "Listening";
    try
    {
        while (IsListening)
        {
            var cw = new ClientWrapper();
            cw.Client = await server.AcceptTcpClientAsync();
            cw.EndPoint = cw.Client.Client.RemoteEndPoint;
            Pool.Add(cw);
            HandleClient(cw);
        }
    }
    catch (Exception e)
    {
        throw;
    }
}


void StopServer(object obj)
{
    DisposeClients();
    server.Stop();
    IsListening = false;
    Status = "Start Server";
}

当我单击Start 时,它开始侦听并毫无问题地接受客户端。没有任何客户端,如果我只是Start服务器然后单击Stop,它会正常执行StopServer函数然后点击ICommandExecute函数并到达catch块的StartServer方法! e 中的消息是:

无法访问已处置的对象。对象名称: 'System.Net.Sockets.Socket'。

我不希望它转到catch 块!如何正确处理?

【问题讨论】:

  • "我不希望它转到catch 块!" - catch在服务器级别 没有任何影响;坦率地说,按照设计,套接字是凌乱而脆弱的:异常处理是正常的,也是意料之中的。 “如何妥善处理?” - 通过不在catch 中调用throw :)
  • @MarcGravell,由于StopServer 正常执行,我的TcpListener 肯定没有问题。所以从这个await server.AcceptTcpClientAsync() 行抛出异常!现在,如何在停止之前从AcceptTcpClientAsync 停止服务器。如果无法限制AcceptTcpClientAsync,如果您不介意,请在答案部分提供一个小示例。
  • 似乎没有接受 CancellationToken 的重载。因此,最好的办法是听从 Marc 的建议,在 await server.AcceptTcpClientAsync() 周围使用 try/catch。无论如何我都会推荐。根据错误代码,您可以决定是否可以实际处理错误或退出循环。
  • @Fildor,几年前我尝试 beginaccept/endaccept 类型的异步操作时,我想我在那里使用了 socketerror。这些 async/await 有什么选择吗?
  • 当然。 SocketException 具有 SocketErrorCode 属性。

标签: c# tcplistener


【解决方案1】:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace tcplistenertest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread listenerThread = StartBackgroundThread(new ThreadStart(ThreadProc));

            while(Console.ReadLine() != string.Empty)
            {
            }

            mListener.Stop();
            listenerThread.Join();
        }

        static void ThreadProc()
        {
            try
            {
                mListener = StartListening();

                AcceptLoop(mListener);
            }
            catch(Exception e)
            {
                Console.WriteLine($"ThreadProc error: {e.Message}");
                Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
            }
        }

        static Thread StartBackgroundThread(ThreadStart task)
        {
            Thread thread = new Thread(task);
            thread.IsBackground = true;
            thread.Start();
            return thread;
        }

        static TcpListener StartListening()
        {
            TcpListener result = new TcpListener(IPAddress.Any, PORT);
            result.Start();

            return result;
        }

        static void AcceptLoop(TcpListener listener)
        {
            // this is the main loop of the Socket based server
            while (true)
            {
                Socket socket = null;
                try
                {
                    Console.WriteLine("Accepting connection...");
                    socket = AcceptConnection(listener);
                    Console.WriteLine($"Connection accepted from {socket.RemoteEndPoint}");
                }
                catch (SocketException e)
                {
                    Console.WriteLine($"Socket error accepting connection: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Generic error accepting connection: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }

                try
                {
                    socket.Close();
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Error closing socket: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }
            }

            Console.WriteLine("Accept loop thread stopping");
        }

        static Socket AcceptConnection(TcpListener listener)
        {
            Socket socket = listener.AcceptSocket();
            try
            {
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Debug, 1);
                LingerOption optionValue = new LingerOption(true, 3);

                socket.SetSocketOption(SocketOptionLevel.Socket,
                    SocketOptionName.Linger, optionValue);

                return socket;
            }
            catch (Exception)
            {
                socket.Close();
                throw;
            }
        }

        static TcpListener mListener;
        const int PORT = 55000;
    }
}

【讨论】:

  • 是的,添加一些注释来解释。
  • OP 已经在使用 async/await。我没有看到将所有代码更改为普通旧线程的好处。
猜你喜欢
  • 2010-09-26
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
  • 2014-03-14
  • 2014-03-16
  • 2015-07-08
  • 2021-12-23
  • 2019-06-15
相关资源
最近更新 更多