TcpListener:如何在等待AcceptTcpClientAsync()时停止侦听?

Bas*_*lew 15 c# sockets c#-5.0 .net-4.5

我不知道如何在异步方法等待传入连接时正确关闭TcpListener.我在SO上找到了这个代码,这里是代码:

public class Server
{
    private TcpListener _Server;
    private bool _Active;

    public Server()
    {
        _Server = new TcpListener(IPAddress.Any, 5555);
    }

    public async void StartListening()
    {
        _Active = true;
        _Server.Start();
        await AcceptConnections();
    }

    public void StopListening()
    {
        _Active = false;
        _Server.Stop();
    }

    private async Task AcceptConnections()
    {
        while (_Active)
        {
            var client = await _Server.AcceptTcpClientAsync();
            DoStuffWithClient(client);
        }
    }

    private void DoStuffWithClient(TcpClient client)
    {
        // ...
    }

}
Run Code Online (Sandbox Code Playgroud)

主要:

    static void Main(string[] args)
    {
        var server = new Server();
        server.StartListening();

        Thread.Sleep(5000);

        server.StopListening();
        Console.Read();
    }
Run Code Online (Sandbox Code Playgroud)

这条线上有一个例外

        await AcceptConnections();
Run Code Online (Sandbox Code Playgroud)

当我调用Server.StopListening()时,该对象被删除.

所以我的问题是,如何取消AcceptTcpClientAsync()以正确关闭TcpListener.

por*_*ges 6

由于这里没有合适的工作示例,这里是一个:

假设您同时拥有cancellationTokenand 的范围tcpListener,那么您可以执行以下操作:

using (cancellationToken.Register(() => tcpListener.Stop()))
{
    try
    {
        var tcpClient = await tcpListener.AcceptTcpClientAsync();
        // … carry on …
    }
    catch (InvalidOperationException)
    {
        // Either tcpListener.Start wasn't called (a bug!)
        // or the CancellationToken was cancelled before
        // we started accepting (giving an InvalidOperationException),
        // or the CancellationToken was cancelled after
        // we started accepting (giving an ObjectDisposedException).
        //
        // In the latter two cases we should surface the cancellation
        // exception, or otherwise rethrow the original exception.
        cancellationToken.ThrowIfCancellationRequested();
        throw;
    }
}
Run Code Online (Sandbox Code Playgroud)


Rob*_*žan 5

虽然基于Stephen Toub博客文章有一个相当复杂的解决方案,但使用内置.NET API的解决方案更为简单:

var cancellation = new CancellationTokenSource();
await Task.Run(() => listener.AcceptTcpClientAsync(), cancellation.Token);

// somewhere in another thread
cancellation.Cancel();
Run Code Online (Sandbox Code Playgroud)

此解决方案不会终止待处理的接受呼叫.但其他解决方案也没有这样做,这个解决方案至少更短.

更新:一个更完整的示例,显示取消后应发生的情况:

var cancellation = new CancellationTokenSource();
var listener = new TcpListener(IPAddress.Any, 5555);
listener.Start();
try
{
    while (true)
    {
        var client = await Task.Run(
            () => listener.AcceptTcpClientAsync(),
            cancellation.Token);
        // use the client, pass CancellationToken to other blocking methods too
    }
}
finally
{
    listener.Stop();
}

// somewhere in another thread
cancellation.Cancel();
Run Code Online (Sandbox Code Playgroud)

更新2: Task.Run仅在任务开始时检查取消令牌.要加速终止接受循环,您可能希望注册取消操作:

cancellation.Token.Register(() => listener.Stop());
Run Code Online (Sandbox Code Playgroud)

  • 这会泄漏套接字,异步调用并使端口永远保持使用状态. (4认同)
  • 是的它无处不在,但你必须打电话给Stop.它不是或者.它是停止或停止和CT.永远停下来 即使CT必须处理,**也会导致异常. (2认同)