.NET环境下TCP多客户端简易聊天系统的实现逻辑与问题咨询(含现有服务器代码)
.NET环境下TCP多客户端简易聊天系统的实现逻辑与问题咨询(含现有服务器代码)
嘿,我完全懂你现在的困境——作业要求用TCP做多人聊天,已经搭了能接客户端、存消息的服务器,但不知道怎么把消息广播给所有人,还遇上客户端发完消息就崩的问题对吧?别慌,我先给你理清楚整个聊天系统的核心逻辑,你跟着这个思路去补代码肯定没问题~
一、服务器端核心逻辑拆解
你的服务器已经完成了接收客户端连接和收集消息到队列这两步,还差最关键的两个核心部分:
- 维护在线客户端集合:你需要一个线程安全的集合(比如
ConcurrentDictionary<Guid, TcpClient>或者加锁的List<TcpClient>)来保存所有当前在线的TcpClient实例。这样才能明确知道要把消息发给哪些客户端,还要记得在客户端断开连接时从集合中移除它,避免后续广播出错。
- 维护在线客户端集合:你需要一个线程安全的集合(比如
- 独立的消息广播任务:单独启动一个后台异步任务,循环从
ConcurrentQueue<string>中取出消息,然后遍历所有在线客户端,把消息通过它们的NetworkStream发送出去。这里要注意捕获发送时的异常(比如客户端突然断开),及时清理无效的客户端连接。
- 独立的消息广播任务:单独启动一个后台异步任务,循环从
- 现有代码的小优化点:你当前用了Begin/End的老式异步写法,改用
await server.AcceptTcpClientAsync()会更简洁易读;另外当前的消息读取逻辑只把消息存进队列,没有将新连接的客户端加入在线列表,这是后续广播的基础。
- 现有代码的小优化点:你当前用了Begin/End的老式异步写法,改用
二、客户端核心逻辑拆解
你遇到的“发完消息就结束”问题,本质是客户端没有同时处理用户输入发送和接收服务器广播这两个并行任务:
- 双任务并行模型:客户端连接服务器后,需要同时启动两个后台任务:
- 输入监听任务:循环调用
Console.ReadLine()读取用户输入,将输入的字符串转成ASCII字节数组后,通过NetworkStream发送给服务器。 - 消息接收任务:循环从
NetworkStream读取字节数据,转成字符串后打印到控制台,这样就能实时收到其他客户端发的消息。
- 阻止主线程退出:启动两个任务后,主线程不能直接结束,你可以用
Console.ReadLine()让主线程等待,或者用await Task.WhenAll(输入任务, 接收任务)来等待两个任务完成,这样客户端就不会发完一条消息就退出了。
- 阻止主线程退出:启动两个任务后,主线程不能直接结束,你可以用
三、你的现有服务器代码(格式化后)
using System; using System.Threading; using System.Collections.Concurrent; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Collections.Generic; using System.Threading.Tasks; class Program { static TcpListener server = new TcpListener(IPAddress.Any, 9999); static ConcurrentQueue<string> mesaj = new ConcurrentQueue<string>(); private static object locker = new Object(); static void Main() { Task.Run((() => Start())); Console.ReadLine(); } static async Task Start() { server.Start(); Task.Run((() => AcceptConnection())); Console.WriteLine("Sunucu Başlatıldı."); } static async Task AcceptConnection() { server.BeginAcceptTcpClient(Connect, server); } static void Connect(IAsyncResult ar) { TcpClient client = null; try { Task.Run((() => AcceptConnection())); client = server.EndAcceptTcpClient(ar); NetworkStream stream = client.GetStream(); Byte[] bytes = new Byte[256]; string data = null; int i; while (true) { try { i = stream.Read(bytes, 0, bytes.Length); if (i == 0) break; data = Encoding.ASCII.GetString(bytes, 0, i); mesaj.Enqueue(data); } catch (IOException ioEx) { Console.WriteLine("Bağlantı Kesildi..."); break; } catch (Exception ex) { Console.WriteLine(ex.Message); break; } } } catch (IOException e) { } finally { client?.Close(); } } }
内容来源于stack exchange
相关产品推荐
相关产品推荐

