C# Socket单对单通信改造为支持多客户端指定收信方的开发问题
多客户端Socket通信实现方案
核心调整逻辑
- 新增线程安全的客户端管理容器:使用
Dictionary<string, Socket>存储客户端唯一标识(IP+端口组合)与对应的Socket实例,替换原有单客户端Socket变量,操作字典时加锁避免多线程冲突 - 拆分多线程职责:
- 独立线程循环监听客户端连接请求,每成功接入一个客户端就存入管理字典,同时为该客户端启动专属的消息接收线程
- 每个客户端的消息接收逻辑独立运行,互不阻塞,客户端断开连接时自动从管理字典中移除
- 定向消息发送实现:约定服务端输入格式为
[目标客户端标识] 消息内容,解析后匹配对应Socket发送,也可支持广播逻辑 - 调整监听队列配置:将原有
Listen(1)调整为Listen(10),支持最多10个客户端同时排队接入
修改后完整服务端代码
using System; using System.Collections.Generic; using System.Text; using System.Net; using System.Net.Sockets; using System.Threading; namespace Servidor_Chat { class Server { IPAddress ipAddr; IPEndPoint endPoint; Socket s_Server; // 客户端管理字典,key为客户端IP+端口,value为对应Socket Dictionary<string, Socket> clientSockets = new Dictionary<string, Socket>(); // 锁对象,保证字典操作线程安全 object lockObj = new object(); public Server() { ipAddr = IPAddress.Any; endPoint = new IPEndPoint(ipAddr, 1234); s_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); s_Server.Bind(endPoint); // 调整监听队列长度,支持多客户端接入 s_Server.Listen(10); } public void Start() { Console.WriteLine("等待客户端接入..."); // 启动监听线程,循环处理客户端接入请求 Thread acceptThread = new Thread(AcceptClientLoop); acceptThread.IsBackground = true; acceptThread.Start(); } // 循环监听客户端接入 private void AcceptClientLoop() { while (true) { Socket clientSocket = s_Server.Accept(); IPEndPoint clientEP = (IPEndPoint)clientSocket.RemoteEndPoint; string clientKey = $"{clientEP.Address}:{clientEP.Port}"; Console.WriteLine($"新客户端接入:{clientKey}"); // 加入管理字典 lock (lockObj) { clientSockets.Add(clientKey, clientSocket); } // 为当前客户端启动接收线程 Thread receiveThread = new Thread(ReceiveClientMsg); receiveThread.IsBackground = true; receiveThread.Start(clientSocket); } } // 接收指定客户端的消息 private void ReceiveClientMsg(object obj) { Socket clientSocket = obj as Socket; string clientKey = $"{((IPEndPoint)clientSocket.RemoteEndPoint).Address}:{((IPEndPoint)clientSocket.RemoteEndPoint).Port}"; while (true) { try { byte[] ByRec = new byte[255]; int len = clientSocket.Receive(ByRec, 0, ByRec.Length, 0); if (len == 0) { // 客户端正常断开 Console.WriteLine($"客户端{clientKey}断开连接"); lock (lockObj) { clientSockets.Remove(clientKey); } clientSocket.Close(); break; } string textoRecibido = Encoding.Default.GetString(ByRec, 0, len); Console.WriteLine($"客户端[{clientKey}]: {textoRecibido}"); Console.Out.Flush(); } catch (Exception ex) { // 客户端异常断开 Console.WriteLine($"客户端{clientKey}异常断开:{ex.Message}"); lock (lockObj) { if (clientSockets.ContainsKey(clientKey)) { clientSockets.Remove(clientKey); } } clientSocket.Close(); break; } } } // 定向发送消息 public void SendToTarget(string input) { // 输入格式:[目标客户端key] 消息内容,例如 127.0.0.1:54321 你好 // 输入 all 消息内容 则广播给所有客户端 var splitIndex = input.IndexOf(' '); if (splitIndex <= 0) { Console.WriteLine("输入格式错误,正确格式:[目标标识] 消息内容 或 all 消息内容"); return; } string target = input.Substring(0, splitIndex); string msg = input.Substring(splitIndex + 1); byte[] msgBytes = Encoding.Default.GetBytes(msg); if (target == "all") { // 广播 lock (lockObj) { foreach (var socket in clientSockets.Values) { socket.Send(msgBytes, 0, msgBytes.Length, 0); } } Console.WriteLine("广播消息发送完成"); return; } // 定向发送 lock (lockObj) { if (clientSockets.TryGetValue(target, out Socket targetSocket)) { targetSocket.Send(msgBytes, 0, msgBytes.Length, 0); Console.WriteLine($"消息已发送给{target}"); } else { Console.WriteLine("目标客户端不存在"); } } } } class Program { static void Main(string[] args) { Server s = new Server(); s.Start(); while (true) { string input = Console.ReadLine(); s.SendToTarget(input); } Console.WriteLine("按任意键退出"); Console.ReadLine(); } } }
客户端说明
原有客户端代码无需修改,直接运行即可,可多开多个客户端实例测试多连接效果。
测试步骤
- 启动服务端,控制台会打印等待客户端接入的提示
- 启动多个客户端,服务端会打印每个客户端的唯一标识(IP+端口)
- 服务端输入对应客户端标识加消息即可定向发送,例如输入
127.0.0.1:54321 测试消息,对应客户端即可收到消息 - 输入
all 全体通知即可给所有在线客户端发送广播消息
内容的提问来源于stack exchange,提问作者Hades
相关产品推荐
相关产品推荐

