Files
plugin-library/Assets/Main/HotfixUpdateScript/Runtime/UnitySocket/SocketClient/Scripts/Socket/SocketServer.cs
2025-07-02 10:05:26 +08:00

349 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Timers;
using UnityEngine;
public class SocketInfo
{
public string IP;
public Thread ReceiveThread;
public long HeadTime;
}
/// <summary>
/// Socket服务端
/// </summary>
public class SocketServer
{
/// <summary>
/// 主线程
/// </summary>
private readonly SynchronizationContext _mainThread;
private const int HEAD_TIMEOUT = 5000; // 心跳超时 毫秒
private const int HEAD_CHECKTIME = 5000; // 心跳包超时检测 毫秒
public readonly Dictionary<Socket, SocketInfo> ClientInfoDic = new Dictionary<Socket, SocketInfo>();
private readonly Socket _server;
private System.Timers.Timer _headCheckTimer;
private readonly DataBuffer _dataBuffer = new DataBuffer();
public event Action<Socket> OnConnect; //客户端建立连接回调
public event Action<Socket> OnDisconnect; // 客户端断开连接回调
public event Action<Socket, SocketDataPack> OnReceive; // 接收报文回调
public event Action<Socket, SocketDataPack> OnSend; // 发送报文回调
// 目前捕获异常将触发OnDisconnect回调 暂不单独处理
// public event Action<SocketException> OnError; // 异常捕获回调
private bool _isValid = true;
public SocketServer(string ip, int port)
{
OnConnect += (client) =>
{
UnityEngine.Debug.LogFormat("UnityEvo:连接成功 >> IP:{0}", client.LocalEndPoint.ToString());
};
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);
IPAddress 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 System.Timers.Timer(HEAD_CHECKTIME);
_headCheckTimer.AutoReset = true;
_headCheckTimer.Elapsed += delegate(object sender, ElapsedEventArgs args) { CheckHeadTimeOut(); };
_headCheckTimer.Start();
Debug.Log($"SocketServer Start: {ip}:{port}");
}
/// <summary>
/// 监听客户端连接
/// </summary>
private void ListenClientConnect()
{
while (true)
{
try
{
if (!_isValid) break;
Socket client = _server.Accept();
IPEndPoint ipEndPoint = (IPEndPoint)client.RemoteEndPoint;
Thread receiveThread = new Thread(ReceiveEvent);
ClientInfoDic.Add(client,
new SocketInfo() { IP = ipEndPoint.Address.ToString(), ReceiveThread = receiveThread, HeadTime = GetNowTime() });
receiveThread.Start(client);
PostMainThreadAction<Socket>(OnConnect, client);
}
catch
{
break;
}
}
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private long GetNowTime()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalMilliseconds);
}
public void Send(Socket client, UInt16 e, byte[] buff = null, Action<SocketDataPack> onTrigger = null)
{
SendMessage(client, e, buff, onTrigger);
}
private void SendMessage(Socket client, UInt16 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, new AsyncCallback((asyncSend) =>
{
Socket c = (Socket)asyncSend.AsyncState;
c.EndSend(asyncSend);
PostMainThreadAction<SocketDataPack>(onTrigger, dataPack);
PostMainThreadAction<Socket, SocketDataPack>(OnSend, client, dataPack);
}), client);
}
catch (SocketException ex)
{
CloseClient(client);
// onError(ex);
}
}
/// <summary>
/// 线程内接收数据的函数
/// </summary>
private void ReceiveEvent(object client)
{
Socket tsocket = (Socket)client;
while (true)
{
if (!_isValid) return;
if (!ClientInfoDic.ContainsKey(tsocket))
{
return;
}
try
{
byte[] rbytes = new byte[8 * 1024];
int len = tsocket.Receive(rbytes);
if (len > 0)
{
_dataBuffer.AddBuffer(rbytes, len); // 将收到的数据添加到缓存器中
if (_dataBuffer.TryUnpack(out var dataPack)) // 尝试解包
{
if (dataPack.Type == (UInt16)SocketEvent.ClientHead)
{
// 接收到心跳包
ReceiveHead(tsocket);
}
else if (dataPack.Type == (UInt16)SocketEvent.ClientDisconn)
{
// 客户端断开连接
CloseClient(tsocket);
}
else if (dataPack.Type == (UInt16)SocketEvent.ClientMessage)
{
// 收到消息
PostMainThreadAction<Socket, SocketDataPack>(OnReceive, tsocket, dataPack);
}
}
}
else
{
if (tsocket.Poll(-1, SelectMode.SelectRead))
{
CloseClient(tsocket);
return;
}
}
}
catch (SocketException ex)
{
CloseClient(tsocket);
// onError(ex);
return;
}
}
}
/// <summary>
/// 接收到心跳包
/// </summary>
private void ReceiveHead(Socket client)
{
SocketInfo info;
if (ClientInfoDic.TryGetValue(client, out info))
{
long now = GetNowTime();
long offset = now - info.HeadTime;
UnityEngine.Debug.Log("更新心跳时间戳 >>>" + now + " 间隔>>>" + offset);
if (offset > HEAD_TIMEOUT)
{
// 心跳包收到但超时逻辑
}
info.HeadTime = now;
}
}
/// <summary>
/// 检测心跳包超时
/// </summary>
private void CheckHeadTimeOut()
{
var tempList = new List<Socket>();
foreach (var socket in ClientInfoDic.Keys)
{
tempList.Add(socket);
}
foreach (var socket in tempList)
{
var info = ClientInfoDic[socket];
long now = GetNowTime();
long offset = now - info.HeadTime;
if (offset > HEAD_TIMEOUT)
{
// 心跳包超时
KickOut(socket);
}
}
}
public void KickOut(Socket client)
{
// 踢出连接
Send(client, (UInt16)SocketEvent.ServerKickout, null, (dataPack) => { CloseClient(client); });
}
public void KickOutAll()
{
var tempList = new List<Socket>();
foreach (var socket in ClientInfoDic.Keys)
{
tempList.Add(socket);
}
foreach (var socket in tempList)
{
KickOut(socket);
}
}
/// <summary>
/// 清理客户端连接
/// </summary>
/// <param name="client"></param>
private void CloseClient(Socket client)
{
PostMainThreadAction<Socket>((socket) =>
{
if (OnDisconnect != null) OnDisconnect(socket);
ClientInfoDic.Remove(socket);
socket.Close();
}, client);
}
/// <summary>
/// 关闭
/// </summary>
public void Close()
{
if (!_isValid) return;
_isValid = false;
// if (_connectThread != null) _connectThread.Abort();
var tempList = new List<Socket>();
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();
}
// /// <summary>
// /// 错误回调
// /// </summary>
// /// <param name="e"></param>
// private void onError(SocketException ex)
// {
// PostMainThreadAction<SocketException>(OnError, ex);
// }
// <summary>
/// 通知主线程回调
/// </summary>
private void PostMainThreadAction(Action action)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action e = (Action)o.GetType().GetProperty("action")?.GetValue(o);
if (e != null) e();
}), new { action = action });
}
private void PostMainThreadAction<T>(Action<T> action, T arg1)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T> e = (Action<T>)o.GetType().GetProperty("action")?.GetValue(o);
T t1 = (T)o.GetType().GetProperty("arg1")?.GetValue(o);
if (e != null) e(t1);
}), new { action = action, arg1 = arg1 });
}
public void PostMainThreadAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
{
_mainThread.Post(new SendOrPostCallback((o) =>
{
Action<T1, T2> e = (Action<T1, T2>)o.GetType().GetProperty("action")?.GetValue(o);
T1 t1 = (T1)o.GetType().GetProperty("arg1")?.GetValue(o);
T2 t2 = (T2)o.GetType().GetProperty("arg2")?.GetValue(o);
if (e != null) e(t1, t2);
}), new { action = action, arg1 = arg1, arg2 = arg2 });
}
}