6 major server modules (PdkFriendServer/GlobalSever/ServerCore/GameModule/GameNetModule) + game logic (GameFix/GameDAL/ServerData) + network layer (NetWorkMessage) + data layer (ObjectModel) + utilities (MrWu/Core/Config/CloudAPI/dll) + adapters (zyxAdapter/base) .NET 8.0 C# solution, 16 projects, 958 source files
218 lines
6.7 KiB
C#
218 lines
6.7 KiB
C#
using System;
|
||
using MrWu.Debug;
|
||
using Server;
|
||
using WebSocketSharp.Server;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
using GameMessage;
|
||
using MessagePack;
|
||
using MrWu;
|
||
using Server.Core;
|
||
|
||
namespace Server.Net
|
||
{
|
||
public class NetManager : SingleInstance<NetManager>, IDisposable
|
||
{
|
||
public static bool isStart
|
||
{
|
||
get;
|
||
private set;
|
||
}
|
||
|
||
public bool IsDispose { get; private set; }
|
||
|
||
private WebSocketServer _webSocketServer;
|
||
|
||
/// <summary>
|
||
/// 会话集合
|
||
/// </summary>
|
||
private ConcurrentDictionary<long, Session> sessionDic = new ConcurrentDictionary<long, Session>();
|
||
|
||
/// <summary>
|
||
/// SessionIds 队列
|
||
/// </summary>
|
||
private readonly ConcurrentQueue<long> sessionIds = new ConcurrentQueue<long>();
|
||
|
||
/// <summary>
|
||
/// 当前的链接数
|
||
/// </summary>
|
||
public int ConnectCount
|
||
{
|
||
get
|
||
{
|
||
return sessionDic.Count;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前接收的数量
|
||
/// </summary>
|
||
public long ReceiveCount;
|
||
|
||
public const int MaxMessageSize = 1024*1024;
|
||
|
||
/// <summary>
|
||
/// 启动网络
|
||
/// </summary>
|
||
public void Start(string webPath,int webPort)
|
||
{
|
||
if (!string.IsNullOrEmpty(webPath) && webPort > 0)
|
||
{
|
||
//webSocket 服务 启动
|
||
WChannel.OnConnectedEvt += WConnected;
|
||
WChannel.OnDisconnectedEvt += WDisconnected;
|
||
WChannel.OnReceivedEvt += WReceived;
|
||
WChannel.OnErrorEvt += WError;
|
||
_webSocketServer = new WebSocketServer(webPort);
|
||
_webSocketServer.AddWebSocketService<WChannel>(webPath);
|
||
_webSocketServer.Start();
|
||
//webSocket 服务 结束
|
||
|
||
isStart = true;
|
||
}
|
||
|
||
SessionUUIDManager.Instance.Init();
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
IsDispose = true;
|
||
WebSocketStop();
|
||
}
|
||
|
||
private void WebSocketStop()
|
||
{
|
||
Task.Factory.StartNew(() =>
|
||
{
|
||
_webSocketServer?.Stop();
|
||
});
|
||
}
|
||
|
||
#region WebSocket 事件
|
||
|
||
private void WConnected(WChannel channel)
|
||
{
|
||
Session session = new Session(channel);
|
||
Interlocked.Exchange(ref session.ReadWriteTime,TimeInfo.Instance.FrameTime);
|
||
long id = session.Id;
|
||
channel.SessionId = id;
|
||
if (!sessionDic.TryAdd(id, session))
|
||
{
|
||
Debug.Error("添加 webSocket 链接到 字典失败!!!");
|
||
}
|
||
else
|
||
{
|
||
sessionIds.Enqueue(id);
|
||
}
|
||
}
|
||
|
||
private void WDisconnected(WChannel channel)
|
||
{
|
||
long sessionId = channel.SessionId;
|
||
if (sessionDic.TryRemove(sessionId,out Session session))
|
||
{
|
||
session.OnDisConnected();
|
||
}
|
||
else
|
||
{
|
||
Debug.Error("webSocket断开连接,竟然未找到 session!");
|
||
}
|
||
}
|
||
|
||
private void WReceived(WChannel channel, byte[] data)
|
||
{
|
||
long sessionId = channel.SessionId;
|
||
if (sessionDic.TryGetValue(sessionId, out Session session))
|
||
{
|
||
//因为没去掉头
|
||
Received(session,data,0);
|
||
}
|
||
else
|
||
{
|
||
Debug.Error("接收到webSocket包,未找到会话!");
|
||
}
|
||
}
|
||
|
||
private void WError(WChannel channel, Exception e)
|
||
{
|
||
Debug.Error("Web Socket Error:" + e.Message);
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 接受到数据
|
||
/// </summary>
|
||
/// <param name="session"></param>
|
||
/// <param name="data"></param>
|
||
/// <param name="offset"></param>
|
||
private void Received(Session session, byte[] data,int offset)
|
||
{
|
||
try
|
||
{
|
||
//opcode
|
||
int opcode = BitConverter.ToInt32(data, offset);
|
||
offset += 4;
|
||
//4个备份字节
|
||
offset += 4;
|
||
//后面是包内容
|
||
//Debug.Info($"接收到数据{opcode},{data.Length}");
|
||
Type messageType = MessageManager.GetType(opcode);
|
||
MessageData messageData = (MessageData)MessagePackSerializer.Deserialize(messageType,
|
||
new ReadOnlyMemory<byte>(data, offset, data.Length - offset));
|
||
MessageDispatcher.Instance.AddPack(GamePacket.Create(opcode, messageData, session));
|
||
|
||
Interlocked.Exchange(ref session.ReadWriteTime, TimeInfo.Instance.FrameTime);
|
||
Interlocked.Add(ref ReceiveCount, 1);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.Error($"收包解析报错:{e.Message}");
|
||
}
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
// 清理超时,和失效的链接
|
||
long timeNow = TimeInfo.Instance.FrameTime;
|
||
const int MaxCheckNum = 10;
|
||
int n = sessionIds.Count;
|
||
if (n > MaxCheckNum)
|
||
{
|
||
n = MaxCheckNum;
|
||
}
|
||
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
if (this.sessionIds.TryDequeue(out long sessionId))
|
||
{
|
||
if (this.sessionDic.TryGetValue(sessionId, out Session session))
|
||
{
|
||
if (timeNow - session.ReadWriteTime > 20 * 1000)
|
||
{
|
||
Debug.Info($"超时断线! {session.IsDisConnected}");
|
||
//var key = Debug.StartTiming();
|
||
session.DisConnected();
|
||
// double runtime = Debug.GetRunTime(key);
|
||
// if (runtime > 20)
|
||
// {
|
||
// Debug.Warning($"NetManager runtime:{runtime},n:{n}");
|
||
// }
|
||
//超时断线
|
||
continue;
|
||
}
|
||
this.sessionIds.Enqueue(sessionId);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} |