C# 异步 TCP 侦听器不取消循环

Posted

技术标签:

【中文标题】C# 异步 TCP 侦听器不取消循环【英文标题】:C# Asynchronous TCP Listener doesn't cancel loop 【发布时间】:2018-06-09 21:56:52 【问题描述】:

我正在使用 TCPListener 类异步在 C# 中编写服务器。我目前有以下代码作为我的网络侦听器类:

public class NetworkListener

    private readonly SessionController _sessions;
    private readonly TcpListener _server;

    public NetworkListener(KernelConfiguration configuration, SessionController sessions)
    
        _server = new TcpListener(new IPEndPoint(IPAddress.Any, configuration.Network.port));
        _server.Start();

        _sessions = sessions;
    

    public async Task StartAccepting(CancellationToken token)
    
        while (!token.IsCancellationRequested)
        
            var client = await _server.AcceptTcpClientAsync();
            Console.WriteLine($"New connection from client.Client.RemoteEndPoint.");
            _sessions.AddSession(client, Guid.NewGuid(), out var session);
            await session.StartReceiving();
        

        Console.WriteLine("stop");
    

    public void Dispose()
    
        _server.Stop();
    

作为我的 program.cs 我有:

internal class Program

    private static NetworkListener _networkListener;
    private static CancellationTokenSource _cancellationTokenSource;

    private static async Task Main(string[] args)
    
        _networkListener = new NetworkListener(new KernelConfiguration(), new SessionController());
        _cancellationTokenSource = new CancellationTokenSource();

        var thread = new Thread(ProgressConsoleCommands);
        thread.Start();

        try
        
            await _networkListener.StartAccepting(_cancellationTokenSource.Token);
        
        finally
        
            _networkListener.Dispose();
        
    

    private static void ProgressConsoleCommands()
    
        while (true)
        
            var command = Console.ReadLine();
            Console.WriteLine(command);

            switch (command)
            
                case "close":
                    Console.WriteLine("called");
                    _cancellationTokenSource.Cancel();
                    break;
            
        
    

目前发生的情况:

我启动我的程序。它启动 TcpListener。我可以连接到 TcpListener 正在监听的端口。当我输入“close”(当然没有引号)时,它会写“called”。但是,“停止”不会写入控制台,并且 TcpListener 仍在接受新问题。

所需状态:

每当我输入“关闭”时,它应该停止接受新连接,写入“停止”并退出循环。然后它应该调用我的 NetworkListener 类的 Dispose 函数来完全停止 TcpListener 并释放它。

【问题讨论】:

您的解释出了什么问题有点令人困惑(至少对我而言)您能否尝试通过编辑您的帖子/改写/添加更多解释来澄清这一点?您还可以查看how to create a minimal, complete, verifiable example 和how to ask 您可以尝试通过在currentState 下声明您的CurrentState 和在wanted state 下的WantedState 来解决您的问题。我想这可以帮助我理解你的问题 :) 欢迎来到 ***! 不完全确定,但您可能需要将CancellationToken 传递给AcceptTcpClientAsync @CamiloTerevinto - 不幸的是,没有过载可以接受。但你说得对,这就是症结所在。一种(丑陋的)解决方案是在取消后打开一个环回连接,以便我们知道Accept 任务将完成。 @CamiloTerevinto 不,据我所知,它不接受任何参数。 不幸的是,库中许多类的异步实现缺乏关键功能。只需关闭侦听器即可中止挂起的异步任务。 【参考方案1】:

在调用 AcceptTcpClientAsync() 之前,您可以先尝试在 while 循环中检查 TcpListener.Pending(),这样只有在实际有新连接需要处理时,循环才会阻塞。

https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.pending(v=vs.110).aspx

EDIT(0):如果这样做,您可能希望在 while 循环中添加 Thread.Sleep,以最大程度地减少 CPU 负载

EDIT(1):所以我的目标是这样的:

while (!token.IsCancellationRequested) 
    if (_server.Pending()) 
        var client = await _server.AcceptTcpClientAsync();
        // insert code for handling new connection
    
    Thread.Sleep(500);

正如您所描述的,问题似乎是“await AcceptTcpClientAsync()”无限期地阻塞了while循环,直到一个新的客户端尝试连接到服务器。

正如@Damien_The_Unbeliever 在他的评论中指出的那样,您可以通过简单地为 TcpListener 提供一个新的客户端连接来解除阻塞(以一种肮脏的方式)。

然而,上面的例子应该 - 实际上 - 一旦 Thread.Sleep 过期就执行取消

【讨论】:

但这仍然不能让我得到我想要的。 见上文 EDIT(1) @MichaelJagroep 不推荐使用Sleep 线程和socket,因为它会阻塞socket 的I/O Channel,或者任何I/O 流,从而影响程序的性能。跨度>

以上是关于C# 异步 TCP 侦听器不取消循环的主要内容,如果未能解决你的问题,请参考以下文章

C# TCP 在同一个本地端口上侦听和连接

哪种 SSL 证书可以确保 Amazon Lightsail 实例上的 C# 侦听器与 iOS 应用程序之间的安全 TCP 连接?

C# TCP 聊天服务器:连接仅以一种方式工作

如何从 C# 中的 TCP 数据包中获取 Http 正文消息

C# Async TCP Server 矫枉过正?

多线程批处理队列