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

C# TCPListener/TcpClient出现ObjectDisposedException问题求助

问题分析与解决方案

首先,你的代码抛出ObjectDisposedException和IOException的核心原因是NetworkStream的生命周期被错误地多次释放,具体来说有两个关键问题:

1. StreamWriter的using块默认会关闭底层NetworkStream

在你的SendMsg方法中,你用using包裹了StreamWriter,而默认情况下,StreamWriter的Dispose方法会自动关闭它所依赖的底层NetworkStream。当这个using块执行完毕,NetworkStream就被释放了,而你的KeepAlive方法还在使用同一个TcpClient的NetworkStream,后续的读写操作自然会抛出异常。

同样,如果KeepAlive方法中的using (NetworkStream networkStream = tcpClient.GetStream())先执行完毕(比如循环因异常退出),也会释放这个流,导致SendMsg中再次调用GetStream()时拿到已释放的对象。

2. async void方法的生命周期难以控制

你的KeepAlive和AcceptConnections都是async void方法,这种方法无法被等待,也很难捕获全局异常,一旦内部出现未处理的异常,可能会直接终止整个应用,同时也会导致using块提前执行,释放资源。


修复方案

方案一:让StreamWriter不关闭底层流

修改SendMsg方法,创建StreamWriter时指定leaveOpen: true,这样using块释放StreamWriter时不会关闭NetworkStream:

public async Task SendMsg(String message) { // 改用async Task代替async void
    try {
        NetworkStream networkStream = tcpClient.GetStream();
        // 设置leaveOpen: true,避免关闭底层流
        using (var writer = new StreamWriter(networkStream, leaveOpen: true)) {
            await writer.WriteLineAsync(message);
            await writer.FlushAsync(); // 手动刷新,确保数据发送
            Console.WriteLine("msg sent");
        };
    } catch (Exception e) {
        Console.WriteLine("SendMsg异常: " + e.Message); // 不要吞异常,便于调试
    }
}

方案二:统一管理NetworkStream的生命周期

不要在多个方法中分别获取NetworkStream,而是在AcceptConnections中拿到TcpClient后,一次性获取NetworkStream,并传递给KeepAlive和SendMsg方法,确保整个连接生命周期内复用同一个流,只在连接断开时才释放它:

public async Task AcceptConnections() { // 改用async Task
    while (true) {
        while (clientsConnected <= maxConnections) {
            try {
                var tcpClient = await tcpListener.AcceptTcpClientAsync();
                var networkStream = tcpClient.GetStream();
                // 启动KeepAlive任务,后台运行
                _ = KeepAlive(tcpClient, networkStream);
            } catch (Exception e) {
                Console.WriteLine("TOP EXCEPTION :: " + e);
            }
            clientsConnected++;
            Console.WriteLine("SERVER Clients connected: " + clientsConnected);
        }
        await Task.Delay(100); // 避免空循环占用CPU
    }
}

public async Task KeepAlive(TcpClient tcpClient, NetworkStream networkStream) { // 改用async Task
    bool clientConnected = true;
    // 不再用using包裹networkStream,而是在连接断开时手动释放
    using (var reader = new StreamReader(networkStream, leaveOpen: true))
    using (var writer = new StreamWriter(networkStream, leaveOpen: true)) {
        writer.AutoFlush = true;
        char keepalive = '0';
        while (clientConnected) {
            try {
                await writer.WriteLineAsync(keepalive);
                string dataFromClient = await reader.ReadLineAsync();
                Console.WriteLine("Server: " + dataFromClient);
                await Task.Delay(500); // 用Task.Delay代替Thread.Sleep,避免阻塞异步线程
            } catch (IOException e) {
                Console.WriteLine("连接异常: " + e.Message);
                clientConnected = false;
            } catch(ObjectDisposedException e) {
                clientConnected = false;
                clientsConnected--;
                Console.WriteLine("客户端断开连接");
            } catch (Exception e) {
                Console.WriteLine("未知异常: " + e.Message);
                clientConnected = false;
            }
        }
        // 连接断开后,释放资源
        networkStream.Dispose();
        tcpClient.Dispose();
    }
}

额外注意事项

  • 永远不要吞掉异常(你的代码里有catch (Exception e) { }),至少要打印异常信息,否则调试时根本不知道哪里出问题。
  • 用Task.Delay代替Thread.Sleep,因为Thread.Sleep会阻塞当前线程,而异步代码中应该用非阻塞的延迟。
  • 避免使用async void,除非是事件处理方法,否则一律用async Task,这样可以更好地控制任务生命周期和捕获异常。
  • 你的Main方法如果要调用异步方法,应该改成async Task Main(C# 7.1+支持),比如:
static async Task Main(string[] args) {
    server.Listen();
    _ = server.AcceptConnections(); // 后台运行接受连接的任务
    client.ConnectToServer();
    for (int i = 0; i < 5; i++) {
        await Task.Delay(3000);
        await server.SendMsg("SENT MSG");
    }
    await Console.In.ReadLineAsync(); // 改用异步读取
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:11:22