using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using UnityEngine; using Timer = System.Timers.Timer; public class SocketInfo { public long HeadTime; public string IP; public Thread ReceiveThread; } /// /// Socket服务端 /// public class SocketServer { private const int HEAD_TIMEOUT = 5000; // 心跳超时 毫秒 private readonly DataBuffer _dataBuffer = new(); /// /// 主线程 /// private readonly SynchronizationContext _mainThread; private readonly Socket _server; public readonly Dictionary ClientInfoDic = new(); private Timer _headCheckTimer; // 目前捕获异常将触发OnDisconnect回调 暂不单独处理 // public event Action OnError; // 异常捕获回调 private bool _isValid = true; public SocketServer(string ip, int port) { OnConnect += client => { Debug.LogFormat("UnityEvo:连接成功 >> IP:{0}", client.LocalEndPoint); }; OnDisconnect += client => { if (ClientInfoDic.TryGetValue(client, out var value)) Debug.LogFormat("UnityEvo:连接断开 >> IP:{0}", value.IP); }; _mainThread = SynchronizationContext.Current; _server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); var ipAddress = IPAddress.Parse(ip); //解析IP地址 _server.Bind(new IPEndPoint(ipAddress, port)); //绑定IP地址:端口 _server.Listen(10); //设定最多10个排队连接请求 // 启动线程监听连接 var connectThread = new Thread(ListenClientConnect); connectThread.Start(); // 心跳包定时检测 _headCheckTimer = new Timer(HEAD_TIMEOUT); _headCheckTimer.AutoReset = true; _headCheckTimer.Elapsed += delegate { CheckHeadTimeOut(); }; _headCheckTimer.Start(); Debug.Log($"SocketServer Start: {ip}:{port}"); } public event Action OnConnect; //客户端建立连接回调 public event Action OnDisconnect; // 客户端断开连接回调 public event Action OnReceive; // 接收报文回调 public event Action OnSend; // 发送报文回调 public event Action OnHeadTimeOut; // 心跳包收到超时回调 /// /// 监听客户端连接 /// private void ListenClientConnect() { while (true) try { if (!_isValid) break; var client = _server.Accept(); var ipEndPoint = (IPEndPoint)client.RemoteEndPoint; var receiveThread = new Thread(ReceiveEvent); ClientInfoDic.Add(client, new SocketInfo { IP = ipEndPoint.Address.ToString(), ReceiveThread = receiveThread, HeadTime = GetNowTime() }); receiveThread.Start(client); PostMainThreadAction(OnConnect, client); } catch { break; } } /// /// 获取当前时间戳 /// /// private long GetNowTime() { var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds); } public void Send(Socket client, ushort e, byte[] buff = null, Action onTrigger = null) { SendMessage(client, e, buff, onTrigger); } private void SendMessage(Socket client, ushort e, byte[] buff = null, Action onTrigger = null) { buff = buff ?? new byte[] { }; var dataPack = new SocketDataPack(e, buff); var data = dataPack.Buff; try { client.BeginSend(data, 0, data.Length, SocketFlags.None, asyncSend => { var c = (Socket)asyncSend.AsyncState; c.EndSend(asyncSend); PostMainThreadAction(onTrigger, dataPack); PostMainThreadAction(OnSend, client, dataPack); }, client); } catch (SocketException ex) { CloseClient(client); // onError(ex); } } /// /// 线程内接收数据的函数 /// private void ReceiveEvent(object client) { var tsocket = (Socket)client; while (true) { if (!_isValid) return; if (!ClientInfoDic.ContainsKey(tsocket)) return; try { var rbytes = new byte[8 * 1024]; var len = tsocket.Receive(rbytes); if (len > 0) { _dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中 if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包 { if (dataPack.Type == (ushort)SocketEvent.ClientHead) { // 接收到心跳包 ReceiveHead(tsocket); //返回客户端心跳 Send(tsocket, (ushort)SocketEvent.ServerHead); } else if (dataPack.Type == (ushort)SocketEvent.ClientDisconn) // 客户端断开连接 { CloseClient(tsocket); } else if (dataPack.Type == (ushort)SocketEvent.ClientMessage) // 收到消息 { PostMainThreadAction(OnReceive, tsocket, dataPack); } } } else { if (tsocket.Poll(-1, SelectMode.SelectRead)) { CloseClient(tsocket); return; } } } catch (SocketException ex) { CloseClient(tsocket); // onError(ex); return; } } } /// /// 接收到心跳包 /// private void ReceiveHead(Socket client) { SocketInfo info; if (ClientInfoDic.TryGetValue(client, out info)) { var now = GetNowTime(); var offset = now - info.HeadTime; Debug.Log("更新心跳时间戳 >>>" + now + " 间隔>>>" + offset); if (offset > HEAD_TIMEOUT) // 心跳包收到但超时逻辑 OnHeadTimeOut?.Invoke(client); info.HeadTime = now; } } /// /// 检测心跳包超时 /// private void CheckHeadTimeOut() { var tempList = new List(); foreach (var socket in ClientInfoDic.Keys) tempList.Add(socket); foreach (var socket in tempList) { var info = ClientInfoDic[socket]; var now = GetNowTime(); var offset = now - info.HeadTime; if (offset > HEAD_TIMEOUT) // 心跳包超时 KickOut(socket); } } public void KickOut(Socket client) { // 踢出连接 Send(client, (ushort)SocketEvent.ServerKickout, null, dataPack => { CloseClient(client); }); } public void KickOutAll() { var tempList = new List(); foreach (var socket in ClientInfoDic.Keys) tempList.Add(socket); foreach (var socket in tempList) KickOut(socket); } /// /// 清理客户端连接 /// /// private void CloseClient(Socket client) { PostMainThreadAction(socket => { if (OnDisconnect != null) OnDisconnect(socket); ClientInfoDic.Remove(socket); socket.Close(); }, client); } /// /// 关闭 /// public void Close() { if (!_isValid) return; _isValid = false; // if (_connectThread != null) _connectThread.Abort(); var tempList = new List(); foreach (var socket in ClientInfoDic.Keys) tempList.Add(socket); foreach (var socket in tempList) CloseClient(socket); if (_headCheckTimer != null) { _headCheckTimer.Stop(); _headCheckTimer = null; } _server.Close(); } // /// // /// 错误回调 // /// // /// // private void onError(SocketException ex) // { // PostMainThreadAction(OnError, ex); // } // /// 通知主线程回调 /// private void PostMainThreadAction(Action action) { _mainThread.Post(o => { var e = (Action)o.GetType().GetProperty("action")?.GetValue(o); if (e != null) e(); }, new { action }); } private void PostMainThreadAction(Action action, T arg1) { _mainThread.Post(o => { var e = (Action)o.GetType().GetProperty("action")?.GetValue(o); var t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o); if (e != null) e(t1); }, new { action, arg1 }); } public void PostMainThreadAction(Action action, T1 arg1, T2 arg2) { _mainThread.Post(o => { var e = (Action)o.GetType().GetProperty("action")?.GetValue(o); var t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o); var t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o); if (e != null) e(t1, t2); }, new { action, arg1, arg2 }); } }