feat: initial commit - HJHA game server full source
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
This commit is contained in:
8
GameNetModule/EventDispatcher/GameNetDispatcher.cs
Normal file
8
GameNetModule/EventDispatcher/GameNetDispatcher.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public class GameNetDispatcher : MultiHandleBaseDispatcher<GameNetDispatcher,int,object>
|
||||
{
|
||||
}
|
||||
}
|
||||
29
GameNetModule/EventDispatcher/GameNetMsgId.cs
Normal file
29
GameNetModule/EventDispatcher/GameNetMsgId.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public class GameNetMsgId
|
||||
{
|
||||
public static int curSor = 0;
|
||||
|
||||
// /// <summary>
|
||||
// /// 会话已连接
|
||||
// /// </summary>
|
||||
// public static readonly uint SessionConnected = ++curSor;
|
||||
|
||||
/// <summary>
|
||||
/// 会话断开
|
||||
/// </summary>
|
||||
public static readonly int SessionDisConnected = ++curSor;
|
||||
|
||||
/// <summary>
|
||||
/// 会话绑定用户事件
|
||||
/// </summary>
|
||||
public static readonly int SessionBindUser = ++curSor;
|
||||
|
||||
/// <summary>
|
||||
/// 会话解除用户绑定
|
||||
/// </summary>
|
||||
public static readonly int SessionUnBindUser = ++curSor;
|
||||
}
|
||||
}
|
||||
150
GameNetModule/EventDispatcher/MessageDispatcher.cs
Normal file
150
GameNetModule/EventDispatcher/MessageDispatcher.cs
Normal file
@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public delegate Task MessageDispatcherDelegate(GamePacket gamePacket);
|
||||
|
||||
/// <summary>
|
||||
/// 网络消息 分发器
|
||||
/// </summary>
|
||||
public sealed class MessageDispatcher : SingleInstance<MessageDispatcher>
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否需要按顺序执行 一般来说子游戏需要按顺序执行,中心服务器不需要按顺序等待
|
||||
/// </summary>
|
||||
public bool IsOrderMessage { get; private set; }
|
||||
|
||||
private ConcurrentQueue<GamePacket> packes = new ConcurrentQueue<GamePacket>();
|
||||
|
||||
public void AddPack(GamePacket gamePacket)
|
||||
{
|
||||
//Debug.Info($"添加一个待处理包裹:{gamePacket.OpCode}");
|
||||
packes.Enqueue(gamePacket);
|
||||
}
|
||||
|
||||
private CoroutineLockManager coroutineLockManager;
|
||||
|
||||
private Dictionary<int, MessageDispatcherDelegate> listeners =
|
||||
new Dictionary<int, MessageDispatcherDelegate>();
|
||||
|
||||
public void Start(bool isOrderMessage, CoroutineLockManager coroutineLockManager)
|
||||
{
|
||||
this.IsOrderMessage = isOrderMessage;
|
||||
this.coroutineLockManager = coroutineLockManager;
|
||||
}
|
||||
|
||||
public void AddListener(int msgId, MessageDispatcherDelegate messageDispatcherDelegate)
|
||||
{
|
||||
listeners[msgId] = messageDispatcherDelegate;
|
||||
}
|
||||
|
||||
public void RemoveListener(int msgId, MessageDispatcherDelegate messageDispatcherDelegate)
|
||||
{
|
||||
if (listeners.ContainsKey(msgId) && listeners[msgId] == messageDispatcherDelegate)
|
||||
{
|
||||
listeners.Remove(msgId);
|
||||
}
|
||||
}
|
||||
|
||||
private const int MaxCnt = 200;
|
||||
|
||||
private int PackCnt;
|
||||
|
||||
//此处可以优化
|
||||
public void Handle()
|
||||
{
|
||||
PackCnt = packes.Count;
|
||||
if (PackCnt > MaxCnt)
|
||||
{
|
||||
Debug.ImportantLog($"单帧处理消息过多! {packes.Count}");
|
||||
PackCnt = MaxCnt;
|
||||
//进行统计
|
||||
}
|
||||
|
||||
while (PackCnt -- > 0)
|
||||
{
|
||||
if (!packes.TryDequeue(out GamePacket pack))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!listeners.TryGetValue(pack.OpCode, out var handle))
|
||||
{
|
||||
Debug.Error($"这个客户端消息没有处理器! {pack.OpCode}");
|
||||
pack.Dispose();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsOrderMessage)
|
||||
{
|
||||
_ = SafeInvokeOrderMessage(pack, handle);
|
||||
// //同用户的消息顺序才需要等待上一个包的完成
|
||||
// using (await coroutineLockManager.Wait(CoroutineLockType.ClientMessage, pack.Session.Id))
|
||||
// {
|
||||
// await SafeInvoke(pack, handle);
|
||||
// }
|
||||
}
|
||||
else
|
||||
{
|
||||
_ = SafeInvoke(pack, handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按玩家的顺序来处理
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task SafeInvokeOrderMessage(GamePacket packet, MessageDispatcherDelegate handler)
|
||||
{
|
||||
using (await coroutineLockManager.Wait(CoroutineLockType.ClientMessage, packet.Session.InstanceId))
|
||||
{
|
||||
await SafeInvoke(packet, handler);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SafeInvoke(GamePacket packet, MessageDispatcherDelegate handler)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (packet.Session.IsDisConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long key = Debug.StartTiming();
|
||||
await handler(packet);
|
||||
double _runtime = Debug.GetRunTime(key);
|
||||
if (_runtime > 1000f)
|
||||
{
|
||||
Debug.Warning($"消息执行处理慢:{packet.OpCode} {_runtime}");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Error($"消息处理异常:{packet.OpCode} {e.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
await PacketDispose(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task PacketDispose(GamePacket packet)
|
||||
{
|
||||
try
|
||||
{
|
||||
await packet.Wait();
|
||||
}
|
||||
finally
|
||||
{
|
||||
packet.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
49
GameNetModule/GameNet/Base/BaseWChannel.cs
Normal file
49
GameNetModule/GameNet/Base/BaseWChannel.cs
Normal file
@ -0,0 +1,49 @@
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Server;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public abstract class BaseWChannel : WebSocketBehavior
|
||||
{
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get { return ReadyState == WebSocketState.Open; }
|
||||
}
|
||||
|
||||
public string RemoteEndPointIP {
|
||||
get
|
||||
{
|
||||
string ipAddress = null;
|
||||
if (this.Headers["X-Forwarded-For"] != null)
|
||||
{
|
||||
ipAddress = this.Headers["X-Forwarded-For"];
|
||||
}else if (this.Headers["X-Real-IP"] != null)
|
||||
{
|
||||
ipAddress = this.Headers["X-Real-IP"];
|
||||
}
|
||||
|
||||
if (ipAddress == null)
|
||||
{
|
||||
return this.UserEndPoint.Address.ToString();
|
||||
}
|
||||
|
||||
if (ipAddress.IndexOf(',') >= 0)
|
||||
{
|
||||
string[] ss = ipAddress.Split(',');
|
||||
ipAddress = ss[0];
|
||||
}
|
||||
|
||||
return ipAddress.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
public void BeginDisconnect()
|
||||
{
|
||||
Task.Factory.StartNew(Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
262
GameNetModule/GameNet/Base/GameNetManager.cs
Normal file
262
GameNetModule/GameNet/Base/GameNetManager.cs
Normal file
@ -0,0 +1,262 @@
|
||||
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 GameNetManager : SingleInstance<GameNetManager>, 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;
|
||||
|
||||
public event Action<Session> ConnectedEvt;
|
||||
|
||||
public event Action<Session> DisConnectedEvt;
|
||||
|
||||
public event Action<Session, byte[]> ReceiveEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 启动网络
|
||||
/// </summary>
|
||||
public void Start(string webPath, int webPort)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(webPath) && webPort > 0)
|
||||
{
|
||||
//webSocket 服务 启动
|
||||
GameWChannel.OnConnectedEvt += WConnected;
|
||||
GameWChannel.OnDisconnectedEvt += WDisconnected;
|
||||
GameWChannel.OnReceivedEvt += WReceived;
|
||||
GameWChannel.OnErrorEvt += WError;
|
||||
_webSocketServer = new WebSocketServer(webPort);
|
||||
_webSocketServer.AddWebSocketService<GameWChannel>(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(GameWChannel 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);
|
||||
try
|
||||
{
|
||||
ConnectedEvt?.Invoke(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Error($"链接成功事件执行报错:{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void WDisconnected(GameWChannel channel)
|
||||
{
|
||||
long sessionId = channel.SessionId;
|
||||
if (sessionDic.TryRemove(sessionId, out Session session))
|
||||
{
|
||||
lock (session.DisConnectLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisConnectedEvt?.Invoke(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Error($"断开连接事件执行报错:{e.Message}");
|
||||
}
|
||||
|
||||
session.OnDisConnected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_forceDisConnectedSessionIds.TryRemove(sessionId, out Session _))
|
||||
{
|
||||
Debug.Error("webSocket断开连接,竟然未找到 session");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<long,Session> _forceDisConnectedSessionIds = new ConcurrentDictionary<long, Session>();
|
||||
|
||||
public void ForceDisconnected(long sessionId)
|
||||
{
|
||||
if (sessionDic.TryRemove(sessionId, out Session session))
|
||||
{
|
||||
if (!_forceDisConnectedSessionIds.TryAdd(sessionId, session))
|
||||
{
|
||||
Debug.Error("不存在说添加不进去吧???");
|
||||
}
|
||||
|
||||
lock (session.DisConnectLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
DisConnectedEvt?.Invoke(session);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Error($"断开连接事件执行报错:{e.Message}");
|
||||
}
|
||||
|
||||
session.OnDisConnected();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Error($"ForceDisconnected 断开连接,竟然未找到 session {sessionId}");
|
||||
}
|
||||
}
|
||||
|
||||
private void WReceived(GameWChannel 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(GameWChannel channel, Exception e)
|
||||
{
|
||||
if (e == null)
|
||||
{
|
||||
Debug.Error($"Web Socket Error: e is null!");
|
||||
}else
|
||||
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
|
||||
{
|
||||
ReceiveEvt?.Invoke(session, data);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Error($"收包解析报错:{e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// 清理超时,和失效的链接
|
||||
long timeNow = TimeInfo.Instance.FrameTime;
|
||||
const int MaxCheckNum = 10;
|
||||
int n = this.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
91
GameNetModule/GameNet/Base/GameWChannel.cs
Normal file
91
GameNetModule/GameNet/Base/GameWChannel.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Server;
|
||||
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public class GameWChannel : BaseWChannel
|
||||
{
|
||||
public long SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 链接成功事件
|
||||
/// </summary>
|
||||
public static event Action<GameWChannel> OnConnectedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 收到包事件
|
||||
/// </summary>
|
||||
public static event Action<GameWChannel, byte[]> OnReceivedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接事件
|
||||
/// </summary>
|
||||
public static event Action<GameWChannel> OnDisconnectedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 发送错误事件
|
||||
/// </summary>
|
||||
public static event Action<GameWChannel, Exception> OnErrorEvt;
|
||||
|
||||
|
||||
//未收齐的字节,写入内存字节
|
||||
private MemoryStream _memoryStream = new MemoryStream();
|
||||
|
||||
private bool waitReceiveHead = true;
|
||||
private int waitReceiveLength;
|
||||
|
||||
public NameValueCollection WHeaders
|
||||
{
|
||||
get { return this.Headers; }
|
||||
}
|
||||
|
||||
|
||||
protected override void OnMessage(MessageEventArgs e)
|
||||
{
|
||||
base.OnMessage(e);
|
||||
|
||||
// 收到包了!
|
||||
lock (_memoryStream)
|
||||
{
|
||||
_memoryStream.Write(e.RawData, 0, e.RawData.Length);
|
||||
_memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
//Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}");
|
||||
long byteLength = _memoryStream.Length - _memoryStream.Position;
|
||||
|
||||
byte[] bodyData = new byte[byteLength];
|
||||
_memoryStream.Read(bodyData, 0, (int)byteLength);
|
||||
//Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}");
|
||||
waitReceiveHead = true;
|
||||
OnReceivedEvt?.Invoke(this, bodyData);
|
||||
|
||||
_memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
_memoryStream.SetLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClose(CloseEventArgs e)
|
||||
{
|
||||
base.OnClose(e);
|
||||
OnDisconnectedEvt?.Invoke(this);
|
||||
}
|
||||
|
||||
protected override void OnError(ErrorEventArgs e)
|
||||
{
|
||||
base.OnError(e);
|
||||
OnErrorEvt?.Invoke(this, e.Exception);
|
||||
}
|
||||
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
OnConnectedEvt?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
GameNetModule/GameNet/Base/HttpNetManager.cs
Normal file
64
GameNetModule/GameNet/Base/HttpNetManager.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MrWu;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
using WebSocketSharp.Net;
|
||||
using WebSocketSharp.Server;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public class HttpNetManager<T> : SingleInstance<T>,IDisposable where T : SingleInstance,new()
|
||||
{
|
||||
protected HttpServer _httpServer;
|
||||
|
||||
public virtual void Start(int port,string docPath = null)
|
||||
{
|
||||
if (_httpServer != null)
|
||||
{
|
||||
Debug.Warning("HttpServer is not null!");
|
||||
return;
|
||||
}
|
||||
|
||||
_httpServer = new HttpServer($"http://0.0.0.0:{port}");
|
||||
if (!string.IsNullOrEmpty(docPath))
|
||||
{
|
||||
_httpServer.DocumentRootPath = docPath;
|
||||
}
|
||||
|
||||
_httpServer.OnPost += OnPost;
|
||||
_httpServer.OnGet += OnGet;
|
||||
_httpServer.Start();
|
||||
}
|
||||
|
||||
protected virtual void OnGet(object sender,HttpRequestEventArgs args)
|
||||
{
|
||||
Debug.Log("OnGet---");
|
||||
var res = args.Response;
|
||||
|
||||
res.StatusCode = (int)HttpStatusCode.NotModified;
|
||||
}
|
||||
|
||||
protected virtual void OnPost(object sender, HttpRequestEventArgs args)
|
||||
{
|
||||
Debug.Log("OnPost---");
|
||||
var res = args.Response;
|
||||
|
||||
res.StatusCode = (int)HttpStatusCode.NotModified;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
_httpServer.Stop();
|
||||
}
|
||||
|
||||
protected string ReadRequest(HttpListenerRequest request)
|
||||
{
|
||||
using (StreamReader streamReader = new StreamReader(request.InputStream))
|
||||
{
|
||||
return streamReader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
GameNetModule/GameNet/Base/IUserSession.cs
Normal file
46
GameNetModule/GameNet/Base/IUserSession.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using GameMessage;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public interface IUserSession
|
||||
{
|
||||
long Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 实例ID
|
||||
/// </summary>
|
||||
long InstanceId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否断开连接
|
||||
/// </summary>
|
||||
bool IsDisConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录验证码
|
||||
/// </summary>
|
||||
string VerificationCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户数据
|
||||
/// </summary>
|
||||
SessionUserData UserData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 远端ip地址
|
||||
/// </summary>
|
||||
string RemoteIPAddress
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
void Send(byte[] buffer, Type messageType);
|
||||
|
||||
void Send(MessageData messageData);
|
||||
|
||||
void BindUser(int userid, string uuid, int clientVer, int plat,string deviceInfo,bool isNewUser,string storeType, bool reset = false);
|
||||
|
||||
void DisConnected();
|
||||
}
|
||||
}
|
||||
218
GameNetModule/GameNet/Base/NetManager.cs
Normal file
218
GameNetModule/GameNet/Base/NetManager.cs
Normal file
@ -0,0 +1,218 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
GameNetModule/GameNet/Base/PacketUtility.cs
Normal file
129
GameNetModule/GameNet/Base/PacketUtility.cs
Normal file
@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using GameMessage;
|
||||
using K4os.Compression.LZ4;
|
||||
using MessagePack;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public static class PacketUtility
|
||||
{
|
||||
private static int MaxSendLength = 0;
|
||||
|
||||
private const int LogMinLength = 1024 * 100;
|
||||
|
||||
public static int MessageParseDataV1(byte[] data, Type messageType, MemoryStream memoryStream)
|
||||
{
|
||||
int opcode = MessageManager.GetOpcode(messageType);
|
||||
memoryStream.Seek(5, SeekOrigin.Begin);
|
||||
//写入数据
|
||||
memoryStream.Write(data,0,data.Length);
|
||||
|
||||
int length = (int)memoryStream.Position;
|
||||
memoryStream.GetBuffer()[0] = 0; //设置为不压缩
|
||||
memoryStream.GetBuffer().WriteTo(1,opcode);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return length;
|
||||
}
|
||||
|
||||
public static int MessageParseDataV1(MessageData messageData,MemoryStream memoryStream,MemoryStream compressStream)
|
||||
{
|
||||
Type messageType = messageData.GetType();
|
||||
int opcode = MessageManager.GetOpcode(messageType);
|
||||
|
||||
compressStream.Seek(0, SeekOrigin.Begin);
|
||||
MessagePackSerializer.Serialize(messageType,compressStream, messageData);
|
||||
|
||||
memoryStream.Seek(5, SeekOrigin.Begin);
|
||||
byte compressFlag = 0;
|
||||
if (compressStream.Position < 2048) //2k 一下不压缩
|
||||
{
|
||||
memoryStream.Write(compressStream.GetBuffer(),0,(int)compressStream.Position); //把包内容全部写入
|
||||
}
|
||||
else
|
||||
{
|
||||
//压缩处理
|
||||
byte[] compressData = LZ4Pickler.Pickle(compressStream.GetBuffer(), 0, (int)compressStream.Position);
|
||||
memoryStream.WriteInt((int)compressStream.Position); //写入压缩前的长度
|
||||
memoryStream.Write(compressData,0,compressData.Length);
|
||||
compressFlag = 1;
|
||||
//Debug.Info($"得到数据:{compressData.Length} {compressData[0]} {compressData[compressData.Length - 1]}");
|
||||
}
|
||||
|
||||
int length = (int)memoryStream.Position;
|
||||
memoryStream.GetBuffer()[0] = compressFlag;
|
||||
memoryStream.GetBuffer().WriteTo(1,opcode);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
return length;
|
||||
}
|
||||
|
||||
public static byte[] MessageParseData(Type messageType,byte[] data)
|
||||
{
|
||||
int opcode = MessageManager.GetOpcode(messageType);
|
||||
int len = 4 + 4 + data.Length;
|
||||
|
||||
//包长度
|
||||
byte[] lenData = BitConverter.GetBytes(len);
|
||||
//包类型
|
||||
byte[] opcodeData = BitConverter.GetBytes(opcode);
|
||||
//备用 4个字节
|
||||
byte[] bakData = BitConverter.GetBytes(0);
|
||||
//发送的数据
|
||||
byte[] sendData = new byte[len + 4];
|
||||
|
||||
if (len > MaxSendLength)
|
||||
{
|
||||
MaxSendLength = len;
|
||||
if (MaxSendLength > LogMinLength)
|
||||
{
|
||||
Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Buffer.BlockCopy(lenData, 0, sendData, 0, 4);
|
||||
Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4);
|
||||
Buffer.BlockCopy(bakData, 0, sendData, 8, 4);
|
||||
Buffer.BlockCopy(data, 0, sendData, 12, data.Length);
|
||||
|
||||
return sendData;
|
||||
}
|
||||
|
||||
public static byte[] MessageParseData(MessageData messageData)
|
||||
{
|
||||
Type messageType = messageData.GetType();
|
||||
int opcode = MessageManager.GetOpcode(messageType);
|
||||
byte[] data = MessagePackSerializer.Serialize(messageType, messageData);
|
||||
int len = 4 + 4 + data.Length;
|
||||
|
||||
//包长度
|
||||
byte[] lenData = BitConverter.GetBytes(len);
|
||||
//包类型
|
||||
byte[] opcodeData = BitConverter.GetBytes(opcode);
|
||||
//备用 4个字节
|
||||
byte[] bakData = BitConverter.GetBytes(0);
|
||||
//发送的数据
|
||||
byte[] sendData = new byte[len + 4];
|
||||
|
||||
if (len > MaxSendLength)
|
||||
{
|
||||
MaxSendLength = len;
|
||||
if (MaxSendLength > LogMinLength)
|
||||
{
|
||||
Debug.ImportantLog($"当前最大发送数据长度:{MaxSendLength},opcode:{opcode}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Buffer.BlockCopy(lenData, 0, sendData, 0, 4);
|
||||
Buffer.BlockCopy(opcodeData, 0, sendData, 4, 4);
|
||||
Buffer.BlockCopy(bakData, 0, sendData, 8, 4);
|
||||
Buffer.BlockCopy(data, 0, sendData, 12, data.Length);
|
||||
|
||||
return sendData;
|
||||
}
|
||||
}
|
||||
}
|
||||
167
GameNetModule/GameNet/Base/ProxySession.cs
Normal file
167
GameNetModule/GameNet/Base/ProxySession.cs
Normal file
@ -0,0 +1,167 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using GameDAL.Game;
|
||||
using GameMessage;
|
||||
using Server.Core;
|
||||
using Debug = MrWu.Debug.Debug;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 用在这个代理Session的原因是 源Session可以被复用
|
||||
/// </summary>
|
||||
public class ProxySession : Reference, IUserSession
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否已经被释放
|
||||
/// </summary>
|
||||
private bool IsDisposed { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实例Id
|
||||
/// </summary>
|
||||
public long InstanceId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 注意这个ID 与 普通 Session 会有相同的情况
|
||||
/// </summary>
|
||||
public long Id
|
||||
{
|
||||
get { return SourceSession.Id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否断开连接
|
||||
/// </summary>
|
||||
public bool IsDisConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
//Debug.Info($"IsDisConnected: {SourceSession == null} {InstanceId} {SourceSession?.InstanceId}");
|
||||
return SourceSession == null || SourceSession.IsDisConnected || InstanceId != SourceSession.InstanceId;
|
||||
}
|
||||
}
|
||||
|
||||
private SessionUserData _userData;
|
||||
|
||||
public SessionUserData UserData {
|
||||
get
|
||||
{
|
||||
CheckDisposed();
|
||||
return _userData;
|
||||
}
|
||||
private set
|
||||
{
|
||||
CheckDisposed();
|
||||
_userData = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string RemoteIPAddress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录验证码
|
||||
/// </summary>
|
||||
public string VerificationCode
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SourceSession == null)
|
||||
{
|
||||
Debug.Fatal("VerificationCode Get Error, SourceSession is null!");
|
||||
return null;
|
||||
}
|
||||
return SourceSession.VerificationCode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (SourceSession == null)
|
||||
{
|
||||
Debug.Fatal("VerificationCode Set Error, SourceSession is null!");
|
||||
return;
|
||||
}
|
||||
SourceSession.VerificationCode = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 源会话 -- 使用者不能拿这个用,因为这个是对象池中的,随时会变
|
||||
/// </summary>
|
||||
public UserSession SourceSession { get; private set; }
|
||||
|
||||
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false)
|
||||
{
|
||||
if (!IsDisConnected)
|
||||
{
|
||||
SourceSession.BindUser(userid, uuid, clientVer, plat, deviceInfo, isNewUser,storeType, reset);
|
||||
UserData = SourceSession.UserData;
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Send(byte[] buffer, Type messageType)
|
||||
{
|
||||
if (!IsDisConnected)
|
||||
{
|
||||
SourceSession.Send(buffer, messageType);
|
||||
}
|
||||
}
|
||||
|
||||
public void Send(MessageData messageData)
|
||||
{
|
||||
//Debug.Info($"发送消息:{IsDisConnected}");
|
||||
if (!IsDisConnected)
|
||||
{
|
||||
SourceSession.Send(messageData);
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
}
|
||||
|
||||
public static ProxySession Create(UserSession session)
|
||||
{
|
||||
ProxySession proxySession = ReferencePool.Fetch<ProxySession>();
|
||||
proxySession.IsDisposed = false;
|
||||
proxySession.InstanceId = session.InstanceId;
|
||||
proxySession.UserData = session.UserData;
|
||||
proxySession.RemoteIPAddress = session.RemoteIPAddress;
|
||||
proxySession.SourceSession = session;
|
||||
return proxySession;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (this.IsFromPool)
|
||||
{
|
||||
this.SourceSession = null;
|
||||
this.UserData = null;
|
||||
ReferencePool.Recycle(this);
|
||||
this.IsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void DisConnected()
|
||||
{
|
||||
if (!IsDisConnected)
|
||||
{
|
||||
SourceSession.DisConnected();
|
||||
}
|
||||
|
||||
CheckDisposed();
|
||||
}
|
||||
|
||||
private void CheckDisposed()
|
||||
{
|
||||
if (this.IsDisposed)
|
||||
{
|
||||
//打印堆栈信息
|
||||
StackTrace st = new StackTrace();
|
||||
Debug.Fatal($"ProxySession is Disposed! {st.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
186
GameNetModule/GameNet/Base/Session.cs
Normal file
186
GameNetModule/GameNet/Base/Session.cs
Normal file
@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using GameMessage;
|
||||
using MessagePack;
|
||||
using MrWu;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
using Server.Core.Extend;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话
|
||||
/// </summary>
|
||||
public class Session : IUserSession
|
||||
{
|
||||
/// <summary>
|
||||
/// 唯一id
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
public long InstanceId {
|
||||
get
|
||||
{
|
||||
return Id;
|
||||
}
|
||||
}
|
||||
|
||||
public BaseWChannel WChannel { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 读写时间
|
||||
/// </summary>
|
||||
public long ReadWriteTime;
|
||||
|
||||
/// <summary>
|
||||
/// 登录验证码
|
||||
/// </summary>
|
||||
public string VerificationCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否已经断开连接
|
||||
/// </summary>
|
||||
public bool IsDisConnected { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绑定的用户数据 -- 老的用户数据
|
||||
/// </summary>
|
||||
public SessionUserData UserData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 断线锁
|
||||
/// </summary>
|
||||
public readonly object DisConnectLock = new object();
|
||||
|
||||
public string RemoteIPAddress
|
||||
{
|
||||
get { return WChannel.RemoteEndPointIP; }
|
||||
}
|
||||
|
||||
public Session(BaseWChannel wChannel)
|
||||
{
|
||||
this.WChannel = wChannel;
|
||||
Id = NetServices.CreateAcceptChannelId();
|
||||
}
|
||||
|
||||
public virtual void Send(MessageData messageData)
|
||||
{
|
||||
if (messageData == null)
|
||||
{
|
||||
Debug.Error("不能发送空数据!");
|
||||
return;
|
||||
}
|
||||
byte[] data = PacketUtility.MessageParseData(messageData);
|
||||
Send(data);
|
||||
}
|
||||
|
||||
public void Send(byte[] data,Type messageType)
|
||||
{
|
||||
data = PacketUtility.MessageParseData(messageType, data);
|
||||
Send(data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
public virtual void Send(byte[] data)
|
||||
{
|
||||
if (IsDisConnected)
|
||||
{
|
||||
Debug.Info("发送失败,链接已经断开!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WChannel.Active)
|
||||
{
|
||||
Debug.Info("链接已经断开,发送失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime);
|
||||
// 组件 data 数据
|
||||
// 分类发送
|
||||
WChannel.Send(data);
|
||||
}
|
||||
|
||||
public virtual void Send(string message)
|
||||
{
|
||||
if (IsDisConnected)
|
||||
{
|
||||
Debug.Info("发送失败,链接已经断开!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WChannel.Active)
|
||||
{
|
||||
Debug.Info("链接已经断开,发送失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Exchange(ref ReadWriteTime, TimeInfo.Instance.FrameTime);
|
||||
WChannel.Send(message);
|
||||
}
|
||||
|
||||
public void DisConnected()
|
||||
{
|
||||
WChannel.BeginDisconnect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绑定用户
|
||||
/// </summary>
|
||||
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo, bool isNewUser,string storeType, bool reset = false)
|
||||
{
|
||||
if (IsDisConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reset && UserData != null)
|
||||
{
|
||||
Debug.Error("该会话已经绑定了用户数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
ClearUserData();
|
||||
|
||||
UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType);
|
||||
SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData);
|
||||
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this);
|
||||
}
|
||||
|
||||
public void ClearUserData()
|
||||
{
|
||||
if (UserData != null)
|
||||
{
|
||||
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this);
|
||||
SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData);
|
||||
UserData = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接事件
|
||||
/// </summary>
|
||||
public virtual void OnDisConnected()
|
||||
{
|
||||
if (IsDisConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsDisConnected = true;
|
||||
GameNetDispatcher.Instance.DispatchNextFrame(GameNetMsgId.SessionDisConnected, this);
|
||||
ClearUserData();
|
||||
Clear();
|
||||
}
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
92
GameNetModule/GameNet/Base/SessionUserData.cs
Normal file
92
GameNetModule/GameNet/Base/SessionUserData.cs
Normal file
@ -0,0 +1,92 @@
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话用户数据 -- 这个还不能使用线程池,找不准释放的机会
|
||||
/// </summary>
|
||||
public class SessionUserData
|
||||
{
|
||||
/// <summary>
|
||||
/// 玩家的Userid;
|
||||
/// </summary>
|
||||
public int Userid
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 会话Uuid
|
||||
/// </summary>
|
||||
public string Uuid
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户端版本
|
||||
/// </summary>
|
||||
public int ClientVer
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 0桌面模式 1安卓平台 2苹果平台 3小游戏 4WebGl
|
||||
/// </summary>
|
||||
public int Plat
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备信息
|
||||
/// </summary>
|
||||
public string DeviceInfo
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否是新用户
|
||||
/// </summary>
|
||||
public bool IsNewUser
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 商店类型
|
||||
/// </summary>
|
||||
public string StoreType
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static SessionUserData Create(int userid,string uuid,int clientVer,int plat,string deviceInfo,bool isNewUser,string storeType)
|
||||
{
|
||||
SessionUserData data = new SessionUserData();
|
||||
data.Userid = userid;
|
||||
data.Uuid = uuid;
|
||||
data.ClientVer = clientVer;
|
||||
data.Plat = plat;
|
||||
data.DeviceInfo = deviceInfo;
|
||||
data.IsNewUser = isNewUser;
|
||||
data.StoreType = storeType;
|
||||
return data;
|
||||
}
|
||||
|
||||
// public override void Dispose()
|
||||
// {
|
||||
// Uuid = null;
|
||||
// DeviceInfo = null;
|
||||
// }
|
||||
}
|
||||
}
|
||||
68
GameNetModule/GameNet/Base/TService.cs
Normal file
68
GameNetModule/GameNet/Base/TService.cs
Normal file
@ -0,0 +1,68 @@
|
||||
// using System;
|
||||
// using System.Collections.Concurrent;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Linq;
|
||||
// using System.Net;
|
||||
// using System.Net.Sockets;
|
||||
// using MrWu.Debug;
|
||||
// using Sodao.FastSocket.Server;
|
||||
// using Sodao.FastSocket.Server.Messaging;
|
||||
// using Sodao.FastSocket.SocketBase;
|
||||
//
|
||||
// namespace Server.Net
|
||||
// {
|
||||
// public sealed class TService : AbsSocketService<ThriftMessage>
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 链接成功事件
|
||||
// /// </summary>
|
||||
// public event Action<IConnection> OnConnectedEvt;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 收到包事件
|
||||
// /// </summary>
|
||||
// public event Action<IConnection, byte[]> OnReceivedEvt;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 断开连接事件
|
||||
// /// </summary>
|
||||
// public event Action<IConnection> OnDisconnectedEvt;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 发送错误事件
|
||||
// /// </summary>
|
||||
// public event Action<IConnection, Exception> OnErrorEvt;
|
||||
//
|
||||
// public override void OnConnected(IConnection connection)
|
||||
// {
|
||||
// base.OnConnected(connection);
|
||||
// connection.BeginReceive();
|
||||
//
|
||||
// OnConnectedEvt?.Invoke(connection);
|
||||
// }
|
||||
//
|
||||
// public override void OnDisconnected(IConnection connection, Exception ex)
|
||||
// {
|
||||
// base.OnDisconnected(connection, ex);
|
||||
// OnDisconnectedEvt?.Invoke(connection);
|
||||
// }
|
||||
//
|
||||
// public override void OnException(IConnection connection, Exception ex)
|
||||
// {
|
||||
// base.OnException(connection, ex);
|
||||
// OnErrorEvt?.Invoke(connection,ex);
|
||||
// }
|
||||
//
|
||||
// public override void OnReceived(IConnection connection, ThriftMessage message)
|
||||
// {
|
||||
// base.OnReceived(connection, message);
|
||||
// OnReceivedEvt?.Invoke(connection,message.Payload);
|
||||
// }
|
||||
//
|
||||
// public override void OnSendCallback(IConnection connection, Sodao.FastSocket.SocketBase.Packet packet, bool isSuccess)
|
||||
// {
|
||||
// base.OnSendCallback(connection, packet, isSuccess);
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// }
|
||||
226
GameNetModule/GameNet/Base/UserSession.cs
Normal file
226
GameNetModule/GameNet/Base/UserSession.cs
Normal file
@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using ActorCore;
|
||||
using GameMessage;
|
||||
using MessagePack.Formatters;
|
||||
using NetWorkMessage;
|
||||
using Server.Core.Extend;
|
||||
using Debug = MrWu.Debug.Debug;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 这个是路由过来的用户Session用来做对象池
|
||||
/// </summary>
|
||||
public class UserSession : IUserSession
|
||||
{
|
||||
/// <summary>
|
||||
/// 实例Id
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实例ID 如果Id 改变就不能用了,不再是之前的用户
|
||||
/// </summary>
|
||||
public long InstanceId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录验证码
|
||||
/// </summary>
|
||||
public string VerificationCode { get; set; }
|
||||
|
||||
public string RemoteIPAddress { get; set; }
|
||||
|
||||
public bool IsDisConnected { get; set; }
|
||||
|
||||
public SessionUserData UserData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// UserData 设置的时候需要锁
|
||||
/// </summary>
|
||||
public readonly object DisConnectLock = new object();
|
||||
|
||||
// 收到链接包 -> State = Using
|
||||
// 收到断开包 然后就回复断开成功的包 Session设置为断开
|
||||
|
||||
//UserSession 被 ProxySession 包装使用, 代理持有UserSession 那么RefCount + 1 当ProxySession 销毁那么RefCount - 1
|
||||
|
||||
public UserSession(long id, long instanceId)
|
||||
{
|
||||
this.Id = id;
|
||||
this.InstanceId = instanceId;
|
||||
}
|
||||
|
||||
public void BindUser(int userid, string uuid, int clientVer, int plat, string deviceInfo,bool isNewUser,string storeType, bool reset = false)
|
||||
{
|
||||
if (IsDisConnected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reset && UserData != null)
|
||||
{
|
||||
Debug.Error("该会话已经绑定了用户数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
ClearUserData();
|
||||
|
||||
UserData = SessionUserData.Create(userid, uuid, clientVer, plat, deviceInfo,isNewUser, storeType);
|
||||
SessionUUIDManager.Instance.AddSessionUUID(uuid, UserData);
|
||||
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionBindUser, this);
|
||||
}
|
||||
|
||||
private const int WarningCnt = 30 * 8 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据给客户端
|
||||
/// </summary>
|
||||
/// <param name="buffer"></param>
|
||||
/// <param name="messageType"></param>
|
||||
public void Send(byte[] buffer, Type messageType)
|
||||
{
|
||||
if (buffer == null)
|
||||
{
|
||||
Debug.Error("不能发送空数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDisConnected)
|
||||
{
|
||||
Debug.Info($"链接并未在使用,不能发送! {Id} {this.InstanceId}");
|
||||
return;
|
||||
}
|
||||
|
||||
//发送给客户端
|
||||
Server2ClientMessage message = Server2ClientMessage.Create();
|
||||
int len = PacketUtility.MessageParseDataV1(buffer, messageType, message.MemoryStream);
|
||||
byte[] data = new byte[len];
|
||||
int count = message.MemoryStream.Read(data, 0, len);
|
||||
if (count != len)
|
||||
{
|
||||
Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}");
|
||||
}
|
||||
|
||||
if (buffer.Length > WarningCnt)
|
||||
{
|
||||
Debug.ImportantLog($"还有包大于240K? {messageType}");
|
||||
}
|
||||
|
||||
message.Data = data;
|
||||
Send2Router(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 这个只能用来发送给客户端
|
||||
/// </summary>
|
||||
/// <param name="messageData"></param>
|
||||
public void Send(MessageData messageData)
|
||||
{
|
||||
if (messageData == null)
|
||||
{
|
||||
Debug.Error("不能发送空数据!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsDisConnected)
|
||||
{
|
||||
Debug.Info($"链接并未在使用,不能发送! - {Id} {this.InstanceId}");
|
||||
return;
|
||||
}
|
||||
|
||||
//发送给客户端
|
||||
Server2ClientMessage message = Server2ClientMessage.Create();
|
||||
int len = PacketUtility.MessageParseDataV1(messageData, message.MemoryStream, message.CompressStream);
|
||||
byte[] data = new byte[len];
|
||||
int count = message.MemoryStream.Read(data, 0, len);
|
||||
if (count != len)
|
||||
{
|
||||
Debug.Error($"怎么会读取的数据跟要读取的长度不一致! {len} {count}");
|
||||
}
|
||||
|
||||
//Debug.Info($"压缩标记:{data[0]},opcode:{BitConverter.ToInt32(data,1)}");
|
||||
message.Data = data;
|
||||
Send2Router(message);
|
||||
}
|
||||
|
||||
private IActor MainActor;
|
||||
|
||||
private IActor UerNetActor;
|
||||
|
||||
private void GetActor()
|
||||
{
|
||||
if (MainActor == null)
|
||||
{
|
||||
MainActor = ActorManager.Instance.Get(ActorTypeId.Main);
|
||||
}
|
||||
|
||||
if (UerNetActor == null)
|
||||
{
|
||||
UerNetActor = ActorManager.Instance.Get(ActorTypeId.UserNet);
|
||||
}
|
||||
}
|
||||
|
||||
private void Send2Router(MessageObject message)
|
||||
{
|
||||
GetActor();
|
||||
|
||||
UserNetSendMessage userNetSendMessage = new UserNetSendMessage();
|
||||
userNetSendMessage.Id = Id;
|
||||
userNetSendMessage.InstanceId = InstanceId;
|
||||
userNetSendMessage.MessageObject = message;
|
||||
MainActor.Send(UerNetActor.ActorId,userNetSendMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接
|
||||
/// </summary>
|
||||
public void DisConnected()
|
||||
{
|
||||
GetActor();
|
||||
|
||||
Debug.Info("UserSession 强制断开链接");
|
||||
UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage();
|
||||
userNetForceFinMessage.Id = Id;
|
||||
userNetForceFinMessage.InstanceId = InstanceId;
|
||||
userNetForceFinMessage.FinCode = 0;
|
||||
MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage);
|
||||
|
||||
//提前处理用户断开
|
||||
UserSessionManager.Instance.UserFin(this);
|
||||
}
|
||||
|
||||
public void ClearUserData()
|
||||
{
|
||||
if (UserData != null)
|
||||
{
|
||||
Debug.Info($"注销绑定用户数据 Session:{Id} {UserData.Userid}");
|
||||
GameNetDispatcher.Instance.Dispatch(GameNetMsgId.SessionUnBindUser, this);
|
||||
SessionUUIDManager.Instance.RemoveLocalUUID(UserData.Uuid, UserData);
|
||||
UserData = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 强制下线,强制下线也要等待1帧,因为线程不一样
|
||||
/// </summary>
|
||||
/// <param name="finCode"></param>
|
||||
/// <param name="userId"></param>
|
||||
public void ForceDiscConnect(int finCode,int userId)
|
||||
{
|
||||
GetActor();
|
||||
|
||||
UserNetForceFinMessage userNetForceFinMessage = new UserNetForceFinMessage();
|
||||
userNetForceFinMessage.Id = Id;
|
||||
userNetForceFinMessage.InstanceId = InstanceId;
|
||||
userNetForceFinMessage.FinCode = finCode;
|
||||
MainActor.Send(UerNetActor.ActorId,userNetForceFinMessage);
|
||||
|
||||
//提前处理用户断开
|
||||
UserSessionManager.Instance.UserFin(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
15
GameNetModule/GameNet/Base/UserSessionExt.cs
Normal file
15
GameNetModule/GameNet/Base/UserSessionExt.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using GameMessage;
|
||||
using ObjectMessage;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
public static class UserSessionExt
|
||||
{
|
||||
public static void SendTipCode(this IUserSession session, int code)
|
||||
{
|
||||
TipCodeResponse tcResponse = new TipCodeResponse();
|
||||
tcResponse.SetCode(code);
|
||||
session.Send(tcResponse);
|
||||
}
|
||||
}
|
||||
}
|
||||
151
GameNetModule/GameNet/Base/WChannel.cs
Normal file
151
GameNetModule/GameNet/Base/WChannel.cs
Normal file
@ -0,0 +1,151 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using MrWu.Debug;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Server;
|
||||
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// WebSocket 单个链接的通道
|
||||
/// </summary>
|
||||
public class WChannel: BaseWChannel
|
||||
{
|
||||
public long SessionId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 链接成功事件
|
||||
/// </summary>
|
||||
public static event Action<WChannel> OnConnectedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 收到包事件
|
||||
/// </summary>
|
||||
public static event Action<WChannel, byte[]> OnReceivedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接事件
|
||||
/// </summary>
|
||||
public static event Action<WChannel> OnDisconnectedEvt;
|
||||
|
||||
/// <summary>
|
||||
/// 发送错误事件
|
||||
/// </summary>
|
||||
public static event Action<WChannel, Exception> OnErrorEvt;
|
||||
|
||||
//未收齐的字节,写入内存字节
|
||||
private MemoryStream _memoryStream = new MemoryStream();
|
||||
|
||||
private byte[] lengthBytes = new byte[4];
|
||||
private bool waitReceiveHead = true;
|
||||
private int waitReceiveLength;
|
||||
|
||||
public NameValueCollection WHeaders {
|
||||
get
|
||||
{
|
||||
return this.Headers;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnMessage(MessageEventArgs e)
|
||||
{
|
||||
base.OnMessage(e);
|
||||
|
||||
lock (_memoryStream)
|
||||
{
|
||||
_memoryStream.Write(e.RawData,0,e.RawData.Length);
|
||||
_memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
//Debug.Info($"OnMessage,{e.RawData.Length},{_memoryStream.Position},{_memoryStream.Length}");
|
||||
while (true)
|
||||
{
|
||||
long byteLength = _memoryStream.Length - _memoryStream.Position;
|
||||
|
||||
//Debug.Log("缓存区长度:" + _memoryStream.Length);
|
||||
if (waitReceiveHead)
|
||||
{
|
||||
// 没收到 包头
|
||||
if (byteLength < 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
//收到了包头
|
||||
_memoryStream.Read(lengthBytes, 0, 4);
|
||||
waitReceiveLength = BitConverter.ToInt32(lengthBytes, 0);
|
||||
waitReceiveHead = false;
|
||||
|
||||
if (waitReceiveLength > GameNetManager.MaxMessageSize)
|
||||
{
|
||||
Debug.Error("包体过大:" + waitReceiveLength);
|
||||
throw new Exception("Message Size :" + waitReceiveLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//还没有收齐,不处理
|
||||
if (byteLength < waitReceiveLength)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] bodyData = new byte[waitReceiveLength];
|
||||
_memoryStream.Read(bodyData, 0, waitReceiveLength);
|
||||
//Debug.Info($"收到包体:{_memoryStream.Position},{_memoryStream.Length},bodyLength:{waitReceiveLength}");
|
||||
waitReceiveHead = true;
|
||||
OnReceivedEvt?.Invoke(this, bodyData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//未读取的长度
|
||||
int noReadLength = (int)(_memoryStream.Length - _memoryStream.Position);
|
||||
//Debug.Info($"剩余长度:{noReadLength}");
|
||||
if (noReadLength > 0)
|
||||
{
|
||||
byte[] noReadBytes = new byte[noReadLength];
|
||||
_memoryStream.Read(noReadBytes, 0, noReadLength);
|
||||
_memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
_memoryStream.SetLength(0);
|
||||
_memoryStream.Write(noReadBytes, 0, noReadLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
_memoryStream.SetLength(0);
|
||||
}
|
||||
//Debug.Info($"重置收包:{_memoryStream.Position},{_memoryStream.Length}");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClose(CloseEventArgs e)
|
||||
{
|
||||
base.OnClose(e);
|
||||
OnDisconnectedEvt?.Invoke(this);
|
||||
}
|
||||
|
||||
protected override void OnError(ErrorEventArgs e)
|
||||
{
|
||||
base.OnError(e);
|
||||
OnErrorEvt?.Invoke(this, e.Exception);
|
||||
}
|
||||
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
OnConnectedEvt?.Invoke(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
103
GameNetModule/GameNet/GamePacket.cs
Normal file
103
GameNetModule/GameNet/GamePacket.cs
Normal file
@ -0,0 +1,103 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading.Tasks;
|
||||
using GameMessage;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
using Server.Core.Extend;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏包
|
||||
/// </summary>
|
||||
public class GamePacket : Reference
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息Code
|
||||
/// </summary>
|
||||
public int OpCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息数据
|
||||
/// </summary>
|
||||
public MessageData MessageData { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 谁发来的 - 包处理完会释放
|
||||
/// </summary>
|
||||
public IUserSession Session { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 任务
|
||||
/// </summary>
|
||||
private ConcurrentQueue<Task> Tasks = new ConcurrentQueue<Task>();
|
||||
|
||||
public static GamePacket Create(int opCode, MessageData messageData)
|
||||
{
|
||||
GamePacket packet = ReferencePool.Fetch<GamePacket>();
|
||||
packet.OpCode = opCode;
|
||||
packet.MessageData = messageData;
|
||||
|
||||
#if DEBUG
|
||||
if (!packet.Tasks.IsEmpty)
|
||||
{
|
||||
Debug.Fatal("Packet has task!");
|
||||
}
|
||||
#endif
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static GamePacket Create(int opCode, MessageData messageData,IUserSession session)
|
||||
{
|
||||
GamePacket packet = ReferencePool.Fetch<GamePacket>();
|
||||
packet.OpCode = opCode;
|
||||
packet.MessageData = messageData;
|
||||
packet.Session = session;
|
||||
|
||||
#if DEBUG
|
||||
if (!packet.Tasks.IsEmpty)
|
||||
{
|
||||
Debug.Fatal("Packet has task!");
|
||||
}
|
||||
#endif
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
public void BeginTask(Task task)
|
||||
{
|
||||
if (task == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Tasks.Enqueue(task);
|
||||
}
|
||||
|
||||
public Task Wait()
|
||||
{
|
||||
if (Tasks.IsEmpty)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
//等待所有任务完成
|
||||
return Task.WhenAll(Tasks);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (IsFromPool)
|
||||
{
|
||||
MessageData = null;
|
||||
if (Session is ProxySession proxySession)
|
||||
{
|
||||
proxySession.Dispose();
|
||||
}
|
||||
|
||||
Tasks.Clear();
|
||||
Session = null;
|
||||
ReferencePool.Recycle(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
67
GameNetModule/GameNetModule.csproj
Normal file
67
GameNetModule/GameNetModule.csproj
Normal file
@ -0,0 +1,67 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>GameNetModule</RootNamespace>
|
||||
<AssemblyName>GameNetModule</AssemblyName>
|
||||
<LangVersion>9.0</LangVersion>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="K4os.Compression.LZ4" />
|
||||
<PackageReference Include="MessagePack" />
|
||||
<PackageReference Include="MessagePack.Annotations" />
|
||||
<PackageReference Include="MessagePackAnalyzer" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
|
||||
<PackageReference Include="Microsoft.NET.StringTools" />
|
||||
<PackageReference Include="System.Buffers" />
|
||||
<PackageReference Include="System.Collections.Immutable" />
|
||||
<PackageReference Include="System.Memory" />
|
||||
<PackageReference Include="System.Numerics.Vectors" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" />
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="StackExchange.Redis">
|
||||
<HintPath>..\dll\StackExchange.Redis.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="websocket-sharp">
|
||||
<HintPath>..\dll_new\websocket-sharp.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
<ProjectReference Include="..\GameDAL\GameDAL.csproj" />
|
||||
<ProjectReference Include="..\MrWu\MrWu.csproj" />
|
||||
<ProjectReference Include="..\NetWorkMessage\NetWorkMessage.csproj" />
|
||||
<ProjectReference Include="..\ServerCore\ServerCore.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
68
GameNetModule/Manager/GameSessionManager.cs
Normal file
68
GameNetModule/Manager/GameSessionManager.cs
Normal file
@ -0,0 +1,68 @@
|
||||
using System.Collections.Concurrent;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
using Server.Net;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public class GameSessionManager : SingleInstance<GameSessionManager>
|
||||
{
|
||||
private readonly ConcurrentDictionary<int, IUserSession> _sessions = new ConcurrentDictionary<int, IUserSession>();
|
||||
|
||||
/// <summary>
|
||||
/// 如果已经存在,则不能添加
|
||||
/// </summary>
|
||||
/// <param name="userid"></param>
|
||||
/// <param name="session"></param>
|
||||
/// <returns></returns>
|
||||
public bool AddPlayerSession(int userid, IUserSession session)
|
||||
{
|
||||
Debug.Info($"GameSessionManager 添加玩家:{userid}");
|
||||
return _sessions.TryAdd(userid, session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果Session 不是同一个不能移除
|
||||
/// </summary>
|
||||
/// <param name="userid"></param>
|
||||
/// <param name="session"></param>
|
||||
/// <returns></returns>
|
||||
public bool RemovePlayerSession(int userid, IUserSession session)
|
||||
{
|
||||
Debug.Log($"移除玩家Session:{userid}");
|
||||
// 尝试获取 userid 对应的 Session
|
||||
if (_sessions.TryGetValue(userid, out IUserSession existingSession))
|
||||
{
|
||||
Debug.Log($"找到玩家的Session! {existingSession.GetType()} {session.GetType()}");
|
||||
// 检查获取到的 Session 是否与传入的 session 相同
|
||||
if (existingSession == session)
|
||||
{
|
||||
// 如果相同,则尝试移除
|
||||
Debug.Info($"GameSessionManager 移除玩家:{userid}");
|
||||
return _sessions.TryRemove(userid, out _);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果 Session 不相同或 userid 不存在,则返回 false
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找Session
|
||||
/// </summary>
|
||||
/// <param name="userid"></param>
|
||||
/// <returns></returns>
|
||||
public IUserSession FindSession(int userid)
|
||||
{
|
||||
// 尝试获取 userid 对应的 Session
|
||||
if (_sessions.TryGetValue(userid, out IUserSession session))
|
||||
{
|
||||
// 如果找到,返回该 Session
|
||||
return session;
|
||||
}
|
||||
|
||||
// 如果未找到,返回 null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
314
GameNetModule/Manager/SessionUUIDManager.cs
Normal file
314
GameNetModule/Manager/SessionUUIDManager.cs
Normal file
@ -0,0 +1,314 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using GameDAL.Game;
|
||||
using GameMessage;
|
||||
using MrWu;
|
||||
using MrWu.Debug;
|
||||
using ObjectMessage;
|
||||
using Server.DB.Redis;
|
||||
using StackExchange.Redis;
|
||||
using Server.Core;
|
||||
|
||||
namespace Server.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话UUID管理
|
||||
/// </summary>
|
||||
public class SessionUUIDManager : SingleInstance<SessionUUIDManager>, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 会话 UUID 集合
|
||||
/// </summary>
|
||||
protected ConcurrentDictionary<string, SessionUserData> SessionUUIDs =
|
||||
new ConcurrentDictionary<string, SessionUserData>();
|
||||
|
||||
public int SessionCnt
|
||||
{
|
||||
get { return SessionUUIDs.Count; }
|
||||
}
|
||||
|
||||
private Timer _timer;
|
||||
|
||||
public SessionUUIDManager()
|
||||
{
|
||||
MessageDispatcher.Instance.AddListener(MessageCode.RegisterSessionRequest, OnRegisterSession);
|
||||
MessageDispatcher.Instance.AddListener(MessageCode.UpdateSessionExpireTime, OnSessionExpireTime);
|
||||
|
||||
MessageDispatcher.Instance.AddListener(MessageCode.GamePackTestRequest,OnGamePackTestRequest);
|
||||
|
||||
|
||||
_timer = new Timer(OnTimer, null, 3000, 10 * 1000);
|
||||
}
|
||||
|
||||
public void Init()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新过期时间
|
||||
/// </summary>
|
||||
public void UpdateExpiredTime(string sessionUUID, int userid)
|
||||
{
|
||||
if (SessionUUIDs.ContainsKey(sessionUUID))
|
||||
{
|
||||
SessionUUID.UpdateExpiredTime(sessionUUID);
|
||||
//redisManager.db.KeyExpire(GetSessionUUIDKey(sessionUUID), TimeSpan.FromMinutes(ExpireTime));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个会话 UUID
|
||||
/// </summary>
|
||||
/// <param name="userid"></param>
|
||||
/// <returns></returns>
|
||||
public string CreateSessionUUID(int userid)
|
||||
{
|
||||
return SessionUUID.CreateSessionUUID(userid);
|
||||
|
||||
// string sessionUUID = Guid.NewGuid().ToString().Replace("-", "");
|
||||
// redisManager.db.StringSet(GetSessionUUIDKey(sessionUUID), userid, TimeSpan.FromMinutes(ExpireTime));
|
||||
// return sessionUUID;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sessionUUID"></param>
|
||||
/// <param name="userData"></param>
|
||||
public void AddSessionUUID(string sessionUUID, SessionUserData userData)
|
||||
{
|
||||
if (sessionUUID == null)
|
||||
{
|
||||
Debug.Error("不能添加空SeesionId!");
|
||||
return;
|
||||
}
|
||||
|
||||
SessionUUIDs.AddOrUpdate(sessionUUID, (k) => userData, (k, oldValue) => userData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除本地会话UUID 断开连接时移除本地会话
|
||||
/// </summary>
|
||||
/// <param name="sessionUUID"></param>
|
||||
/// <param name="userData"></param>
|
||||
public void RemoveLocalUUID(string sessionUUID, SessionUserData userData)
|
||||
{
|
||||
//null 会报错并闪退
|
||||
if (sessionUUID == null)
|
||||
{
|
||||
Debug.Error("不能移除空SeesionId!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SessionUUIDs.TryGetValue(sessionUUID, out SessionUserData data) && data == userData)
|
||||
{
|
||||
if (SessionUUIDs.TryRemove(sessionUUID, out data))
|
||||
{
|
||||
if (data == userData)
|
||||
Debug.Info($"移除本地会话UUID:{data.Userid}");
|
||||
else
|
||||
Debug.Error($"删错了!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检测一个 UUID 是否存在
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public int CheckSessionUUID(string sessionUUID)
|
||||
{
|
||||
if (string.IsNullOrEmpty(sessionUUID))
|
||||
{
|
||||
Debug.Error("sessionUUID is null!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SessionUUIDs.TryGetValue(sessionUUID, out SessionUserData data))
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
Debug.Info("data is null");
|
||||
}
|
||||
|
||||
return data.Userid;
|
||||
}
|
||||
|
||||
return SessionUUID.GetUserIdBySessionUUID(sessionUUID);
|
||||
// RedisValue value = redisManager.db.StringGet(GetSessionUUIDKey(sessionUUID));
|
||||
// if (value.HasValue && int.TryParse(value, out userid) && userid > 0)
|
||||
// {
|
||||
// return userid;
|
||||
// }
|
||||
//
|
||||
// return -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
MessageDispatcher.Instance.RemoveListener(MessageCode.RegisterSessionRequest, OnRegisterSession);
|
||||
MessageDispatcher.Instance.RemoveListener(MessageCode.UpdateSessionExpireTime, OnSessionExpireTime);
|
||||
MessageDispatcher.Instance.RemoveListener(MessageCode.GamePackTestRequest,OnGamePackTestRequest);
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册会话绑定的数量
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, int> SessionUUidBindCnt = new ConcurrentDictionary<string, int>();
|
||||
|
||||
/// <summary>
|
||||
/// 会话的黑名单
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, long> SessionUUidBlackList =
|
||||
new ConcurrentDictionary<string, long>();
|
||||
|
||||
/// <summary>
|
||||
/// 黑名单队列
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<string> SessionUUidBlackListQueue = new ConcurrentQueue<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 10 秒钟最多绑定的次数
|
||||
/// </summary>
|
||||
private const int BindMaxCnt = 10;
|
||||
|
||||
private const int CheckCnt = 10;
|
||||
|
||||
/// <summary>
|
||||
/// 黑名单一个小时过期时间 , 测试就用一分钟
|
||||
/// </summary>
|
||||
private const int BlackListExpireTime = 60 * 60 * 1000;
|
||||
|
||||
private void OnTimer(object state)
|
||||
{
|
||||
SessionUUidBindCnt.Clear();
|
||||
|
||||
int cnt = SessionUUidBlackListQueue.Count;
|
||||
if (cnt > CheckCnt)
|
||||
{
|
||||
cnt = CheckCnt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
if (SessionUUidBlackListQueue.TryDequeue(out string uuid))
|
||||
{
|
||||
if (SessionUUidBlackList.TryGetValue(uuid, out long time))
|
||||
{
|
||||
if (TimeInfo.Instance.FrameTime >= time)
|
||||
{
|
||||
SessionUUidBlackList.TryRemove(uuid, out time);
|
||||
//Debug.Info($"移除黑名单:{uuid}");
|
||||
}
|
||||
else
|
||||
{
|
||||
SessionUUidBlackListQueue.Enqueue(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task OnRegisterSession(GamePacket packet)
|
||||
{
|
||||
if (packet.MessageData is RegisterSessionRequest request)
|
||||
{
|
||||
if (!packet.Session.IsDisConnected)
|
||||
{
|
||||
//黑名单用户
|
||||
if (SessionUUidBlackList.ContainsKey(request.SessionUUID))
|
||||
{
|
||||
packet.Session.DisConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
int cnt = SessionUUidBindCnt.AddOrUpdate(request.SessionUUID, (k) => 1,
|
||||
(k, oldValue) => oldValue + 1);
|
||||
|
||||
if (cnt >= BindMaxCnt) //添加到黑名单
|
||||
{
|
||||
//Debug.Info($"添加到黑名单:{request.SessionUUID}");
|
||||
SessionUUidBlackList.TryAdd(request.SessionUUID,
|
||||
TimeInfo.Instance.FrameTime + BlackListExpireTime);
|
||||
SessionUUidBlackListQueue.Enqueue(request.SessionUUID);
|
||||
packet.Session.DisConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
//已经绑定过了,来重复绑定
|
||||
if (packet.Session.UserData != null)
|
||||
{
|
||||
//Debug.Info("绑定过了,断开链接!");
|
||||
packet.Session.DisConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
RegisterSessionResponse response = new RegisterSessionResponse();
|
||||
response.SeqId = request.SeqId;
|
||||
int userid = CheckSessionUUID(request.SessionUUID);
|
||||
if (userid < 0)
|
||||
{
|
||||
response.ResultCode = MessageErrCode.SessionUuidExpired;
|
||||
}
|
||||
else if (userid != request.Userid)
|
||||
{
|
||||
response.ResultCode = MessageErrCode.UseridFail;
|
||||
}
|
||||
else
|
||||
{
|
||||
response.ResultCode = MessageErrCode.Success;
|
||||
}
|
||||
|
||||
// if (userid == 15497437)
|
||||
// {
|
||||
// NetManager.BlackList.Enqueue(packet.Data);
|
||||
// }
|
||||
|
||||
Debug.Info($"注册SessionUUID:{request.SessionUUID}");
|
||||
packet.Session.Send(response);
|
||||
|
||||
//绑定成功才添加绑定
|
||||
if (response.ResultCode == MessageErrCode.Success)
|
||||
{
|
||||
packet.Session.BindUser(userid, request.SessionUUID, request.ClientVer, request.Plat,
|
||||
request.DeviceInfo, false, request.StoreType);
|
||||
UpdateExpiredTime(request.SessionUUID, userid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Task OnSessionExpireTime(GamePacket packet)
|
||||
{
|
||||
if (packet.MessageData is SessionUpdateExpireTime request)
|
||||
{
|
||||
if (packet.Session.UserData != null)
|
||||
{
|
||||
UpdateExpiredTime(packet.Session.UserData.Uuid, packet.Session.UserData.Userid);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task OnGamePackTestRequest(GamePacket packet)
|
||||
{
|
||||
if (packet.MessageData is GamePackTestRequest request)
|
||||
{
|
||||
GamePackTestResponse response = new GamePackTestResponse();
|
||||
response.Id = request.Id;
|
||||
packet.Session.Send(response);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
GameNetModule/Manager/UserNet/UserNetActor.cs
Normal file
80
GameNetModule/Manager/UserNet/UserNetActor.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using ActorCore;
|
||||
using NetWorkMessage;
|
||||
using Server.Net;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户网络
|
||||
/// </summary>
|
||||
public class UserNetActor : Actor
|
||||
{
|
||||
public override SchedulerType SchedulerType => SchedulerType.Thread;
|
||||
|
||||
public override MailBoxType MailBoxType => MailBoxType.UnOrderedMessage;
|
||||
|
||||
private UserNetSessionManager _userNetSessionManager;
|
||||
|
||||
public int ConnectCnt
|
||||
{
|
||||
get
|
||||
{
|
||||
return _userNetSessionManager.ConnectCnt;
|
||||
}
|
||||
}
|
||||
|
||||
public UserNetActor(byte moduleId, byte nodeNum,IPEndPoint ipEndPoint) : base(moduleId, nodeNum, ActorTypeId.UserNet)
|
||||
{
|
||||
_userNetSessionManager = new UserNetSessionManager();
|
||||
_userNetSessionManager.Start(ipEndPoint);
|
||||
}
|
||||
|
||||
public override void RegisterHandle()
|
||||
{
|
||||
this.RegisterMessageHandle(typeof(UserNetSendMessage), HandleUserNetSend);
|
||||
this.RegisterMessageHandle(typeof(UserNetForceFinMessage), HandleUserNetForceFin);
|
||||
|
||||
}
|
||||
|
||||
public bool IsPrintLog;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (IsPrintLog)
|
||||
{
|
||||
IsPrintLog = false;
|
||||
_userNetSessionManager.Log();
|
||||
}
|
||||
|
||||
_userNetSessionManager.Update();
|
||||
base.Update();
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_userNetSessionManager.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private Task HandleUserNetSend(ActorId fromActorId, IMessage message)
|
||||
{
|
||||
if (message is UserNetSendMessage userNetSendMessage)
|
||||
{
|
||||
_userNetSessionManager.Send2Router(userNetSendMessage.Id,userNetSendMessage.InstanceId,userNetSendMessage.MessageObject);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private Task HandleUserNetForceFin(ActorId fromActorId, IMessage message)
|
||||
{
|
||||
if (message is UserNetForceFinMessage forceFinMessage)
|
||||
{
|
||||
_userNetSessionManager.ForceDisConnect(forceFinMessage.Id,forceFinMessage.InstanceId,forceFinMessage.FinCode);
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
GameNetModule/Manager/UserNet/UserNetSession.cs
Normal file
82
GameNetModule/Manager/UserNet/UserNetSession.cs
Normal file
@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Server.Net;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
/// <summary>
|
||||
/// UserNet 是 UserNetActor 管理, 与 主线程的 UserSession 对应, id 和 instanceId 用来查找对应的 Session
|
||||
/// </summary>
|
||||
public class UserNetSession : BaseSession
|
||||
{
|
||||
/// <summary>
|
||||
/// 实例ID 如果Id 改变就不能用了,不再是之前的用户
|
||||
/// </summary>
|
||||
public long InstanceId { get; set; }
|
||||
|
||||
public byte Ver;
|
||||
|
||||
public bool IsDisConnected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fin 就是链接断了 Idle 就是还没绑定用户 从收到链接请求,到发送中断确认之间都是Using
|
||||
/// </summary>
|
||||
public UserSessionState State = UserSessionState.Idle;
|
||||
|
||||
private static long IdGenerate;
|
||||
|
||||
public int ForceDisConnectCode { get; set; }
|
||||
|
||||
private long GetInstanceId()
|
||||
{
|
||||
if (InstanceId >= long.MaxValue)
|
||||
{
|
||||
IdGenerate = 0;
|
||||
}
|
||||
|
||||
IdGenerate++;
|
||||
|
||||
return IdGenerate;
|
||||
}
|
||||
|
||||
public UserNetSession(long id, TService service, Action<long> disposeAction) : base(id, service,disposeAction)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 链接确认 建立链接就生成新的
|
||||
/// </summary>
|
||||
public void ConnectAck()
|
||||
{
|
||||
InstanceId = GetInstanceId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到链接包 - 回链接成功,表示建立连接成功 标志为使用中
|
||||
* 如果收到断开连接包,则触发用户断线的事件 标志为空闲
|
||||
* 如果是真断开,判断状态如果是使用中,也要出发用户断线事件, 标志为断开
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public enum UserSessionState
|
||||
{
|
||||
/// <summary>
|
||||
/// 空闲
|
||||
/// </summary>
|
||||
Idle,
|
||||
|
||||
/// <summary>
|
||||
/// 使用中
|
||||
/// </summary>
|
||||
Using,
|
||||
|
||||
/// <summary>
|
||||
/// 断开中
|
||||
/// </summary>
|
||||
Fin,
|
||||
}
|
||||
}
|
||||
427
GameNetModule/Manager/UserNet/UserNetSessionManager.cs
Normal file
427
GameNetModule/Manager/UserNet/UserNetSessionManager.cs
Normal file
@ -0,0 +1,427 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
223
GameNetModule/Manager/UserSessionManager.cs
Normal file
223
GameNetModule/Manager/UserSessionManager.cs
Normal file
@ -0,0 +1,223 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using MrWu.Debug;
|
||||
using Server.Core;
|
||||
using Server.Net;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
/// <summary>
|
||||
/// 这个是主线程处理的
|
||||
/// </summary>
|
||||
public class UserSessionManager : Singleton<UserSessionManager>
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户会话事件队列
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<UserSessionEvt> evtQueue = new ConcurrentQueue<UserSessionEvt>();
|
||||
|
||||
/// <summary>
|
||||
/// 用户的Session
|
||||
/// </summary>
|
||||
private Dictionary<long, UserSession> UserSessions = new Dictionary<long, UserSession>();
|
||||
|
||||
public bool IsGame { get; private set; }
|
||||
|
||||
public UserSessionManager(bool isGame)
|
||||
{
|
||||
IsGame = isGame;
|
||||
}
|
||||
|
||||
public void AddUserSessionEvt(UserSessionEvt evt)
|
||||
{
|
||||
evtQueue.Enqueue(evt);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
int count = evtQueue.Count;
|
||||
while (count -- > 0)
|
||||
{
|
||||
if (!evtQueue.TryDequeue(out var evt))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
switch (evt.EvtType)
|
||||
{
|
||||
case UserSessionEvtType.Connected:
|
||||
{
|
||||
UserSession userSession = new UserSession(evt.Id, evt.InstanceId);
|
||||
if (UserSessions.ContainsKey(evt.Id))
|
||||
{
|
||||
Debug.Error($"怎么会存在这个链接实例:{evt.Id}");
|
||||
}
|
||||
|
||||
userSession.IsDisConnected = false;
|
||||
userSession.RemoteIPAddress = evt.RemoteIPAddress;
|
||||
UserSessions[evt.Id] = userSession;
|
||||
}
|
||||
break;
|
||||
case UserSessionEvtType.Fin:
|
||||
{
|
||||
//断开处理
|
||||
if (UserSessions.TryGetValue(evt.Id, out UserSession userSession))
|
||||
{
|
||||
if (evt.InstanceId != userSession.InstanceId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
UserFin(userSession);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UserSessionEvtType.ReceivePack:
|
||||
{
|
||||
if (UserSessions.TryGetValue(evt.Id,out UserSession userSession))
|
||||
{
|
||||
if (evt.InstanceId != userSession.InstanceId)
|
||||
{
|
||||
Debug.Error("实例ID改变,不处理消息!");
|
||||
}
|
||||
else
|
||||
{
|
||||
//要不这里就单独处理包,不走MessageDispatcher
|
||||
userSession.RemoteIPAddress = evt.RemoteIPAddress;
|
||||
evt.GamePacket.Session = ProxySession.Create(userSession);
|
||||
MessageDispatcher.Instance.AddPack(evt.GamePacket);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Error("未找到收包的链接!");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
evt.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户断开链接
|
||||
/// </summary>
|
||||
public void UserFin(UserSession userSession)
|
||||
{
|
||||
if (UserSessions.TryGetValue(userSession.Id, out UserSession _session))
|
||||
{
|
||||
if (_session == userSession)
|
||||
{
|
||||
UserSessions.Remove(userSession.Id);
|
||||
|
||||
userSession.IsDisConnected = true;
|
||||
int userid = 0;
|
||||
if (userSession.UserData != null)
|
||||
{
|
||||
userid = userSession.UserData.Userid;
|
||||
}
|
||||
if (userSession.UserData != null && IsGame)
|
||||
{
|
||||
if (!GameSessionManager.Instance.RemovePlayerSession(userid, userSession))
|
||||
{
|
||||
Debug.Error($"玩家未正常移除! {userid}");
|
||||
}
|
||||
Debug.ImportantLog($"玩家离线,注销Session {userid}");
|
||||
}
|
||||
userSession.ClearUserData();
|
||||
if (userid > 0)
|
||||
{
|
||||
GameDispatcher.Instance.Dispatch(GameMsgId.DisConnected, userid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Info("移除失败,不是同一个Session!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Info("移除失败,没找到Session!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class UserSessionEvt : Reference
|
||||
{
|
||||
/// <summary>
|
||||
/// 链接ID
|
||||
/// </summary>
|
||||
public long Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 实例ID
|
||||
/// </summary>
|
||||
public long InstanceId { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// IP地址 链接的时候会赋值
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string RemoteIPAddress { get; private set; }
|
||||
|
||||
public GamePacket GamePacket { get; private set; }
|
||||
|
||||
public UserSessionEvtType EvtType { get; private set; }
|
||||
|
||||
public static UserSessionEvt Create(long id, long instanceId, string remoteIpAddress)
|
||||
{
|
||||
UserSessionEvt self =ReferencePool.Fetch<UserSessionEvt>();
|
||||
self.Id = id;
|
||||
self.InstanceId = instanceId;
|
||||
self.RemoteIPAddress = remoteIpAddress;
|
||||
self.EvtType = UserSessionEvtType.Connected;
|
||||
return self;
|
||||
}
|
||||
|
||||
public static UserSessionEvt Create(long id, long instanceId, GamePacket gamePacket,string remoteIpAddress)
|
||||
{
|
||||
UserSessionEvt self = ReferencePool.Fetch<UserSessionEvt>();
|
||||
self.Id = id;
|
||||
self.InstanceId = instanceId;
|
||||
self.GamePacket = gamePacket;
|
||||
self.RemoteIPAddress = remoteIpAddress;
|
||||
self.EvtType = UserSessionEvtType.ReceivePack;
|
||||
return self;
|
||||
}
|
||||
|
||||
public static UserSessionEvt Create(long id,long instanceId,UserSessionEvtType evtType)
|
||||
{
|
||||
var evt = new UserSessionEvt();
|
||||
evt.Id = id;
|
||||
evt.InstanceId = instanceId;
|
||||
evt.EvtType = evtType;
|
||||
return evt;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
RemoteIPAddress = null;
|
||||
GamePacket = null;
|
||||
ReferencePool.Recycle(this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum UserSessionEvtType
|
||||
{
|
||||
/// <summary>
|
||||
/// 链接
|
||||
/// </summary>
|
||||
Connected,
|
||||
|
||||
/// <summary>
|
||||
/// 断开链接
|
||||
/// </summary>
|
||||
Fin,
|
||||
|
||||
/// <summary>
|
||||
/// 收包
|
||||
/// </summary>
|
||||
ReceivePack,
|
||||
}
|
||||
}
|
||||
35
GameNetModule/Properties/AssemblyInfo.cs
Normal file
35
GameNetModule/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("GameNetModule")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("GameNetModule")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2025")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("308FBEBB-0ED2-4B28-A98D-2397B5470E3D")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
Reference in New Issue
Block a user