using System; using System.Net; using System.Net.Sockets; using System.Threading; using UnityEngine; using Timer = System.Timers.Timer; /// /// Socket客户端 /// public class SocketClient { private const int TIMEOUT_CONNECT = 3000; // 连接超时时间 毫秒 private const int TIMEOUT_SEND = 3000; // 发送超时时间 毫秒 private const int TIMEOUT_RECEIVE = 3000; //接收超时时间 毫秒 private const int HEAD_OFFSET = 2000; //心跳包发送间隔 毫秒 private const int RECONN_MAX_SUM = 3; //最大重连次数 private readonly DataBuffer _dataBuffer = new(); private readonly string _ip; /// /// 主线程 /// private readonly SynchronizationContext _mainThread; private readonly int _port; private readonly int HEARTBEAT_TIMEOUT = 5000; private Socket _client; private Timer _connTimeoutTimer; private Timer _headTimer; private Timer _heartbeatTimeoutTimer; private bool _isConnect; private bool _isReconnect; private long _lastHeartbeatResponseTime; private Thread _receiveThread; public SocketClient(string ip, int port) { _mainThread = SynchronizationContext.Current; _ip = ip; _port = port; } public event Action OnConnectSuccess; // 连接成功回调 public event Action OnConnectError; // 连接失败回调 public event Action OnDisconnect; // 断开回调 public event Action OnReceive; // 接收报文回调 public event Action OnSend; // 发送报文回调 public event Action OnError; // 异常捕获回调 public event Action OnReConnectSuccess; // 重连成功回调 public event Action OnReConnectError; // 单次重连失败回调 public event Action OnReconnecting; // 单次重连中回调 public void Connect(Action success = null, Action error = null) { OnDisconnect += () => { Debug.Log("UnityEvo:断开连接"); }; OnReceive += dataPack => { Debug.LogFormat("UnityEvo:接收数据>>>{0}", (SocketEvent)dataPack.Type); }; OnSend += dataPack => { Debug.LogFormat("UnityEvo:发送数据>>>{0}", (SocketEvent)dataPack.Type); }; OnError += ex => { Debug.LogFormat("UnityEvo:出现异常>>>{0}", ex); }; OnReConnectSuccess += num => { Debug.LogFormat("UnityEvo:第{0}次重连成功", num); }; OnReConnectError += num => { Debug.LogFormat("UnityEvo:第{0}次重连失败", num); }; OnReconnecting += num => { Debug.LogFormat("UnityEvo:正在进行第{0}次重连", num); }; void OnTrigger(bool flag) { if (flag) { PostMainThreadAction(success); PostMainThreadAction(OnConnectSuccess); } else { PostMainThreadAction(error); PostMainThreadAction(OnConnectError); } if (_connTimeoutTimer != null) { _connTimeoutTimer.Stop(); _connTimeoutTimer = null; } } try { _client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //创建套接字 _client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, TIMEOUT_SEND); _client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, TIMEOUT_RECEIVE); var ipAddress = IPAddress.Parse(_ip); //解析IP地址 var ipEndpoint = new IPEndPoint(ipAddress, _port); var result = _client.BeginConnect(ipEndpoint, iar => { try { var client = (Socket)iar.AsyncState; client.EndConnect(iar); _isConnect = true; // 开始发送心跳包 _headTimer = new Timer(HEAD_OFFSET); _headTimer.AutoReset = true; _headTimer.Elapsed += delegate { Send((ushort)SocketEvent.ClientHead); }; _headTimer.Start(); // 初始化心跳超时检测 _lastHeartbeatResponseTime = GetNowTime(); _heartbeatTimeoutTimer = new Timer(HEARTBEAT_TIMEOUT); _heartbeatTimeoutTimer.AutoReset = true; _heartbeatTimeoutTimer.Elapsed += (sender, e) => { // 检测是否超时未收到服务端心跳响应 ReceiveHead(); }; _heartbeatTimeoutTimer.Start(); // 开始接收数据 _receiveThread = new Thread(ReceiveEvent); _receiveThread.IsBackground = true; _receiveThread.Start(); OnTrigger(true); } catch (SocketException ex) { OnTrigger(false); } }, _client); //异步连接 _connTimeoutTimer = new Timer(TIMEOUT_CONNECT); _connTimeoutTimer.AutoReset = false; _connTimeoutTimer.Elapsed += delegate { OnTrigger(false); }; _connTimeoutTimer.Start(); } catch (SocketException ex) { OnTrigger(false); // throw; } } /// /// 断线重连 /// /// /// public void ReConnect(int num = RECONN_MAX_SUM, int index = 0) { _isReconnect = true; num--; index++; if (num < 0) { onDisconnect(); _isReconnect = false; return; } PostMainThreadAction(OnReconnecting, index); Connect(() => { PostMainThreadAction(OnReConnectSuccess, index); _isReconnect = false; }, () => { PostMainThreadAction(OnReConnectError, index); ReConnect(num, index); }); } public void Send(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, dataPack); }, _client); } catch (SocketException ex) { onError(ex); } } /// /// 线程内接收数据的函数 /// private void ReceiveEvent() { while (true) try { if (!_isConnect) break; if (_client.Available <= 0) continue; var rbytes = new byte[8 * 1024]; var len = _client.Receive(rbytes); if (len > 0) { _dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中 if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包 { //新增:处理服务器心跳响应 if (dataPack.Type == (ushort)SocketEvent.ServerHead) _lastHeartbeatResponseTime = GetNowTime(); // 更新心跳响应时间戳 else if (dataPack.Type == (ushort)SocketEvent.ServerKickout) // 服务端踢出 onDisconnect(); else if (dataPack.Type == (ushort)SocketEvent.ServerMessage) // 收到消息 PostMainThreadAction(OnReceive, dataPack); } } else if (len == 0) { // 服务端正常关闭连接 onDisconnect(); break; } } catch (SocketException ex) { onError(ex); break; } } /// /// 业务逻辑 - 客户端主动断开 /// public void DisConnect() { Send((ushort)SocketEvent.ClientDisconn); onDisconnect(); } /// /// 接收到心跳包 /// private void ReceiveHead() { var now = GetNowTime(); var offset = now - _lastHeartbeatResponseTime; Debug.Log("客户端更新服务端心跳时间戳 >>>" + GetNowTime() + " 间隔>>>" + offset); if (GetNowTime() - _lastHeartbeatResponseTime > HEARTBEAT_TIMEOUT) onError(new SocketException((int)SocketError.TimedOut)); } /// /// 缓存数据清理 /// public void Close() { if (!_isConnect) return; _isConnect = false; if (_headTimer != null) { _headTimer.Stop(); _headTimer = null; } // 停止并释放心跳超时检测定时器 if (_heartbeatTimeoutTimer != null) { _heartbeatTimeoutTimer.Stop(); _heartbeatTimeoutTimer.Dispose(); // 释放资源 _heartbeatTimeoutTimer = null; } // if (_receiveThread != null) // { // _receiveThread.Abort(); // _receiveThread = null; // } if (_connTimeoutTimer != null) { _connTimeoutTimer.Stop(); _connTimeoutTimer = null; } if (_client != null) { _client.Close(); _client = null; } } /// /// 错误回调 /// /// private void onError(SocketException ex) { Close(); PostMainThreadAction(OnError, ex); if (!_isReconnect) ReConnect(); } /// /// 断开回调 /// private void onDisconnect() { Close(); PostMainThreadAction(OnDisconnect); } /// /// 通知主线程回调 /// 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 }); } /// /// 获取当前时间戳 /// /// private long GetNowTime() { var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalMilliseconds); } }