371 lines
12 KiB
C#
371 lines
12 KiB
C#
using System;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
using UnityEngine;
|
|
using Timer = System.Timers.Timer;
|
|
|
|
/// <summary>
|
|
/// Socket客户端
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// 主线程
|
|
/// </summary>
|
|
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<SocketDataPack> OnReceive; // 接收报文回调
|
|
public event Action<SocketDataPack> OnSend; // 发送报文回调
|
|
public event Action<SocketException> OnError; // 异常捕获回调
|
|
public event Action<int> OnReConnectSuccess; // 重连成功回调
|
|
public event Action<int> OnReConnectError; // 单次重连失败回调
|
|
public event Action<int> 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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 断线重连
|
|
/// </summary>
|
|
/// <param name="num"></param>
|
|
/// <param name="index"></param>
|
|
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<SocketDataPack> 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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 线程内接收数据的函数
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 业务逻辑 - 客户端主动断开
|
|
/// </summary>
|
|
public void DisConnect()
|
|
{
|
|
Send((ushort)SocketEvent.ClientDisconn);
|
|
onDisconnect();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 接收到心跳包
|
|
/// </summary>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 缓存数据清理
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 错误回调
|
|
/// </summary>
|
|
/// <param name="e"></param>
|
|
private void onError(SocketException ex)
|
|
{
|
|
Close();
|
|
|
|
PostMainThreadAction(OnError, ex);
|
|
|
|
if (!_isReconnect) ReConnect();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 断开回调
|
|
/// </summary>
|
|
private void onDisconnect()
|
|
{
|
|
Close();
|
|
|
|
PostMainThreadAction(OnDisconnect);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 通知主线程回调
|
|
/// </summary>
|
|
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<T>(Action<T> action, T arg1)
|
|
{
|
|
_mainThread.Post(o =>
|
|
{
|
|
var e = (Action<T>)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<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
|
|
{
|
|
_mainThread.Post(o =>
|
|
{
|
|
var e = (Action<T1, T2>)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 });
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前时间戳
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private long GetNowTime()
|
|
{
|
|
var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
|
return Convert.ToInt64(ts.TotalMilliseconds);
|
|
}
|
|
} |