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
相关产品推荐
相关产品推荐

