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

C#实现TCP服务端客户端时客户端发消息无响应、服务端仅收单次数据问题

现有代码问题排查

服务端问题

  • 单客户端阻塞:AcceptTcpClient接收新连接后直接进入死循环读取数据,不会回到外层循环处理新连接,因此只能同时接入1个客户端。
  • 读取逻辑错误:stream.Read的返回值就是实际读取到的字节数,不需要自行遍历buffer统计非0字节;客户端正常关闭连接时Read会返回0,当前代码未判断该返回值,会触发死循环打空日志,只有客户端强制关闭抛出异常时才会有提示。
  • 编码不统一:服务端用UTF8解码数据,客户端用ASCII编码发送,传输中文等非ASCII字符时会出现乱码。
  • 资源未释放:客户端断开后未释放对应的TcpClient、流对象等资源,长时间运行会出现内存泄漏。

客户端问题

  • 发送完立即关闭连接:sendMessage方法最后直接调用stream.Close()和client.Close(),每次发完消息就主动断开连接,自然无法持续收发消息实现聊天功能。
  • 响应读取逻辑阻塞:代码调用streamReader.ReadLine()等待服务端响应,但服务端没有任何回写数据的逻辑,因此会一直卡在该步骤,表现为无响应无报错。
  • 编码不统一:使用ASCII编码和服务端UTF8解码不匹配,容易出现乱码。

修正建议

  • 服务端多线程改造:接收到新的TcpClient后,通过Task.Run将单个客户端的处理逻辑放到独立线程/异步任务中执行,主线程回到AcceptTcpClient等待新连接,同时维护全局在线客户端列表,用于消息转发。
  • 读写逻辑优化:读取流时直接取stream.Read的返回值作为实际接收长度,返回值为0时直接跳出循环释放资源。
  • 客户端长连接改造:将连接建立、消息发送、连接关闭逻辑拆分,聊天场景下建立连接后保持长连接,仅用户主动退出时再关闭连接,同时将接收消息的逻辑放到独立线程,避免卡住UI。
  • 统一编码:两端都使用UTF8处理字符编码,避免乱码。
  • 新增消息转发逻辑:服务端收到某客户端的消息后,遍历在线客户端列表,将消息转发给其他所有在线客户端,实现客户端间聊天。

核心改造代码示例

服务端改造

// 全局维护在线客户端列表
static List<TcpClient> onlineClients = new List<TcpClient>();
static object lockObj = new object();

static void Main(string[] args)
{
    Console.WriteLine("open a port:");
    string userInputPort = Console.ReadLine();
    TcpListener listener = new TcpListener(System.Net.IPAddress.Any, Convert.ToInt32(userInputPort));
    listener.Start();
    Console.WriteLine("listening...");

    while (true)
    {
        Console.WriteLine("Waiting for connection...");
        TcpClient client = listener.AcceptTcpClient();
        Console.WriteLine("Client Accepted");
        // 加入在线列表
        lock(lockObj) onlineClients.Add(client);
        // 独立线程处理当前客户端逻辑
        Task.Run(() => ProcessClient(client));
    }
}

// 单个客户端处理方法
static void ProcessClient(TcpClient client)
{
    try
    {
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[1024];
        while (true)
        {
            int recvLen = stream.Read(buffer, 0, buffer.Length);
            if (recvLen == 0) break; // 客户端正常断开,退出循环
            string msg = Encoding.UTF8.GetString(buffer, 0, recvLen);
            Console.WriteLine("收到消息:" + msg);
            // 转发消息给其他在线客户端
            lock(lockObj)
            {
                foreach(var c in onlineClients.Where(x=>x!=client))
                {
                    var cStream = c.GetStream();
                    byte[] sendBuf = Encoding.UTF8.GetBytes(msg);
                    cStream.Write(sendBuf, 0, sendBuf.Length);
                    cStream.Flush();
                }
            }
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine("客户端连接异常:" + ex.Message);
    }
    finally
    {
        // 移除离线客户端,释放资源
        lock(lockObj) onlineClients.Remove(client);
        client.Close();
        client.Dispose();
        Console.WriteLine("客户端已断开");
    }
}

客户端改造

移除sendMessage方法末尾的stream.Close()和client.Close()调用,单独新增独立线程持续监听服务端转发过来的消息即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 23:24:03