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

C#中StreamReader搭配NetworkStream导致程序阻塞的问题排查

TCP聊天程序StreamReader/StreamWriter使用问题修复

原代码核心问题

  1. 服务器端阻塞问题:reader.ReadToEnd()会一直等待到流完全关闭才返回结果,而TCP聊天是长连接场景,连接不会主动关闭,导致服务器永远无法读取到客户端发送的消息。
  2. 消息边界缺失:客户端使用writer.Write(message)发送消息时没有添加换行符,即使服务器改用ReadLine(),也因为没有换行标记而无法识别完整消息。
  3. 资源管理隐患:创建StreamReader和StreamWriter时未指定leaveOpen: true,默认会在释放读写器时关闭底层的NetworkStream,导致TCP连接意外断开。

修正后的服务器代码

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;

public static class Server
{
    public static List<TcpClients> clients = new List<TcpClients>();
    public static void Main(string[] args)
    {
        TcpListener listener = new TcpListener(IPAddress.Any, 7777);
        listener.Start();
        Console.WriteLine("服务器已启动,等待客户端连接...");
        while (true)
        {
            var tcpClient = listener.AcceptTcpClient();
            Console.WriteLine("新客户端连接。");
            var client = new TcpClients(tcpClient);
            clients.Add(client);
            Task.Run(() => client.HandleTcpClient());
        }
    }
}

public class TcpClients : IDisposable
{
    public TcpClient tcpClient;
    public NetworkStream networkStream;
    public StreamReader reader;
    public StreamWriter writer;
    private bool disposed = false;

    public TcpClients(TcpClient client)
    {
        tcpClient = client;
        networkStream = tcpClient.GetStream();
        // 指定leaveOpen: true,避免读写器释放时关闭NetworkStream
        reader = new StreamReader(networkStream, leaveOpen: true);
        writer = new StreamWriter(networkStream, leaveOpen: true);
        // 自动刷新StreamWriter,避免消息滞留
        writer.AutoFlush = true;
    }

    public async void HandleTcpClient()
    {
        try
        {
            string message;
            // 使用ReadLineAsync异步读取,直到客户端断开
            while ((message = await reader.ReadLineAsync()) != null)
            {
                Console.WriteLine($"收到消息:{message}");
                SendMessage(message);
            }
        }
        catch (IOException)
        {
            Console.WriteLine("客户端断开连接");
        }
        finally
        {
            Dispose();
            Server.clients.Remove(this);
        }
    }

    public void SendMessage(string message)
    {
        foreach (TcpClients client in Server.clients)
        {
            if (client == this)
                continue;
            // 发送消息时添加换行符,让客户端的ReadLine能识别
            client.writer.WriteLine(message);
        }
    }

    public void Dispose()
    {
        if (!disposed)
        {
            reader?.Dispose();
            writer?.Dispose();
            networkStream?.Dispose();
            tcpClient?.Close();
            disposed = true;
        }
    }
}

修正后的客户端代码

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;

public class Client
{
    public static TcpClient tcpClient = new TcpClient();
    public static NetworkStream ns;
    public static StreamReader reader;
    public static StreamWriter writer;

    static async Task Main(string[] args)
    {
        try
        {
            tcpClient.Connect(IPAddress.Loopback, 7777);
            ns = tcpClient.GetStream();
            reader = new StreamReader(ns, leaveOpen: true);
            writer = new StreamWriter(ns, leaveOpen: true) { AutoFlush = true };

            // 启动异步任务接收服务器消息
            _ = Task.Run(ReceiveMessages);

            Console.WriteLine("已连接到服务器,输入消息发送:");
            while (true)
            {
                var message = Console.ReadLine();
                if (string.IsNullOrEmpty(message))
                    continue;
                // 发送消息时添加换行符
                writer.WriteLine(message);
            }
        }
        catch (SocketException)
        {
            Console.WriteLine("无法连接到服务器");
        }
        catch (IOException)
        {
            Console.WriteLine("与服务器断开连接");
        }
        finally
        {
            reader?.Dispose();
            writer?.Dispose();
            ns?.Dispose();
            tcpClient?.Close();
        }
    }

    static async Task ReceiveMessages()
    {
        string message;
        while ((message = await reader.ReadLineAsync()) != null)
        {
            Console.WriteLine($"收到其他客户端消息:{message}");
        }
    }
}

关键修改说明

  • 替换ReadToEnd为ReadLineAsync:异步读取直到换行符,既不会阻塞线程,又能正确识别每条消息的边界。
  • 添加换行符标记:客户端发送和服务器转发消息时使用WriteLine,自动添加换行符,让ReadLine能正确分割消息。
  • 资源管理优化:指定leaveOpen: true保护底层流,实现IDisposable接口确保资源正确释放,添加异常处理捕获连接断开场景。
  • 异步处理:使用异步方法避免主线程阻塞,提升程序响应性。

内容的提问来源于stack exchange,提问作者J. Holtan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:59:52