【问题标题】:TcpListener blocks thread while listening for incoming messages from TcpClientsTcpListener 在侦听来自 TcpClients 的传入消息时阻塞线程
【发布时间】:2020-08-26 00:28:06
【问题描述】:

我创建了一个新的 .NET Core 辅助服务项目。此服务还应充当 TCP 套接字服务器并侦听传入消息。所以基于the code sample from the docs这就是我基本上在做的事情

public class Worker : BackgroundService
{
    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
        tcpListener.Start();
        
        try
        {
            while (true)
            {
                TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync();
                NetworkStream tcpClientStream = tcpClient.GetStream();
                using StreamReader streamReader = new StreamReader(tcpClientStream);
                string messageText = await streamReader.ReadToEndAsync();
        
                // ... do things with messageText ...
            }
        }
        catch (Exception exception)
        {
            // ... error handling ...
        }

        await base.StartAsync(cancellationToken);
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await Task.Delay(1000, stoppingToken);
        }
    }
}

ExecuteAsync 方法将不再被调用,因为代码卡在StartAsync 方法中的while 循环中。当我注释掉整个覆盖 StartAsync 方法时,一切正常,ExecuteAsync 方法被调用。

是否有可能摆脱阻塞的while循环并使用事件处理程序或类似的东西?

【问题讨论】:

  • await tcpListener.AcceptTcpClientAsync() 将阻塞直到有连接,所以你很可能不想在那里做。可能将其移至ExecuteAsync 或从ExecuteAsync 调用的单独方法?
  • 这似乎本质上是the same question you already posted and deleted请勿转发问题。如果您对您发布的问题收到的回复感到失望,处理它的正确方法是改进原始问题。重新发布相同的问题违反了 Stack Overflow 社区准则,而且会适得其反。

标签: c# .net-core tcplistener


【解决方案1】:

BackgroundService 的典型模式是简单地覆盖ExecuteAsync 并等待stoppingToken 发出信号然后立即退出。

看看BackgroundService的源代码。

如果您想使用“开始/停止”模式,请实现IHostedService

如果你想使用BackgroundService,那么它应该这样布局:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
    tcpListener.Start();
    
    try
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync(); // READ BELOW ABOUT THIS
            NetworkStream tcpClientStream = tcpClient.GetStream();
            using StreamReader streamReader = new StreamReader(tcpClientStream);
            string messageText = await streamReader.ReadToEndAsync();
    
            // ... do things with messageText ...
        }
    }
    catch (Exception exception)
    {
        // ... error handling ...
    }
}

现在,这里最大的问题是AccpetTcpClientAsync 没有取消功能。所以,你可能需要实现something like this

那么你可以这样做:

var tcpClient = await tcpListener.AcceptTcpClientAsync().WithCancellation(stoppingToken);

然后吞下在这种情况下会抛出的OperationCancelledExeption

编辑添加

另一个选项是您可以一起跳过 async/await 版本并使用Begin../End...(在本例中为BeginAcceptTcpClient())。我经常走这条路。当有东西连接时触发事件是非常方便的。请记住,此时您的 TcpListener 需要在类范围内,因此它不会被释放。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-04
    • 2014-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 2018-05-03
    • 1970-01-01
    相关资源
    最近更新 更多