You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决TcpListener在.NET 8 Windows下抛出的未观察任务异常

解决.NET 8中TcpListener单元测试的未观察Task异常问题

你的测试在.NET Framework 4.8中正常运行,但在.NET 8 Windows环境下触发未观察Task异常,核心原因是:

  • .NET 5+对APM(Begin/End)异步模式的底层实现做了调整,BeginAcceptTcpClient会创建一个后台Task执行异步接受操作。
  • 调用tcpListener.Stop()时,该异步操作会被强制中止并抛出SocketException,但这个Task未被任何代码“观察”(既没有等待完成,也没有访问其Exception属性)。
  • 当GC回收相关资源时,未被观察的Task异常会触发UnobservedTaskException事件,最终导致测试失败。

以下是两种可行的解决方案:

方案1:改用现代异步API(推荐)

放弃老旧的APM模式,使用基于Task的AcceptTcpClientAsync配合CancellationToken优雅控制异步操作生命周期,同时捕获预期异常:

private TcpListener _tcpListener;
private CancellationTokenSource _cts;

[SetUp]
public void Setup()
{
    _cts = new CancellationTokenSource();
    _tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 12345));
    _tcpListener.Start();
}

[Test]
public async Task TestTcpListener()
{
    // 启动异步接受任务并绑定取消信号
    var acceptTask = _tcpListener.AcceptTcpClientAsync(_cts.Token);
    
    // 停止Listener并触发取消
    _tcpListener.Stop();
    _cts.Cancel();
    
    try
    {
        // 等待任务完成,捕获预期的操作中止异常
        await acceptTask;
    }
    catch (SocketException ex) when (ex.SocketErrorCode == SocketError.OperationAborted)
    {
        // 该异常为预期情况,无需向上抛出
    }
}

[TearDown]
public void TearDown()
{
    _cts.Dispose();
    _tcpListener?.Stop();
}

方案2:兼容APM模式的处理方式

如果必须保留BeginAcceptTcpClient,需手动提取并处理对应的Task,确保异常被观察:

TcpListener tcpListener;

public void TestTcpListener()
{
    tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 12345));
    tcpListener.Start();
    
    // 获取APM操作对应的Task
    var asyncResult = tcpListener.BeginAcceptTcpClient(null, null);
    var acceptTask = Task.Factory.FromAsync(asyncResult, ar => tcpListener.EndAcceptTcpClient(ar));
    
    tcpListener.Stop();
    
    // 观察任务异常:通过Wait()等待并捕获预期异常
    try
    {
        acceptTask.Wait();
    }
    catch (AggregateException ex) when (ex.InnerException is SocketException se && se.SocketErrorCode == SocketError.OperationAborted)
    {
        // 捕获预期的操作中止异常
    }
}

关键注意事项

  • 无论采用哪种方式,必须确保异步接受操作的Task被“观察”:要么等待其完成(同步/异步均可),要么主动访问其Exception属性。
  • 使用CancellationToken可以避免粗暴中止操作带来的未处理异常,是.NET现代异步编程的标准实践。
  • .NET 5+中APM模式的API已逐步被Task异步API替代,推荐优先使用现代编程模型。

内容的提问来源于stack exchange,提问作者user29240887

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:54:51