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
427 lines
15 KiB
C#
427 lines
15 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using System.Net;
|
||
using GameMessage;
|
||
using K4os.Compression.LZ4;
|
||
using MessagePack;
|
||
using MrWu.Debug;
|
||
using NetWorkMessage;
|
||
using Server.Core;
|
||
using UnityGame;
|
||
|
||
namespace Server.Net
|
||
{
|
||
/// <summary>
|
||
/// 用户链接管理 这个是管理新的用户连接 子线程
|
||
/// </summary>
|
||
public class UserNetSessionManager : IDisposable
|
||
{
|
||
private TService Service { get; set; }
|
||
|
||
/// <summary>
|
||
/// 60s 超时断开 25秒一个心跳包
|
||
/// </summary>
|
||
private long TimeOut = 60 * 1000;
|
||
|
||
/// <summary>
|
||
/// 所有Session
|
||
/// </summary>
|
||
private Dictionary<long, UserNetSession> Sessions = new Dictionary<long, UserNetSession>();
|
||
|
||
private readonly Queue<long> SessionIds = new Queue<long>();
|
||
|
||
public int ConnectCnt;
|
||
|
||
public void Start(string innerIp, int innerPort)
|
||
{
|
||
Start(new IPEndPoint(IPAddress.Parse(innerIp), innerPort));
|
||
}
|
||
|
||
public void Start(IPEndPoint ipEndPoint)
|
||
{
|
||
Service = new TService(ipEndPoint, ServiceType.Router);
|
||
|
||
Debug.Log($"启动监听:{ipEndPoint}");
|
||
this.Service.AcceptCallBack = OnAccept;
|
||
this.Service.ReadCallBack = OnRead;
|
||
this.Service.ErrorCallBack = OnError;
|
||
}
|
||
|
||
public void Log()
|
||
{
|
||
Debug.ImportantLog($"当前用户链接数:{Sessions.Count}");
|
||
Service.Log();
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
CheckTimeOut();
|
||
Service.Update();
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
Service.Dispose();
|
||
}
|
||
|
||
private void SendMessage(long id, MessageObject message)
|
||
{
|
||
(uint opcode, MemoryBuffer memoryBuffer) =
|
||
MessageSerializeHelper.ToRouterMemoryBuffer(Service, message);
|
||
|
||
memoryBuffer.Seek(0, SeekOrigin.Begin);
|
||
Service.Send(id, memoryBuffer);
|
||
message.Dispose();
|
||
}
|
||
|
||
#region 网络事件
|
||
|
||
private void OnAccept(long channelId, IPEndPoint ipEndPoint)
|
||
{
|
||
Debug.Log($"User OnAccept! {System.Threading.Thread.CurrentThread.ManagedThreadId}");
|
||
//userSessionEvents.Enqueue(UserSessionEvent.Create(channelId, ipEndPoint));
|
||
UserNetSession userSession = new UserNetSession(channelId, Service, SessionDisposed);
|
||
userSession.LastRecvTime = TimeInfo.Instance.ClientFrameTime();
|
||
if (this.Sessions.ContainsKey(channelId))
|
||
{
|
||
Debug.Error($"AddSession UserSession alreadyExists SessionId:{userSession.Id}");
|
||
return;
|
||
}
|
||
|
||
Debug.Info($"用户连接:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
|
||
|
||
this.Sessions.Add(channelId, userSession);
|
||
ConnectCnt++;
|
||
userSession.RemoteAddress = ipEndPoint;
|
||
SessionIds.Enqueue(channelId);
|
||
}
|
||
|
||
public void ForceDisConnect(long id,long instanceId,int finCode)
|
||
{
|
||
UserNetSession session = Get(id);
|
||
if (session == null || session.InstanceId != instanceId)
|
||
{
|
||
Debug.Info("强制玩家断开时,链接已经不在!");
|
||
return;
|
||
}
|
||
|
||
session.ForceDisConnectCode = finCode;
|
||
if (finCode == 0)
|
||
{
|
||
TChannel channel = Service.Get(id);
|
||
channel.Dispose();
|
||
}
|
||
else
|
||
{
|
||
UserSessionFin(session, false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用户断开链接
|
||
/// </summary>
|
||
/// <param name="session"></param>
|
||
/// <param name="initiative">是否是主动断开</param>
|
||
private void UserSessionFin(UserNetSession session, bool initiative)
|
||
{
|
||
if (session == null)
|
||
{
|
||
Debug.Error($"断开时找不到session!");
|
||
return;
|
||
}
|
||
|
||
long id = session.Id;
|
||
|
||
if (session.InstanceId > 0)
|
||
{
|
||
UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(session.Id, session.InstanceId, UserSessionEvtType.Fin));
|
||
}
|
||
|
||
//Debug.Info($"玩家断开:{id} {initiative} {session.State}");
|
||
if (!initiative)
|
||
{
|
||
//已经处理过异常断开
|
||
if (session.IsDisConnected)
|
||
{
|
||
return;
|
||
}
|
||
session.IsDisConnected = true;
|
||
}
|
||
|
||
if (session.State == UserSessionState.Using)
|
||
{
|
||
{
|
||
//置为空
|
||
session.InstanceId = 0;
|
||
|
||
//Debug.Info($"发送断开原因! {session.ForceDisConnectCode}");
|
||
session.State = UserSessionState.Idle;
|
||
|
||
//发送确认断开
|
||
RouterFinAckMessage ackMessage = RouterFinAckMessage.Create(session.ForceDisConnectCode);
|
||
SendMessage(session.Id, ackMessage);
|
||
ackMessage.Dispose();
|
||
|
||
}
|
||
}
|
||
|
||
if (!initiative) //只要不是主动断开,那就是真断开了
|
||
{
|
||
session.State = UserSessionState.Fin;
|
||
ConnectCnt--;
|
||
if (!this.Sessions.Remove(id))
|
||
{
|
||
Debug.Error($"RemoveSession UserSession not exists SessionId:{id}");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
//单线程
|
||
private void OnRead(long channelId, MemoryBuffer memoryBuffer)
|
||
{
|
||
// 解包,把包数据发给主线程处理
|
||
UserNetSession session = Get(channelId);
|
||
|
||
if (session == null)
|
||
{
|
||
Debug.Error($"收到包,未找到链接,链接可能已经断开!{channelId}");
|
||
return;
|
||
}
|
||
|
||
session.LastRecvTime = TimeInfo.Instance.ClientFrameTime();
|
||
//Debug.Log($"收到包:{session.LastRecvTime} {System.Threading.Thread.CurrentThread.ManagedThreadId}");
|
||
(uint opcode, IMessage message) = MessageSerializeHelper.ToRouterMessage(Service, memoryBuffer);
|
||
Service.Recycle(memoryBuffer);
|
||
|
||
if (message == null)
|
||
{
|
||
Debug.Error("message is null!");
|
||
return;
|
||
}
|
||
|
||
switch (opcode)
|
||
{
|
||
case MessageOpcode.Client2ServerMessage:
|
||
OnOuterMessage(session, message);
|
||
break;
|
||
case MessageOpcode.RouterHeart:
|
||
OnRouterHeartMessage(session, message);
|
||
break;
|
||
//请求连接
|
||
case MessageOpcode.RouterSyn:
|
||
OnRouterSynMessage(session, message);
|
||
break;
|
||
// case MessageOpcode.RouterAck:
|
||
// OnRouterAckMessage(session, message);
|
||
// break;
|
||
case MessageOpcode.RouterFin:
|
||
OnRouterFinMessage(session, message);
|
||
break;
|
||
// case MessageOpcode.RouterFinAck:
|
||
// OnRouterFinAckMessage(session, message);
|
||
// break;
|
||
|
||
default:
|
||
Debug.Warning($"收到未知的包类型:{opcode}");
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void OnError(long channelId, int error)
|
||
{
|
||
Debug.Info($"User OnError :{error}");
|
||
UserNetSession userSession = Get(channelId);
|
||
if (userSession == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
userSession.Error = error;
|
||
userSession.Dispose();
|
||
}
|
||
|
||
#endregion
|
||
|
||
private UserNetSession Get(long channelId)
|
||
{
|
||
if (this.Sessions.TryGetValue(channelId, out UserNetSession userSession))
|
||
{
|
||
return userSession;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
//销毁事件 这个是多线程的
|
||
private void SessionDisposed(long channelId)
|
||
{
|
||
UserNetSession session = Get(channelId);
|
||
UserSessionFin(session, false);
|
||
}
|
||
|
||
//[长度][版本][压缩位][Actor][opcode]
|
||
private const int PackIndex = 4 + 1 + 1 + 3 + 4;
|
||
|
||
private void OnOuterMessage(UserNetSession userSession, IMessage message)
|
||
{
|
||
if (userSession.ForceDisConnectCode > 0)
|
||
{
|
||
Debug.Info("这个Session 已经被强制下线!");
|
||
return;
|
||
}
|
||
|
||
if (message is Client2ServerMessage outerMessage)
|
||
{
|
||
userSession.Ver = outerMessage.ver;
|
||
//解包
|
||
|
||
try
|
||
{
|
||
int opcode = outerMessage.OpCode;
|
||
Type messageType = MessageManager.GetType(opcode);
|
||
|
||
//Debug.Log($"收到包 opcode:{opcode} ver:{outerMessage.ver} compress:{outerMessage.Compress} length:{outerMessage.SourceData.Length}");
|
||
|
||
ReadOnlyMemory<byte> sourceData = null;
|
||
if (outerMessage.Compress == 1) //压缩了,需要解压
|
||
{
|
||
//原始数据长度
|
||
//int originLength = BitConverter.ToInt32(outerMessage.SourceData, PackIndex);
|
||
int inputLength = outerMessage.SourceData.Length - PackIndex - 4;
|
||
byte[] packSourceData =
|
||
LZ4Pickler.Unpickle(outerMessage.SourceData, PackIndex + 4, inputLength);
|
||
sourceData = new ReadOnlyMemory<byte>(packSourceData);
|
||
}
|
||
else
|
||
{
|
||
sourceData = new ReadOnlyMemory<byte>(outerMessage.SourceData, PackIndex,
|
||
outerMessage.SourceData.Length - PackIndex);
|
||
}
|
||
|
||
MessageData messageData = (MessageData)MessagePackSerializer.Deserialize(messageType, sourceData);
|
||
|
||
//主现成去处理
|
||
UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(userSession.Id, userSession.InstanceId,GamePacket.Create(opcode, messageData),outerMessage.RemoteIpAddress));
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.Error($"解包报错:{e.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 心跳包
|
||
/// </summary>
|
||
/// <param name="userSession"></param>
|
||
/// <param name="message"></param>
|
||
private void OnRouterHeartMessage(UserNetSession userSession, IMessage message)
|
||
{
|
||
if (message is RouterHeart routerHeart)
|
||
{
|
||
SendMessage(userSession.Id, routerHeart);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 链接
|
||
/// </summary>
|
||
/// <param name="userSession"></param>
|
||
/// <param name="message"></param>
|
||
private void OnRouterSynMessage(UserNetSession userSession, IMessage message)
|
||
{
|
||
if (message is RouterSynMessage routerSyn)
|
||
{
|
||
if (userSession.State != UserSessionState.Idle)
|
||
{
|
||
Debug.Error("链接正在使用,不能确认连接!");
|
||
return;
|
||
}
|
||
|
||
userSession.ConnectAck();
|
||
Debug.Log("发送确认连接!");
|
||
//实例ID
|
||
userSession.State = UserSessionState.Using;
|
||
|
||
RouterAckMessage ackMessage = RouterAckMessage.Create();
|
||
SendMessage(userSession.Id, ackMessage);
|
||
|
||
//链接完成的事件
|
||
UserSessionManager.Instance.AddUserSessionEvt(UserSessionEvt.Create(userSession.Id, userSession.InstanceId, UserSessionEvtType.Connected));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 断开
|
||
/// </summary>
|
||
/// <param name="userSession"></param>
|
||
/// <param name="message"></param>
|
||
private void OnRouterFinMessage(UserNetSession userSession, IMessage message)
|
||
{
|
||
if (message is RouterFinMessage routerFin)
|
||
{
|
||
UserSessionFin(userSession, userSession.ForceDisConnectCode <= 0);
|
||
Debug.Info("用户主动断开连接!");
|
||
}
|
||
}
|
||
|
||
private void CheckTimeOut()
|
||
{
|
||
long timeNow = TimeInfo.Instance.FrameTime;
|
||
const int MaxCheckNum = 5;
|
||
int n = SessionIds.Count;
|
||
|
||
if (n > MaxCheckNum)
|
||
{
|
||
n = MaxCheckNum;
|
||
}
|
||
|
||
for (int i = 0; i < n; i++)
|
||
{
|
||
if (this.Sessions.Count <= 0)
|
||
{
|
||
break;
|
||
}
|
||
|
||
long sessionId = this.SessionIds.Dequeue();
|
||
UserNetSession session = Get(sessionId);
|
||
if (session == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (timeNow - session.LastRecvTime > TimeOut)
|
||
{
|
||
Debug.Info($"超时断线:{session.IsDisConnected} {session.LastRecvTime} {timeNow}");
|
||
session.Dispose();
|
||
continue;
|
||
}
|
||
|
||
this.SessionIds.Enqueue(sessionId);
|
||
}
|
||
}
|
||
|
||
public void Send2Router(long id,long instanceId, MessageObject message)
|
||
{
|
||
UserNetSession userNetSession = Get(id);
|
||
if (userNetSession == null || userNetSession.InstanceId != instanceId)
|
||
{
|
||
Debug.Info("用户已经断开链接,不发送");
|
||
return;
|
||
}
|
||
|
||
(uint opcode, MemoryBuffer memoryBuffer) =
|
||
MessageSerializeHelper.ToRouterMemoryBuffer(Service, message);
|
||
|
||
memoryBuffer.Seek(0, SeekOrigin.Begin);
|
||
Service.Send(id, memoryBuffer);
|
||
message.Dispose();
|
||
}
|
||
}
|
||
} |