如何识别区分异步Socket服务端的已连接客户端与数据来源
核心原理
TCP连接本身由源IP、源端口、目标IP、目标端口四元组全局唯一标识,服务端每次调用EndAccept返回的Socket实例就对应一个独立的客户端连接,你不需要在业务数据里额外加标识符就能区分不同客户端。
具体改造方案
方案1:用客户端远程终结点直接区分
不需要额外修改实体类,直接从连接对应的Socket实例里取RemoteEndPoint属性就能唯一识别客户端,适合不需要关联客户端侧自定义序号的场景,直接修改ReadCallback方法即可:
public static void ReadCallback(IAsyncResult ar) { String content = String.Empty; StateObject state = (StateObject) ar.AsyncState; Socket handler = state.workSocket; // 直接获取客户端地址+端口作为唯一标识 IPEndPoint clientEndPoint = (IPEndPoint)handler.RemoteEndPoint; string clientUniqueFlag = $"{clientEndPoint.Address}:{clientEndPoint.Port}"; int bytesRead = handler.EndReceive(ar); if (bytesRead > 0) { state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead)); content = state.sb.ToString(); if (content.IndexOf("<EOF>") > -1) { // 输出时带上客户端标识即可区分来源 Console.WriteLine("客户端{0}发来数据,共{1}字节 \n 内容:{2}", clientUniqueFlag, content.Length, content ); Send(handler, content); } else { handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); } } }
方案2:扩展StateObject存储自定义唯一标识
如果你需要给每个客户端分配自定义的业务ID(比如自增序号),可以直接扩展原有的StateObject类实现:
第一步:修改StateObject类增加标识字段
public class StateObject { public const int BufferSize = 1024; public byte[] buffer = new byte[BufferSize]; public StringBuilder sb = new StringBuilder(); public Socket workSocket = null; // 新增字段:客户端全局唯一ID public Guid ClientUniqueId { get; set; } // 若需要关联客户端侧的序号i,可新增此字段 public int ClientCustomSeq { get; set; } }
第二步:在连接建立时分配标识
在AcceptCallback中给新接入的客户端分配唯一标识:
public static void AcceptCallback(IAsyncResult ar) { allDone.Set(); Socket listener = (Socket) ar.AsyncState; Socket handler = listener.EndAccept(ar); StateObject state = new StateObject(); state.workSocket = handler; // 分配全局唯一ID state.ClientUniqueId = Guid.NewGuid(); handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); }
第三步:在接收回调中直接使用标识
后续所有当前连接的回调都可以直接从StateObject中拿到预存的标识:
public static void ReadCallback(IAsyncResult ar) { StateObject state = (StateObject) ar.AsyncState; // 直接取出当前客户端的唯一标识 Guid currentClientId = state.ClientUniqueId; // 后续业务逻辑 // ... }
补充说明
如果你需要关联客户端侧代码里的i序号,只需要客户端连接成功后第一帧数据发送i的值,服务端在第一次读取到数据的时候把i存到StateObject.ClientCustomSeq里即可,后续所有这个连接发来的数据都可以直接用这个序号匹配客户端侧的实例,不需要后续每个请求都带标识符。
内容的提问来源于stack exchange,提问作者Blaze
相关产品推荐
相关产品推荐

