mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-07 16:45:10 +00:00
Remove old networkmodule
Remove old networkmodule
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 22d1bcdd317c6f64daeb9bf9742495f2
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,62 +0,0 @@
|
|||||||
using UnityEditor;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace TEngine.Editor.Inspector
|
|
||||||
{
|
|
||||||
[CustomEditor(typeof(Network))]
|
|
||||||
internal sealed class NetworkInspector : GameFrameworkInspector
|
|
||||||
{
|
|
||||||
public override void OnInspectorGUI()
|
|
||||||
{
|
|
||||||
base.OnInspectorGUI();
|
|
||||||
|
|
||||||
if (!EditorApplication.isPlaying)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Network t = (Network)target;
|
|
||||||
|
|
||||||
if (IsPrefabInHierarchy(t.gameObject))
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Network Channel Count", t.NetworkChannelCount.ToString());
|
|
||||||
|
|
||||||
INetworkChannel[] networkChannels = t.GetAllNetworkChannels();
|
|
||||||
foreach (INetworkChannel networkChannel in networkChannels)
|
|
||||||
{
|
|
||||||
DrawNetworkChannel(networkChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawNetworkChannel(INetworkChannel networkChannel)
|
|
||||||
{
|
|
||||||
EditorGUILayout.BeginVertical("box");
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField(networkChannel.Name, networkChannel.Connected ? "Connected" : "Disconnected");
|
|
||||||
EditorGUILayout.LabelField("Service Type", networkChannel.ServiceType.ToString());
|
|
||||||
EditorGUILayout.LabelField("Address Family", networkChannel.AddressFamily.ToString());
|
|
||||||
EditorGUILayout.LabelField("Local Address", networkChannel.Connected ? networkChannel.Socket.LocalEndPoint.ToString() : "Unavailable");
|
|
||||||
EditorGUILayout.LabelField("Remote Address", networkChannel.Connected ? networkChannel.Socket.RemoteEndPoint.ToString() : "Unavailable");
|
|
||||||
EditorGUILayout.LabelField("Send Packet", Utility.Text.Format("{0} / {1}", networkChannel.SendPacketCount, networkChannel.SentPacketCount));
|
|
||||||
EditorGUILayout.LabelField("Receive Packet", Utility.Text.Format("{0} / {1}", networkChannel.ReceivePacketCount, networkChannel.ReceivedPacketCount));
|
|
||||||
EditorGUILayout.LabelField("Miss Heart Beat Count", networkChannel.MissHeartBeatCount.ToString());
|
|
||||||
EditorGUILayout.LabelField("Heart Beat", Utility.Text.Format("{0:F2} / {1:F2}", networkChannel.HeartBeatElapseSeconds, networkChannel.HeartBeatInterval));
|
|
||||||
EditorGUI.BeginDisabledGroup(!networkChannel.Connected);
|
|
||||||
{
|
|
||||||
if (GUILayout.Button("Disconnect"))
|
|
||||||
{
|
|
||||||
networkChannel.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EditorGUI.EndDisabledGroup();
|
|
||||||
}
|
|
||||||
EditorGUILayout.EndVertical();
|
|
||||||
|
|
||||||
EditorGUILayout.Separator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3301ba180cdc446bbdf823c860ae7a68
|
|
||||||
timeCreated: 1682045195
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 145b951be40d41dea06e76bd967a5d15
|
|
||||||
timeCreated: 1682045847
|
|
@@ -1,166 +0,0 @@
|
|||||||
using TEngine;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
public enum ClientConnectWatcherStatus
|
|
||||||
{
|
|
||||||
StatusInit,
|
|
||||||
StatusReconnectAuto,
|
|
||||||
StatusReconnectConfirm,
|
|
||||||
StatusWaitExit
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ClientConnectWatcher
|
|
||||||
{
|
|
||||||
private readonly GameClient _client;
|
|
||||||
private ClientConnectWatcherStatus _status;
|
|
||||||
private float _statusTime;
|
|
||||||
private int _reconnectCnt = 0;
|
|
||||||
private int _disconnectReason = 0;
|
|
||||||
|
|
||||||
private bool _enable = false;
|
|
||||||
|
|
||||||
public bool Enable
|
|
||||||
{
|
|
||||||
get => _enable;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_enable != value)
|
|
||||||
{
|
|
||||||
_enable = value;
|
|
||||||
if (_enable)
|
|
||||||
{
|
|
||||||
OnEnable();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
OnDisable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ClientConnectWatcherStatus Status
|
|
||||||
{
|
|
||||||
get => _status;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_status == value) return;
|
|
||||||
_status = value;
|
|
||||||
_statusTime = NowTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float NowTime => GameTime.unscaledTime;
|
|
||||||
|
|
||||||
public ClientConnectWatcher(GameClient client)
|
|
||||||
{
|
|
||||||
_client = client;
|
|
||||||
_statusTime = NowTime;
|
|
||||||
_status = ClientConnectWatcherStatus.StatusInit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (!_enable)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_client.IsEntered)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_status)
|
|
||||||
{
|
|
||||||
case ClientConnectWatcherStatus.StatusInit:
|
|
||||||
UpdateOnInitStatus();
|
|
||||||
break;
|
|
||||||
case ClientConnectWatcherStatus.StatusReconnectAuto:
|
|
||||||
UpdateOnReconnectAuto();
|
|
||||||
break;
|
|
||||||
case ClientConnectWatcherStatus.StatusReconnectConfirm:
|
|
||||||
UpdateOnReconnectConfirm();
|
|
||||||
break;
|
|
||||||
case ClientConnectWatcherStatus.StatusWaitExit:
|
|
||||||
UpdateOnWaitExit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnReConnect()
|
|
||||||
{
|
|
||||||
if (_status == ClientConnectWatcherStatus.StatusReconnectConfirm)
|
|
||||||
{
|
|
||||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOnInitStatus()
|
|
||||||
{
|
|
||||||
int autoReconnectMaxCount = 4;
|
|
||||||
if (_reconnectCnt <= autoReconnectMaxCount)
|
|
||||||
{
|
|
||||||
if (_reconnectCnt == 0)
|
|
||||||
{
|
|
||||||
_disconnectReason = _client.LastNetErrCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
|
||||||
_reconnectCnt++;
|
|
||||||
|
|
||||||
//重连
|
|
||||||
_client.Reconnect();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Status = ClientConnectWatcherStatus.StatusReconnectConfirm;
|
|
||||||
_reconnectCnt++;
|
|
||||||
// UISys.Mgr.ShowUI(GAME_UI_TYPE.Tip_NetDisconn, UISys.Mgr.GetUIWindowParam().SetParam("errCode", m_disconnectReason));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOnReconnectAuto()
|
|
||||||
{
|
|
||||||
if (_client.IsEntered)
|
|
||||||
{
|
|
||||||
Status = ClientConnectWatcherStatus.StatusInit;
|
|
||||||
_reconnectCnt = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float nowTime = NowTime;
|
|
||||||
var timeoutTime = 10f;
|
|
||||||
|
|
||||||
if (_statusTime + timeoutTime < nowTime)
|
|
||||||
{
|
|
||||||
Log.Error("UpdateOnReconnectAuto timeout: {0}", timeoutTime);
|
|
||||||
|
|
||||||
//切换到默认的,下一帧继续判断是否需要自动还是手动
|
|
||||||
Status = ClientConnectWatcherStatus.StatusInit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOnReconnectConfirm()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateOnWaitExit()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
Status = ClientConnectWatcherStatus.StatusInit;
|
|
||||||
_reconnectCnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
Status = ClientConnectWatcherStatus.StatusInit;
|
|
||||||
_reconnectCnt = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3e4e637f3da340dd9512150c3e2ff087
|
|
||||||
timeCreated: 1684334948
|
|
@@ -1,428 +0,0 @@
|
|||||||
using System.Net.Sockets;
|
|
||||||
using GameBase;
|
|
||||||
using GameProto;
|
|
||||||
using TEngine;
|
|
||||||
using CSPkg = GameProto.CSPkg;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
public enum GameClientStatus
|
|
||||||
{
|
|
||||||
StatusInit, //初始化
|
|
||||||
StatusReconnect, //重新连接
|
|
||||||
StatusClose, //断开连接
|
|
||||||
StatusLogin, //登录中
|
|
||||||
StatusEnter, //AccountLogin成功,进入服务器了
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum CsMsgResult
|
|
||||||
{
|
|
||||||
NoError = 0,
|
|
||||||
NetworkError = 1,
|
|
||||||
InternalError = 2,
|
|
||||||
MsgTimeOut = 3,
|
|
||||||
PingTimeOut = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
//定义消息回报的回调接口
|
|
||||||
public delegate void CsMsgDelegate(CsMsgResult result, CSPkg msg);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 统计网络协议的接口
|
|
||||||
/// </summary>
|
|
||||||
public delegate void CsMsgStatDelegate(int cmdID, int pkgSize);
|
|
||||||
|
|
||||||
public class GameClient : Singleton<GameClient>
|
|
||||||
{
|
|
||||||
private readonly INetworkChannel _channel;
|
|
||||||
|
|
||||||
private GameClientStatus _status = GameClientStatus.StatusInit;
|
|
||||||
|
|
||||||
private readonly MsgDispatcher _dispatcher;
|
|
||||||
|
|
||||||
private readonly ClientConnectWatcher _connectWatcher;
|
|
||||||
|
|
||||||
private float _lastLogDisconnectErrTime = 0f;
|
|
||||||
|
|
||||||
private int _lastNetErrCode = 0;
|
|
||||||
|
|
||||||
public int LastNetErrCode => _lastNetErrCode;
|
|
||||||
|
|
||||||
public GameClientStatus Status
|
|
||||||
{
|
|
||||||
get => _status;
|
|
||||||
set => _status = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsEntered => _status == GameClientStatus.StatusEnter;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连续心跳超时
|
|
||||||
/// </summary>
|
|
||||||
private int _heatBeatTimeoutNum = 0;
|
|
||||||
|
|
||||||
private int _ping = -1;
|
|
||||||
|
|
||||||
private float NowTime => GameTime.unscaledTime;
|
|
||||||
|
|
||||||
private string _lastHost = null;
|
|
||||||
private int _lastPort = 0;
|
|
||||||
|
|
||||||
public GameClient()
|
|
||||||
{
|
|
||||||
_connectWatcher = new ClientConnectWatcher(this);
|
|
||||||
_dispatcher = new MsgDispatcher();
|
|
||||||
_dispatcher.SetTimeout(5f);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel,object>(NetworkEvent.NetworkConnectedEvent,OnNetworkConnected);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent,OnNetworkClosed);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel,NetworkErrorCode,SocketError,string>(NetworkEvent.NetworkErrorEvent,OnNetworkError);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel,object>(NetworkEvent.NetworkCustomErrorEvent,OnNetworkCustomError);
|
|
||||||
_channel = Network.Instance.CreateNetworkChannel("GameClient", ServiceType.Tcp, new NetworkChannelHelper());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
|
||||||
{
|
|
||||||
bool isReconnect = (_status == GameClientStatus.StatusReconnect);
|
|
||||||
//准备登录
|
|
||||||
Status = GameClientStatus.StatusLogin;
|
|
||||||
|
|
||||||
OnServerConnected(isReconnect);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkClosed(INetworkChannel channel)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError, string errorMessage)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Connect(string host, int port, bool reconnect = false)
|
|
||||||
{
|
|
||||||
ResetParam();
|
|
||||||
if (!reconnect)
|
|
||||||
{
|
|
||||||
SetWatchReconnect(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reconnect)
|
|
||||||
{
|
|
||||||
// GameEvent.Get<ICommUI>().ShowWaitUITip(WaitUISeq.LOGINWORLD_SEQID, G.R(TextDefine.ID_TIPS_RECONNECTING));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// GameEvent.Get<ICommUI>().ShowWaitUI(WaitUISeq.LOGINWORLD_SEQID);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastHost = host;
|
|
||||||
_lastPort = port;
|
|
||||||
|
|
||||||
Status = reconnect ? GameClientStatus.StatusReconnect : GameClientStatus.StatusInit;
|
|
||||||
|
|
||||||
_channel.Connect(host, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reconnect()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(_lastHost) || _lastPort <= 0)
|
|
||||||
{
|
|
||||||
// GameModule.UI.ShowTipMsg("Invalid reconnect param");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_connectWatcher.OnReConnect();
|
|
||||||
Connect(_lastHost, _lastPort, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void Shutdown()
|
|
||||||
{
|
|
||||||
_channel.Close();
|
|
||||||
_status = GameClientStatus.StatusInit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnServerConnected(bool isReconnect)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SendCsMsg(CSPkg reqPkg)
|
|
||||||
{
|
|
||||||
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DoSendData(reqPkg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsStatusCanSendMsg(uint msgId)
|
|
||||||
{
|
|
||||||
bool canSend = false;
|
|
||||||
if (_status == GameClientStatus.StatusLogin)
|
|
||||||
{
|
|
||||||
canSend = (msgId == (uint)CSMsgID.CsCmdActLoginReq);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_status == GameClientStatus.StatusEnter)
|
|
||||||
{
|
|
||||||
canSend = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!canSend)
|
|
||||||
{
|
|
||||||
float nowTime = NowTime;
|
|
||||||
if (_lastLogDisconnectErrTime + 5 < nowTime)
|
|
||||||
{
|
|
||||||
Log.Error("GameClient not connected, send msg failed, msgId[{0}]", msgId);
|
|
||||||
_lastLogDisconnectErrTime = nowTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
//UISys.Mgr.ShowTipMsg(TextDefine.ID_ERR_NETWORKD_DISCONNECT);
|
|
||||||
}
|
|
||||||
|
|
||||||
return canSend;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SendCsMsg(CSPkg reqPkg, uint resCmd, CsMsgDelegate resHandler = null, bool needShowWaitUI = true)
|
|
||||||
{
|
|
||||||
if (!IsStatusCanSendMsg(reqPkg.Head.MsgId))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = DoSendData(reqPkg);
|
|
||||||
if (!ret)
|
|
||||||
{
|
|
||||||
if (resHandler != null)
|
|
||||||
{
|
|
||||||
resHandler(CsMsgResult.InternalError, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
_dispatcher.NotifyCmdError(resCmd, CsMsgResult.InternalError);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//注册消息
|
|
||||||
if (resHandler != null)
|
|
||||||
{
|
|
||||||
_dispatcher.RegSeqHandle(reqPkg.Head.Echo, resCmd, resHandler);
|
|
||||||
if (reqPkg.Head.Echo > 0 && IsWaitingCmd(resCmd) && needShowWaitUI)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
// GameEvent.Get<ICommUI>().ShowWaitUI(reqPkg.Head.Echo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DoSendData(CSPkg reqPkg)
|
|
||||||
{
|
|
||||||
if (!IsIgnoreLog(reqPkg.Head.MsgId))
|
|
||||||
{
|
|
||||||
Log.Debug("[c-s] CmdId[{0}]\n{1}", reqPkg.Head.MsgId, reqPkg.Body.ToString());
|
|
||||||
}
|
|
||||||
var sendRet = _channel.Send(reqPkg);
|
|
||||||
return sendRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsIgnoreLog(uint msgId)
|
|
||||||
{
|
|
||||||
bool ignoreLog = false;
|
|
||||||
switch (msgId)
|
|
||||||
{
|
|
||||||
case (uint)CSMsgID.CsCmdHeatbeatReq:
|
|
||||||
case (uint)CSMsgID.CsCmdHeatbeatRes:
|
|
||||||
ignoreLog = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ignoreLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsWaitingCmd(uint msgId)
|
|
||||||
{
|
|
||||||
//心跳包不需要读条等待
|
|
||||||
if (msgId == (uint)CSMsgID.CsCmdHeatbeatRes)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ResetParam()
|
|
||||||
{
|
|
||||||
_lastLogDisconnectErrTime = 0f;
|
|
||||||
_heatBeatTimeoutNum = 0;
|
|
||||||
_lastHbTime = 0f;
|
|
||||||
_ping = -1;
|
|
||||||
_lastNetErrCode = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnUpdate()
|
|
||||||
{
|
|
||||||
_dispatcher.Update();
|
|
||||||
TickHeartBeat();
|
|
||||||
CheckHeatBeatTimeout();
|
|
||||||
_connectWatcher.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 注册静态消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="iCmdID"></param>
|
|
||||||
/// <param name="msgDelegate"></param>
|
|
||||||
public void RegCmdHandle(int iCmdID, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
_dispatcher.RegCmdHandle((uint)iCmdID, msgDelegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除消息处理函数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cmdId"></param>
|
|
||||||
/// <param name="msgDelegate"></param>
|
|
||||||
public void RmvCmdHandle(int cmdId, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
_dispatcher.RmvCmdHandle((uint)cmdId, msgDelegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置加密密钥
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key"></param>
|
|
||||||
public void SetEncryptKey(string key)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置是否需要监控网络重连。
|
|
||||||
/// 登录成功后,开启监控,可以自动重连或者提示玩家重连。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="needWatch"></param>
|
|
||||||
public void SetWatchReconnect(bool needWatch)
|
|
||||||
{
|
|
||||||
_connectWatcher.Enable = needWatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsNetworkOkAndLogin()
|
|
||||||
{
|
|
||||||
return _status == GameClientStatus.StatusEnter;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 心跳处理
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 最近一次心跳的时间
|
|
||||||
/// </summary>
|
|
||||||
private float _lastHbTime = 0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 心跳间隔
|
|
||||||
/// </summary>
|
|
||||||
private readonly float _heartBeatDurTime = 5;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 心跳超时的最大次数
|
|
||||||
/// </summary>
|
|
||||||
private const int HeatBeatTimeoutMaxCount = 2;
|
|
||||||
|
|
||||||
private bool CheckHeatBeatTimeout()
|
|
||||||
{
|
|
||||||
if (_heatBeatTimeoutNum >= HeatBeatTimeoutMaxCount)
|
|
||||||
{
|
|
||||||
//断开连接
|
|
||||||
Shutdown();
|
|
||||||
|
|
||||||
//准备重连
|
|
||||||
_heatBeatTimeoutNum = 0;
|
|
||||||
Status = GameClientStatus.StatusClose;
|
|
||||||
Log.Error("heat beat detect timeout");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TickHeartBeat()
|
|
||||||
{
|
|
||||||
if (Status != GameClientStatus.StatusEnter)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var nowTime = NowTime;
|
|
||||||
if (_lastHbTime + _heartBeatDurTime < nowTime)
|
|
||||||
{
|
|
||||||
_lastHbTime = nowTime;
|
|
||||||
|
|
||||||
CSPkg heatPkg = ProtobufUtility.BuildCsMsg((int)CSMsgID.CsCmdHeatbeatReq);
|
|
||||||
heatPkg.Body.HeatBeatReq = new CSHeatBeatReq { HeatEchoTime = _lastHbTime };
|
|
||||||
SendCsMsg(heatPkg, (int)CSMsgID.CsCmdHeatbeatRes, HandleHeatBeatRes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleHeatBeatRes(CsMsgResult result, CSPkg msg)
|
|
||||||
{
|
|
||||||
if (result != CsMsgResult.NoError)
|
|
||||||
{
|
|
||||||
//如果是超时了,则标记最近收到包的次数
|
|
||||||
if (result == CsMsgResult.MsgTimeOut)
|
|
||||||
{
|
|
||||||
_heatBeatTimeoutNum++;
|
|
||||||
Log.Warning("heat beat timeout: {0}", _heatBeatTimeoutNum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var resBody = msg.Body.HeatBeatRes;
|
|
||||||
float diffTime = NowTime - resBody.HeatEchoTime;
|
|
||||||
_ping = (int)(diffTime * 1000);
|
|
||||||
_heatBeatTimeoutNum = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Ping值
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ping值
|
|
||||||
/// </summary>
|
|
||||||
public int Ping
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsPingValid())
|
|
||||||
{
|
|
||||||
return _ping / 4;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsPingValid()
|
|
||||||
{
|
|
||||||
if (IsNetworkOkAndLogin())
|
|
||||||
{
|
|
||||||
return _ping >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8c5441725c9f4d98a7790dc76a6a0c48
|
|
||||||
timeCreated: 1684331687
|
|
@@ -1,265 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using GameProto;
|
|
||||||
using TEngine;
|
|
||||||
using CSPkg = GameProto.CSPkg;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
internal class MsgHandleDataToRmv
|
|
||||||
{
|
|
||||||
public uint MsgId;
|
|
||||||
public CsMsgDelegate Handle;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MsgDispatcher
|
|
||||||
{
|
|
||||||
const int CheckTimeoutPerframe = 10;
|
|
||||||
const int MaxMsgHandle = 256;
|
|
||||||
|
|
||||||
private readonly CsMsgDelegate[] _aMsgHandles = new CsMsgDelegate[MaxMsgHandle];
|
|
||||||
private readonly float[] _fMsgRegTime = new float[MaxMsgHandle];
|
|
||||||
private readonly UInt32[] _adwMsgRegSeq = new UInt32[MaxMsgHandle]; //因为_aiMsgRegResCmdID存储的是hash,不能保证一定seqid一样,所以这儿存储下,用来校验
|
|
||||||
private readonly uint[] _aiMsgRegResCmdID = new uint[MaxMsgHandle];
|
|
||||||
|
|
||||||
UInt32 _dwLastCheckIndex = 0;
|
|
||||||
|
|
||||||
private readonly Dictionary<uint, List<CsMsgDelegate>> _mapCmdHandle = new Dictionary<uint, List<CsMsgDelegate>>();
|
|
||||||
private readonly List<CsMsgStatDelegate> _listStatHandle = new List<CsMsgStatDelegate>();
|
|
||||||
|
|
||||||
//防止在处理消息的时候又删除了消息映射,所以这儿加了个队列来做个保护
|
|
||||||
private readonly List<MsgHandleDataToRmv> _rmvList = new List<MsgHandleDataToRmv>();
|
|
||||||
private bool _isInHandleLoop = false;
|
|
||||||
private float _timeout = 5;
|
|
||||||
|
|
||||||
// 清理所有的网络消息
|
|
||||||
public void CleanAllNetMsg()
|
|
||||||
{
|
|
||||||
_mapCmdHandle.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTimeout(float timeout)
|
|
||||||
{
|
|
||||||
_timeout = timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegSeqHandle(UInt32 dwMsgSeqID, uint iResCmdID, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
UInt32 hashIndex = dwMsgSeqID % MaxMsgHandle;
|
|
||||||
if (_aMsgHandles[hashIndex] != null)
|
|
||||||
{
|
|
||||||
OnCallSeqHandle(_adwMsgRegSeq[hashIndex], _aiMsgRegResCmdID[hashIndex]);
|
|
||||||
NotifyTimeout(_aMsgHandles[hashIndex]);
|
|
||||||
RmvReg((int)hashIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
_aMsgHandles[hashIndex] = msgDelegate;
|
|
||||||
_fMsgRegTime[hashIndex] = NowTime;
|
|
||||||
_adwMsgRegSeq[hashIndex] = dwMsgSeqID;
|
|
||||||
_aiMsgRegResCmdID[hashIndex] = iResCmdID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
if (!_mapCmdHandle.TryGetValue(iCmdID, out var listHandle))
|
|
||||||
{
|
|
||||||
listHandle = new List<CsMsgDelegate>();
|
|
||||||
_mapCmdHandle[iCmdID] = listHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listHandle != null)
|
|
||||||
{
|
|
||||||
if (listHandle.Contains(msgDelegate))
|
|
||||||
{
|
|
||||||
Log.Error("-------------repeat RegCmdHandle:{0}-----------", iCmdID);
|
|
||||||
}
|
|
||||||
|
|
||||||
listHandle.Add(msgDelegate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 注册统计处理接口
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handler"></param>
|
|
||||||
public void RegCmdStatHandle(CsMsgStatDelegate handler)
|
|
||||||
{
|
|
||||||
_listStatHandle.Add(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DispatchCmdStat(int cmdID, int pkgSize)
|
|
||||||
{
|
|
||||||
foreach (CsMsgStatDelegate handle in _listStatHandle)
|
|
||||||
{
|
|
||||||
handle(cmdID, pkgSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RmvCmdHandle(uint iCmdID, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
if (_isInHandleLoop)
|
|
||||||
{
|
|
||||||
MsgHandleDataToRmv toRmvData = new MsgHandleDataToRmv();
|
|
||||||
toRmvData.MsgId = iCmdID;
|
|
||||||
toRmvData.Handle = msgDelegate;
|
|
||||||
|
|
||||||
_rmvList.Add(toRmvData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_mapCmdHandle.TryGetValue(iCmdID, out var listHandle))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listHandle != null)
|
|
||||||
{
|
|
||||||
listHandle.Remove(msgDelegate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void NotifyCmdError(uint iCmdID, CsMsgResult result)
|
|
||||||
{
|
|
||||||
NotifyCmdHandle(iCmdID, result, default(CSPkg));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool NotifyCmdHandle(uint cmdID, CsMsgResult result, CSPkg pkg)
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
if (_mapCmdHandle.TryGetValue(cmdID, out var listHandle))
|
|
||||||
{
|
|
||||||
_isInHandleLoop = true;
|
|
||||||
|
|
||||||
var rmvList = _rmvList;
|
|
||||||
rmvList.Clear();
|
|
||||||
foreach (CsMsgDelegate handle in listHandle)
|
|
||||||
{
|
|
||||||
ret = true;
|
|
||||||
|
|
||||||
TProfiler.BeginSample("handle");
|
|
||||||
handle(result, pkg);
|
|
||||||
TProfiler.EndSample();
|
|
||||||
}
|
|
||||||
|
|
||||||
_isInHandleLoop = false;
|
|
||||||
|
|
||||||
//再统一删除掉
|
|
||||||
int rmvCnt = rmvList.Count;
|
|
||||||
for (int i = 0; i < rmvCnt; i++)
|
|
||||||
{
|
|
||||||
var rmvItem = rmvList[i];
|
|
||||||
Log.Error("-------------remove cmd handle on loop:{0}-----------", rmvItem.MsgId);
|
|
||||||
RmvCmdHandle(rmvItem.MsgId, rmvItem.Handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void OnCallSeqHandle(UInt32 echoSeq, uint resCmdID)
|
|
||||||
{
|
|
||||||
if (echoSeq > 0)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
// GameEvent.Get<ICommUI>().FinWaitUI(echoSeq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void NotifyTimeout(CsMsgDelegate msgHandler)
|
|
||||||
{
|
|
||||||
msgHandler(CsMsgResult.MsgTimeOut, default(CSPkg));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void NotifySeqError(UInt32 dwSeqID, CsMsgResult result)
|
|
||||||
{
|
|
||||||
UInt32 hashIndex = dwSeqID % MaxMsgHandle;
|
|
||||||
|
|
||||||
//先判断是否有注册的指定消息
|
|
||||||
if (_aMsgHandles[hashIndex] != null &&
|
|
||||||
_adwMsgRegSeq[hashIndex] == dwSeqID)
|
|
||||||
{
|
|
||||||
OnCallSeqHandle(dwSeqID, _aiMsgRegResCmdID[hashIndex]);
|
|
||||||
_aMsgHandles[hashIndex](result, null);
|
|
||||||
|
|
||||||
RmvReg((int)hashIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCmdFilterNoLog(int cmdID)
|
|
||||||
{
|
|
||||||
switch (cmdID)
|
|
||||||
{
|
|
||||||
case (int)CSMsgID.CsCmdHeatbeatRes:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void NotifyMsg(CSPkg msg)
|
|
||||||
{
|
|
||||||
UInt32 dwSeq = msg.Head.Echo;
|
|
||||||
UInt32 hashIndex = dwSeq % MaxMsgHandle;
|
|
||||||
//判断是否有固定的消息处理流程
|
|
||||||
bool bHaveHandle = NotifyCmdHandle(msg.Head.MsgId, CsMsgResult.NoError, msg);
|
|
||||||
|
|
||||||
//再判断是否有注册的指定消息
|
|
||||||
if (_aMsgHandles[hashIndex] != null &&
|
|
||||||
_adwMsgRegSeq[hashIndex] == dwSeq &&
|
|
||||||
_aiMsgRegResCmdID[hashIndex] == (int)msg.Head.MsgId)
|
|
||||||
{
|
|
||||||
OnCallSeqHandle(_adwMsgRegSeq[hashIndex], _aiMsgRegResCmdID[hashIndex]);
|
|
||||||
_aMsgHandles[hashIndex](CsMsgResult.NoError, msg);
|
|
||||||
RmvReg((int)hashIndex);
|
|
||||||
bHaveHandle = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bHaveHandle)
|
|
||||||
{
|
|
||||||
Log.Debug("there is no handle for Msg[{0}]", msg.Head.MsgId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float NowTime => GameTime.unscaledTime;
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
CheckTimeOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 定时检查是否请求超时。
|
|
||||||
/// </summary>
|
|
||||||
private void CheckTimeOut()
|
|
||||||
{
|
|
||||||
float timeout = _timeout;
|
|
||||||
float nowTime = NowTime;
|
|
||||||
for (int i = 0; i < CheckTimeoutPerframe; i++)
|
|
||||||
{
|
|
||||||
_dwLastCheckIndex = (_dwLastCheckIndex + 1) % MaxMsgHandle;
|
|
||||||
if (_aMsgHandles[_dwLastCheckIndex] != null)
|
|
||||||
{
|
|
||||||
if (_fMsgRegTime[_dwLastCheckIndex] + timeout < nowTime)
|
|
||||||
{
|
|
||||||
Log.Error("msg timeout, resCmdID[{0}], reqSeq[{1}]", _aiMsgRegResCmdID[_dwLastCheckIndex],
|
|
||||||
_adwMsgRegSeq[_dwLastCheckIndex]);
|
|
||||||
|
|
||||||
OnCallSeqHandle(_adwMsgRegSeq[_dwLastCheckIndex], _aiMsgRegResCmdID[_dwLastCheckIndex]);
|
|
||||||
NotifyTimeout(_aMsgHandles[_dwLastCheckIndex]);
|
|
||||||
|
|
||||||
RmvReg((int)_dwLastCheckIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RmvReg(int index)
|
|
||||||
{
|
|
||||||
_aMsgHandles[index] = null;
|
|
||||||
_adwMsgRegSeq[index] = 0;
|
|
||||||
_aiMsgRegResCmdID[index] = 0;
|
|
||||||
_fMsgRegTime[index] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 7d667fb84fed4c5f93a06c464585512f
|
|
||||||
timeCreated: 1684333223
|
|
@@ -1,226 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using GameProto;
|
|
||||||
using Google.Protobuf;
|
|
||||||
using TEngine;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
public class NetworkChannelHelper : INetworkChannelHelper, IMemory
|
|
||||||
{
|
|
||||||
private readonly Dictionary<int, Type> _serverToClientPacketTypes = new Dictionary<int, Type>();
|
|
||||||
private readonly MemoryStream _cachedStream = new MemoryStream(1024 * 8);
|
|
||||||
private INetworkChannel _networkChannel = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取消息包头长度。
|
|
||||||
/// <remarks>4。</remarks>
|
|
||||||
/// </summary>
|
|
||||||
public int PacketHeaderLength => sizeof(int);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道辅助器。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="networkChannel">网络频道。</param>
|
|
||||||
public void Initialize(INetworkChannel networkChannel)
|
|
||||||
{
|
|
||||||
_networkChannel = networkChannel;
|
|
||||||
|
|
||||||
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel, NetworkErrorCode, SocketError, string>(NetworkEvent.NetworkErrorEvent, OnNetworkError);
|
|
||||||
GameEvent.AddEventListener<INetworkChannel, object>(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭并清理网络频道辅助器。
|
|
||||||
/// </summary>
|
|
||||||
public void Shutdown()
|
|
||||||
{
|
|
||||||
GameEvent.RemoveEventListener<INetworkChannel, object>(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected);
|
|
||||||
GameEvent.RemoveEventListener<INetworkChannel>(NetworkEvent.NetworkClosedEvent, OnNetworkClosed);
|
|
||||||
GameEvent.RemoveEventListener<INetworkChannel, int>(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat);
|
|
||||||
GameEvent.RemoveEventListener<INetworkChannel, NetworkErrorCode, SocketError, string>(NetworkEvent.NetworkErrorEvent, OnNetworkError);
|
|
||||||
GameEvent.RemoveEventListener<INetworkChannel, object>(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError);
|
|
||||||
|
|
||||||
_networkChannel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 准备进行连接。
|
|
||||||
/// </summary>
|
|
||||||
public void PrepareForConnecting()
|
|
||||||
{
|
|
||||||
_networkChannel.Socket.ReceiveBufferSize = 1024 * 64;
|
|
||||||
_networkChannel.Socket.SendBufferSize = 1024 * 64;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CSPkg HeartBeatPack = new CSPkg { Head = new CSPkgHead(), Body = new CSPkgBody() };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送心跳消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>是否发送心跳消息包成功。</returns>
|
|
||||||
public bool SendHeartBeat()
|
|
||||||
{
|
|
||||||
HeartBeatPack.Head.MsgId = (uint)CSMsgID.CsCmdHeatbeatReq;
|
|
||||||
_networkChannel.Send(HeartBeatPack);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 序列化消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
|
||||||
/// <param name="packet">要序列化的消息包。</param>
|
|
||||||
/// <param name="destination">要序列化的目标流。</param>
|
|
||||||
/// <returns>是否序列化成功。</returns>
|
|
||||||
public bool Serialize(CSPkg packet, Stream destination)
|
|
||||||
{
|
|
||||||
if (packet == null)
|
|
||||||
{
|
|
||||||
Log.Warning("Packet is invalid.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cachedStream.SetLength(_cachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入
|
|
||||||
_cachedStream.Position = 0L;
|
|
||||||
global::ProtobufUtility.ToStreamWithHead(packet,_cachedStream);
|
|
||||||
_cachedStream.WriteTo(destination);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包头。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">要反序列化的来源流。</param>
|
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
|
||||||
/// <returns>反序列化后的消息包头。</returns>
|
|
||||||
public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData)
|
|
||||||
{
|
|
||||||
// 注意:此函数并不在主线程调用!
|
|
||||||
customErrorData = null;
|
|
||||||
PacketHeader packetHeader = MemoryPool.Acquire<PacketHeader>();
|
|
||||||
packetHeader.PacketLength = ((MemoryStream)source).GetBuffer()[0];
|
|
||||||
return packetHeader;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packetHeader">消息包头。</param>
|
|
||||||
/// <param name="source">要反序列化的来源流。</param>
|
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
|
||||||
/// <returns>反序列化后的消息包。</returns>
|
|
||||||
public CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData)
|
|
||||||
{
|
|
||||||
// 注意:此函数并不在主线程调用!
|
|
||||||
customErrorData = null;
|
|
||||||
|
|
||||||
PacketHeader scPacketHeader = packetHeader as PacketHeader;
|
|
||||||
if (scPacketHeader == null)
|
|
||||||
{
|
|
||||||
Log.Warning("Packet header is invalid.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
CSPkg csPkg = null;
|
|
||||||
if (scPacketHeader.IsValid)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
csPkg = global::ProtobufUtility.Deserialize(((MemoryStream)source).GetBuffer(),0,scPacketHeader.PacketLength);
|
|
||||||
Log.Debug("[s-c] CmdId[{0}]\n{1}", csPkg.Head.MsgId, csPkg.ToString());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Warning(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Warning("Packet header is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryPool.Release(scPacketHeader);
|
|
||||||
return csPkg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type GetServerToClientPacketType(int id)
|
|
||||||
{
|
|
||||||
if (_serverToClientPacketTypes.TryGetValue(id, out var type))
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
|
||||||
{
|
|
||||||
if (channel != _networkChannel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Info("Network channel '{0}' connected, local address '{1}', remote address '{2}'.",
|
|
||||||
channel.Name, channel.Socket.LocalEndPoint.ToString(),
|
|
||||||
channel.Socket.RemoteEndPoint.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkClosed(INetworkChannel channel)
|
|
||||||
{
|
|
||||||
if (channel != _networkChannel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Info("Network channel '{0}' closed.", channel.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount)
|
|
||||||
{
|
|
||||||
if (channel != _networkChannel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Fatal("Network channel '{0}' miss heart beat '{1}' times.", channel.Name, missCount.ToString());
|
|
||||||
|
|
||||||
if (missCount < 2)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError, string errorMessage)
|
|
||||||
{
|
|
||||||
if (channel != _networkChannel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Fatal("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", channel.Name, networkErrorCode.ToString(), errorMessage);
|
|
||||||
|
|
||||||
channel.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
|
||||||
{
|
|
||||||
if (channel != _networkChannel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: bf86ce2ddfb5429abecfb06257c86acd
|
|
||||||
timeCreated: 1682045961
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e77590d750cf480baae2468038654bc0
|
|
||||||
timeCreated: 1681991042
|
|
@@ -1,23 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络地址类型。
|
|
||||||
/// </summary>
|
|
||||||
public enum AddressFamily : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 未知。
|
|
||||||
/// </summary>
|
|
||||||
Unknown = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IP 版本 4。
|
|
||||||
/// </summary>
|
|
||||||
IPv4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IP 版本 6。
|
|
||||||
/// </summary>
|
|
||||||
IPv6
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: dd8e805430d24bdb8c2679b59ba7c2d6
|
|
||||||
timeCreated: 1681993653
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 9aefe7f3e0fd485091a20579b29872a7
|
|
||||||
timeCreated: 1681994393
|
|
@@ -1,180 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using GameProto;
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络频道接口。
|
|
||||||
/// </summary>
|
|
||||||
public interface INetworkChannel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道名称。
|
|
||||||
/// </summary>
|
|
||||||
string Name
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道所使用的 Socket。
|
|
||||||
/// </summary>
|
|
||||||
Socket Socket
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取是否已连接。
|
|
||||||
/// </summary>
|
|
||||||
bool Connected
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
ServiceType ServiceType
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络地址类型。
|
|
||||||
/// </summary>
|
|
||||||
AddressFamily AddressFamily
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取要发送的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
int SendPacketCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取累计发送的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
int SentPacketCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取已接收未处理的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
int ReceivePacketCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取累计已接收的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
int ReceivedPacketCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取或设置当收到消息包时是否重置心跳流逝时间。
|
|
||||||
/// </summary>
|
|
||||||
bool ResetHeartBeatElapseSecondsWhenReceivePacket
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取丢失心跳的次数。
|
|
||||||
/// </summary>
|
|
||||||
int MissHeartBeatCount
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取或设置心跳间隔时长,以秒为单位。
|
|
||||||
/// </summary>
|
|
||||||
float HeartBeatInterval
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取心跳等待时长,以秒为单位。
|
|
||||||
/// </summary>
|
|
||||||
float HeartBeatElapseSeconds
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 注册网络消息包处理函数。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msgId">网络消息包id。</param>
|
|
||||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
|
||||||
/// <param name="checkRepeat">是否检测重复。</param>
|
|
||||||
void RegisterMsgHandler(int msgId, CsMsgDelegate msgDelegate, bool checkRepeat = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除网络消息包处理函数。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msgId">网络消息包id。</param>
|
|
||||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
|
||||||
void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
void Connect(string ipAddress, int port);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
void Connect(IPAddress ipAddress, int port);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
void Connect(IPAddress ipAddress, int port, object userData);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭网络频道。
|
|
||||||
/// </summary>
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向远程主机发送消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
|
||||||
bool Send(CSPkg packet);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向远程主机发送消息包并注册消息回调。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
|
||||||
/// <param name="resHandler">要注册的回调。</param>
|
|
||||||
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
|
||||||
bool Send(CSPkg packet, CsMsgDelegate resHandler, bool needShowWaitUI = false);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 09b554ed41c546a1bc40e5be392f836a
|
|
||||||
timeCreated: 1681993830
|
|
@@ -1,67 +0,0 @@
|
|||||||
using System.IO;
|
|
||||||
using GameProto;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络频道辅助器接口。
|
|
||||||
/// </summary>
|
|
||||||
public interface INetworkChannelHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取消息包头长度。
|
|
||||||
/// </summary>
|
|
||||||
int PacketHeaderLength
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道辅助器。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="networkChannel">网络频道。</param>
|
|
||||||
void Initialize(INetworkChannel networkChannel);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭并清理网络频道辅助器。
|
|
||||||
/// </summary>
|
|
||||||
void Shutdown();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 准备进行连接。
|
|
||||||
/// </summary>
|
|
||||||
void PrepareForConnecting();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送心跳消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>是否发送心跳消息包成功。</returns>
|
|
||||||
bool SendHeartBeat();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 序列化消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">消息包类型。</typeparam>
|
|
||||||
/// <param name="packet">要序列化的消息包。</param>
|
|
||||||
/// <param name="destination">要序列化的目标流。</param>
|
|
||||||
/// <returns>是否序列化成功。</returns>
|
|
||||||
bool Serialize(CSPkg packet, Stream destination);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包头。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="source">要反序列化的来源流。</param>
|
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
|
||||||
/// <returns>反序列化后的消息包头。</returns>
|
|
||||||
IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packetHeader">消息包头。</param>
|
|
||||||
/// <param name="source">要反序列化的来源流。</param>
|
|
||||||
/// <param name="customErrorData">用户自定义错误数据。</param>
|
|
||||||
/// <returns>反序列化后的消息包。</returns>
|
|
||||||
CSPkg DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 1f847791db5a4e6cbbc39dd56a6888d9
|
|
||||||
timeCreated: 1681993713
|
|
@@ -1,84 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络管理器接口。
|
|
||||||
/// </summary>
|
|
||||||
public interface INetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道数量。
|
|
||||||
/// </summary>
|
|
||||||
int NetworkChannelCount { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络连接成功事件。
|
|
||||||
/// </summary>
|
|
||||||
event Action<INetworkChannel, object> NetworkConnected;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络连接关闭事件。
|
|
||||||
/// </summary>
|
|
||||||
event Action<INetworkChannel> NetworkClosed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络心跳包丢失事件。
|
|
||||||
/// </summary>
|
|
||||||
event Action<INetworkChannel, int> NetworkMissHeartBeat;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
event Action<INetworkChannel, NetworkErrorCode, SocketError, string> NetworkError;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户自定义网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
event Action<INetworkChannel, object> NetworkCustomError;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查是否存在网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>是否存在网络频道。</returns>
|
|
||||||
bool HasNetworkChannel(string name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>要获取的网络频道。</returns>
|
|
||||||
INetworkChannel GetNetworkChannel(string name);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>所有网络频道。</returns>
|
|
||||||
INetworkChannel[] GetAllNetworkChannels();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="results">所有网络频道。</param>
|
|
||||||
void GetAllNetworkChannels(List<INetworkChannel> results);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="serviceType">网络服务类型。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
/// <returns>要创建的网络频道。</returns>
|
|
||||||
INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 销毁网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>是否销毁网络频道成功。</returns>
|
|
||||||
bool DestroyNetworkChannel(string name);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 7de8458fc50e4cd3b49ef73d35ad9763
|
|
||||||
timeCreated: 1681993806
|
|
@@ -1,16 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包头接口。
|
|
||||||
/// </summary>
|
|
||||||
public interface IPacketHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络消息包长度。
|
|
||||||
/// </summary>
|
|
||||||
int PacketLength
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 7659e4a617d84126b0a548077fb4fdcc
|
|
||||||
timeCreated: 1681994226
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 6d988d26345f402f9177488d8921184f
|
|
||||||
timeCreated: 1682092063
|
|
@@ -1,8 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 6dff8f4f5dd94df49bf6d616fb11cc9c
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,71 +0,0 @@
|
|||||||
using System.Buffers;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 用于调试的KCP IO 类,没有Kcp功能
|
|
||||||
/// </summary>
|
|
||||||
public class FakeKcpIO : IKcpIO
|
|
||||||
{
|
|
||||||
QueuePipe<byte[]> recv = new QueuePipe<byte[]>();
|
|
||||||
public int Input(ReadOnlySpan<byte> span)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[span.Length];
|
|
||||||
span.CopyTo(buffer);
|
|
||||||
recv.Write(buffer);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Input(ReadOnlySequence<byte> span)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[span.Length];
|
|
||||||
span.CopyTo(buffer);
|
|
||||||
return Input(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
|
||||||
{
|
|
||||||
var buffer = await recv.ReadAsync().ConfigureAwait(false);
|
|
||||||
var target = writer.GetMemory(buffer.Length);
|
|
||||||
buffer.AsSpan().CopyTo(target.Span);
|
|
||||||
writer.Advance(buffer.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
|
||||||
{
|
|
||||||
var temp = await recv.ReadAsync().ConfigureAwait(false);
|
|
||||||
temp.AsSpan().CopyTo(buffer);
|
|
||||||
return temp.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
QueuePipe<byte[]> send = new QueuePipe<byte[]>();
|
|
||||||
public int Send(ReadOnlySpan<byte> span, object options = null)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[span.Length];
|
|
||||||
span.CopyTo(buffer);
|
|
||||||
send.Write(buffer);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Send(ReadOnlySequence<byte> span, object options = null)
|
|
||||||
{
|
|
||||||
byte[] buffer = new byte[span.Length];
|
|
||||||
span.CopyTo(buffer);
|
|
||||||
return Send(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
|
||||||
{
|
|
||||||
var buffer = await send.ReadAsync().ConfigureAwait(false);
|
|
||||||
Write(writer, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Write(IBufferWriter<byte> writer, byte[] buffer)
|
|
||||||
{
|
|
||||||
var span = writer.GetSpan(buffer.Length);
|
|
||||||
buffer.AsSpan().CopyTo(span);
|
|
||||||
writer.Advance(buffer.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e3c2f9de3c34ffc409549b86ac3fc530
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,147 +0,0 @@
|
|||||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
|
||||||
using System.Buffers;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Kcp回调
|
|
||||||
/// </summary>
|
|
||||||
public interface IKcpCallback
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// kcp 发送方向输出
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">kcp 交出发送缓冲区控制权,缓冲区来自<see cref="RentBuffer(int)"/></param>
|
|
||||||
/// <param name="avalidLength">数据的有效长度</param>
|
|
||||||
/// <returns>不需要返回值</returns>
|
|
||||||
/// <remarks>通过增加 avalidLength 能够在协议栈中有效的减少数据拷贝</remarks>
|
|
||||||
void Output(BufferOwner buffer, int avalidLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Kcp回调
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 失败设计,<see cref="KcpOutputWriter.Output(BufferOwner, int)"/>。IMemoryOwner是没有办法代替的。
|
|
||||||
/// 这里只相当于把 IKcpCallback 和 IRentable 和并。
|
|
||||||
/// </remarks>
|
|
||||||
public interface IKcpOutputWriter : IBufferWriter<byte>
|
|
||||||
{
|
|
||||||
int UnflushedBytes { get; }
|
|
||||||
void Flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
|
||||||
/// </summary>
|
|
||||||
public interface IRentable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 外部提供缓冲区,可以在外部链接一个内存池
|
|
||||||
/// </summary>
|
|
||||||
BufferOwner RentBuffer(int length);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IKcpSetting
|
|
||||||
{
|
|
||||||
int Interval(int interval);
|
|
||||||
/// <summary>
|
|
||||||
/// fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodelay">0:disable(default), 1:enable</param>
|
|
||||||
/// <param name="interval">internal update timer interval in millisec, default is 100ms</param>
|
|
||||||
/// <param name="resend">0:disable fast resend(default), 1:enable fast resend</param>
|
|
||||||
/// <param name="nc">0:normal congestion control(default), 1:disable congestion control</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
int NoDelay(int nodelay, int interval, int resend, int nc);
|
|
||||||
/// <summary>
|
|
||||||
/// change MTU size, default is 1400
|
|
||||||
/// <para>** 这个方法不是线程安全的。请在没有发送和接收时调用 。</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mtu"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// 如果没有必要,不要修改Mtu。过小的Mtu会导致分片数大于接收窗口,造成kcp阻塞冻结。
|
|
||||||
/// </remarks>
|
|
||||||
int SetMtu(int mtu = 1400);
|
|
||||||
/// <summary>
|
|
||||||
/// set maximum window size: sndwnd=32, rcvwnd=128 by default
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sndwnd"></param>
|
|
||||||
/// <param name="rcvwnd"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <remarks>
|
|
||||||
/// 如果没有必要请不要修改。注意确保接收窗口必须大于最大分片数。
|
|
||||||
/// </remarks>
|
|
||||||
int WndSize(int sndwnd = 32, int rcvwnd = 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IKcpUpdate
|
|
||||||
{
|
|
||||||
void Update(in DateTimeOffset time);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IKcpSendable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 将要发送到网络的数据Send到kcp协议中
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="span"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
int Send(ReadOnlySpan<byte> span, object options = null);
|
|
||||||
/// <summary>
|
|
||||||
/// 将要发送到网络的数据Send到kcp协议中
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="span"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
int Send(ReadOnlySequence<byte> span, object options = null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IKcpInputable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 下层收到数据后添加到kcp协议中
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="span"></param>
|
|
||||||
int Input(ReadOnlySpan<byte> span);
|
|
||||||
/// <summary>
|
|
||||||
/// 下层收到数据后添加到kcp协议中
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="span"></param>
|
|
||||||
int Input(ReadOnlySequence<byte> span);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// kcp协议输入输出标准接口
|
|
||||||
/// </summary>
|
|
||||||
public interface IKcpIO : IKcpSendable, IKcpInputable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 从kcp中取出一个整合完毕的数据包
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
UniTask RecvAsync(IBufferWriter<byte> writer, object options = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从kcp中取出一个整合完毕的数据包
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <returns>接收数据长度</returns>
|
|
||||||
UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从kcp协议中取出需要发送到网络的数据。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer"></param>
|
|
||||||
/// <param name="options"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
UniTask OutputAsync(IBufferWriter<byte> writer, object options = null);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b0ca98e42147b3948bc8d30f24e9c366
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,88 +0,0 @@
|
|||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Kcp报头
|
|
||||||
/// https://zhuanlan.zhihu.com/p/559191428
|
|
||||||
/// </summary>
|
|
||||||
public interface IKcpHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 会话编号,两方一致才会通信
|
|
||||||
/// </summary>
|
|
||||||
uint conv { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 指令类型
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <para/> IKCP_CMD_PUSH = 81 // cmd: push data 数据报文
|
|
||||||
/// <para/> IKCP_CMD_ACK = 82 // cmd: ack 确认报文
|
|
||||||
/// <para/> IKCP_CMD_WASK = 83 // cmd: window probe (ask) 窗口探测报文,询问对端剩余接收窗口的大小.
|
|
||||||
/// <para/> IKCP_CMD_WINS = 84 // cmd: window size (tell) 窗口通知报文,通知对端剩余接收窗口的大小.
|
|
||||||
/// </remarks>
|
|
||||||
byte cmd { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 剩余分片数量,表示随后还有多少个报文属于同一个包。
|
|
||||||
/// </summary>
|
|
||||||
byte frg { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 自己可用窗口大小
|
|
||||||
/// </summary>
|
|
||||||
ushort wnd { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 发送时的时间戳 <seealso cref="DateTimeOffset.ToUnixTimeMilliseconds"/>
|
|
||||||
/// </summary>
|
|
||||||
uint ts { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 编号 确认编号或者报文编号
|
|
||||||
/// </summary>
|
|
||||||
uint sn { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 代表编号前面的所有报都收到了的标志
|
|
||||||
/// </summary>
|
|
||||||
uint una { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 数据内容长度
|
|
||||||
/// </summary>
|
|
||||||
uint len { get; }
|
|
||||||
}
|
|
||||||
public interface IKcpSegment : IKcpHeader
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 重传的时间戳。超过当前时间重发这个包
|
|
||||||
/// </summary>
|
|
||||||
uint resendts { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 超时重传时间,根据网络去定
|
|
||||||
/// </summary>
|
|
||||||
uint rto { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 快速重传机制,记录被跳过的次数,超过次数进行快速重传
|
|
||||||
/// </summary>
|
|
||||||
uint fastack { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 重传次数
|
|
||||||
/// </summary>
|
|
||||||
uint xmit { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 数据内容
|
|
||||||
/// </summary>
|
|
||||||
Span<byte> data { get; }
|
|
||||||
/// <summary>
|
|
||||||
/// 将IKcpSegment编码成字节数组,并返回总长度(包括Kcp报头)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
int Encode(Span<byte> buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ISegmentManager<Segment> where Segment : IKcpSegment
|
|
||||||
{
|
|
||||||
Segment Alloc(int appendDateSize);
|
|
||||||
void Free(Segment seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a9259bb6b548e46459cc766d8fe2faeb
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,387 +0,0 @@
|
|||||||
using System.Buffers;
|
|
||||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
public class Kcp<Segment> : KcpCore<Segment>
|
|
||||||
where Segment : IKcpSegment
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// create a new kcp control object, 'conv' must equal in two endpoint
|
|
||||||
/// from the same connection.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="conv_"></param>
|
|
||||||
/// <param name="callback"></param>
|
|
||||||
/// <param name="rentable">可租用内存的回调</param>
|
|
||||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
|
||||||
: base(conv_)
|
|
||||||
{
|
|
||||||
callbackHandle = callback;
|
|
||||||
this.rentable = rentable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//extension 重构和新增加的部分============================================
|
|
||||||
|
|
||||||
IRentable rentable;
|
|
||||||
/// <summary>
|
|
||||||
/// 如果外部能够提供缓冲区则使用外部缓冲区,否则new byte[]
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="needSize"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
internal protected override BufferOwner CreateBuffer(int needSize)
|
|
||||||
{
|
|
||||||
var res = rentable?.RentBuffer(needSize);
|
|
||||||
if (res == null)
|
|
||||||
{
|
|
||||||
return base.CreateBuffer(needSize);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (res.Memory.Length < needSize)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"{nameof(rentable.RentBuffer)} 指定的委托不符合标准,返回的" +
|
|
||||||
$"BufferOwner.Memory.Length 小于 {nameof(needSize)}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
|
||||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
|
||||||
/// <para/>所以只需要部分加锁即可。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public (BufferOwner buffer, int avalidLength) TryRecv()
|
|
||||||
{
|
|
||||||
var peekSize = -1;
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
if (rcv_queue.Count == 0)
|
|
||||||
{
|
|
||||||
///没有可用包
|
|
||||||
return (null, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq = rcv_queue[0];
|
|
||||||
|
|
||||||
if (seq.frg == 0)
|
|
||||||
{
|
|
||||||
peekSize = (int)seq.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcv_queue.Count < seq.frg + 1)
|
|
||||||
{
|
|
||||||
///没有足够的包
|
|
||||||
return (null, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length = 0;
|
|
||||||
|
|
||||||
foreach (var item in rcv_queue)
|
|
||||||
{
|
|
||||||
length += item.len;
|
|
||||||
if (item.frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
peekSize = (int)length;
|
|
||||||
|
|
||||||
if (peekSize <= 0)
|
|
||||||
{
|
|
||||||
return (null, -2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var buffer = CreateBuffer(peekSize);
|
|
||||||
var recvlength = UncheckRecv(buffer.Memory.Span);
|
|
||||||
return (buffer, recvlength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// TryRecv Recv设计上同一时刻只允许一个线程调用。
|
|
||||||
/// <para/>因为要保证数据顺序,多个线程同时调用Recv也没有意义。
|
|
||||||
/// <para/>所以只需要部分加锁即可。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int TryRecv(IBufferWriter<byte> writer)
|
|
||||||
{
|
|
||||||
var peekSize = -1;
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
if (rcv_queue.Count == 0)
|
|
||||||
{
|
|
||||||
///没有可用包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq = rcv_queue[0];
|
|
||||||
|
|
||||||
if (seq.frg == 0)
|
|
||||||
{
|
|
||||||
peekSize = (int)seq.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcv_queue.Count < seq.frg + 1)
|
|
||||||
{
|
|
||||||
///没有足够的包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length = 0;
|
|
||||||
|
|
||||||
foreach (var item in rcv_queue)
|
|
||||||
{
|
|
||||||
length += item.len;
|
|
||||||
if (item.frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
peekSize = (int)length;
|
|
||||||
|
|
||||||
if (peekSize <= 0)
|
|
||||||
{
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return UncheckRecv(writer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int Recv(Span<byte> buffer)
|
|
||||||
{
|
|
||||||
if (0 == rcv_queue.Count)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var peekSize = PeekSize();
|
|
||||||
if (peekSize < 0)
|
|
||||||
{
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (peekSize > buffer.Length)
|
|
||||||
{
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 拆分函数
|
|
||||||
var recvLength = UncheckRecv(buffer);
|
|
||||||
|
|
||||||
return recvLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// user/upper level recv: returns size, returns below zero for EAGAIN
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int Recv(IBufferWriter<byte> writer)
|
|
||||||
{
|
|
||||||
if (0 == rcv_queue.Count)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var peekSize = PeekSize();
|
|
||||||
if (peekSize < 0)
|
|
||||||
{
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if (peekSize > buffer.Length)
|
|
||||||
//{
|
|
||||||
// return -3;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// 拆分函数
|
|
||||||
var recvLength = UncheckRecv(writer);
|
|
||||||
|
|
||||||
return recvLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 这个函数不检查任何参数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
int UncheckRecv(Span<byte> buffer)
|
|
||||||
{
|
|
||||||
var recover = false;
|
|
||||||
if (rcv_queue.Count >= rcv_wnd)
|
|
||||||
{
|
|
||||||
recover = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region merge fragment.
|
|
||||||
/// merge fragment.
|
|
||||||
|
|
||||||
var recvLength = 0;
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
foreach (var seg in rcv_queue)
|
|
||||||
{
|
|
||||||
seg.data.CopyTo(buffer.Slice(recvLength));
|
|
||||||
recvLength += (int)seg.len;
|
|
||||||
|
|
||||||
count++;
|
|
||||||
int frg = seg.frg;
|
|
||||||
|
|
||||||
SegmentManager.Free(seg);
|
|
||||||
if (frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
rcv_queue.RemoveRange(0, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
Move_Rcv_buf_2_Rcv_queue();
|
|
||||||
|
|
||||||
#region fast recover
|
|
||||||
/// fast recover
|
|
||||||
if (rcv_queue.Count < rcv_wnd && recover)
|
|
||||||
{
|
|
||||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
|
||||||
// tell remote my window size
|
|
||||||
probe |= IKCP_ASK_TELL;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
return recvLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 这个函数不检查任何参数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="writer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
int UncheckRecv(IBufferWriter<byte> writer)
|
|
||||||
{
|
|
||||||
var recover = false;
|
|
||||||
if (rcv_queue.Count >= rcv_wnd)
|
|
||||||
{
|
|
||||||
recover = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region merge fragment.
|
|
||||||
/// merge fragment.
|
|
||||||
|
|
||||||
var recvLength = 0;
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
var count = 0;
|
|
||||||
foreach (var seg in rcv_queue)
|
|
||||||
{
|
|
||||||
var len = (int)seg.len;
|
|
||||||
var destination = writer.GetSpan(len);
|
|
||||||
|
|
||||||
seg.data.CopyTo(destination);
|
|
||||||
writer.Advance(len);
|
|
||||||
|
|
||||||
recvLength += len;
|
|
||||||
|
|
||||||
count++;
|
|
||||||
int frg = seg.frg;
|
|
||||||
|
|
||||||
SegmentManager.Free(seg);
|
|
||||||
if (frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
{
|
|
||||||
rcv_queue.RemoveRange(0, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
Move_Rcv_buf_2_Rcv_queue();
|
|
||||||
|
|
||||||
#region fast recover
|
|
||||||
/// fast recover
|
|
||||||
if (rcv_queue.Count < rcv_wnd && recover)
|
|
||||||
{
|
|
||||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
|
||||||
// tell remote my window size
|
|
||||||
probe |= IKCP_ASK_TELL;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
return recvLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// check the size of next message in the recv queue
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int PeekSize()
|
|
||||||
{
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
if (rcv_queue.Count == 0)
|
|
||||||
{
|
|
||||||
///没有可用包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq = rcv_queue[0];
|
|
||||||
|
|
||||||
if (seq.frg == 0)
|
|
||||||
{
|
|
||||||
return (int)seq.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcv_queue.Count < seq.frg + 1)
|
|
||||||
{
|
|
||||||
///没有足够的包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length = 0;
|
|
||||||
|
|
||||||
foreach (var seg in rcv_queue)
|
|
||||||
{
|
|
||||||
length += seg.len;
|
|
||||||
if (seg.frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: b3840e90e37f0194bbfafb564ff80d45
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: fcc0172c723e55a4389ad2f62b68a3fe
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,262 +0,0 @@
|
|||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
using BufferOwner = System.Buffers.IMemoryOwner<byte>;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// <inheritdoc cref="IPipe{T}"/>
|
|
||||||
/// <para></para>这是个简单的实现,更复杂使用微软官方实现<see cref="Channel.CreateBounded{T}(int)"/>
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
internal class QueuePipe<T> : Queue<T>
|
|
||||||
{
|
|
||||||
readonly object _innerLock = new object();
|
|
||||||
private TaskCompletionSource<T> source;
|
|
||||||
|
|
||||||
//线程同步上下文由Task机制保证,无需额外处理
|
|
||||||
//SynchronizationContext callbackContext;
|
|
||||||
//public bool UseSynchronizationContext { get; set; } = true;
|
|
||||||
|
|
||||||
public virtual void Write(T item)
|
|
||||||
{
|
|
||||||
lock (_innerLock)
|
|
||||||
{
|
|
||||||
if (source == null)
|
|
||||||
{
|
|
||||||
Enqueue(item);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Count > 0)
|
|
||||||
{
|
|
||||||
throw new Exception("内部顺序错误,不应该出现,请联系作者");
|
|
||||||
}
|
|
||||||
|
|
||||||
var next = source;
|
|
||||||
source = null;
|
|
||||||
next.TrySetResult(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public new void Enqueue(T item)
|
|
||||||
{
|
|
||||||
lock (_innerLock)
|
|
||||||
{
|
|
||||||
base.Enqueue(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Flush()
|
|
||||||
{
|
|
||||||
lock (_innerLock)
|
|
||||||
{
|
|
||||||
if (Count > 0)
|
|
||||||
{
|
|
||||||
var res = Dequeue();
|
|
||||||
var next = source;
|
|
||||||
source = null;
|
|
||||||
next?.TrySetResult(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual Task<T> ReadAsync()
|
|
||||||
{
|
|
||||||
lock (_innerLock)
|
|
||||||
{
|
|
||||||
if (this.Count > 0)
|
|
||||||
{
|
|
||||||
var next = Dequeue();
|
|
||||||
return Task.FromResult(next);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
source = new TaskCompletionSource<T>();
|
|
||||||
return source.Task;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask<T> ReadValueTaskAsync()
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KcpIO<Segment> : KcpCore<Segment>, IKcpIO
|
|
||||||
where Segment : IKcpSegment
|
|
||||||
{
|
|
||||||
OutputQ outq;
|
|
||||||
|
|
||||||
public KcpIO(uint conv_) : base(conv_)
|
|
||||||
{
|
|
||||||
outq = new OutputQ();
|
|
||||||
callbackHandle = outq;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Parse_data(Segment newseg)
|
|
||||||
{
|
|
||||||
base.Parse_data(newseg);
|
|
||||||
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
var recover = false;
|
|
||||||
if (rcv_queue.Count >= rcv_wnd)
|
|
||||||
{
|
|
||||||
recover = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (TryRecv(out var arraySegment) > 0)
|
|
||||||
{
|
|
||||||
recvSignal.Enqueue(arraySegment);
|
|
||||||
}
|
|
||||||
|
|
||||||
recvSignal.Flush();
|
|
||||||
|
|
||||||
#region fast recover
|
|
||||||
|
|
||||||
/// fast recover
|
|
||||||
if (rcv_queue.Count < rcv_wnd && recover)
|
|
||||||
{
|
|
||||||
// ready to send back IKCP_CMD_WINS in ikcp_flush
|
|
||||||
// tell remote my window size
|
|
||||||
probe |= IKCP_ASK_TELL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QueuePipe<ArraySegment<Segment>> recvSignal = new QueuePipe<ArraySegment<Segment>>();
|
|
||||||
|
|
||||||
internal int TryRecv(out ArraySegment<Segment> package)
|
|
||||||
{
|
|
||||||
package = default;
|
|
||||||
lock (rcv_queueLock)
|
|
||||||
{
|
|
||||||
var peekSize = -1;
|
|
||||||
if (rcv_queue.Count == 0)
|
|
||||||
{
|
|
||||||
///没有可用包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var seq = rcv_queue[0];
|
|
||||||
|
|
||||||
if (seq.frg == 0)
|
|
||||||
{
|
|
||||||
peekSize = (int)seq.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rcv_queue.Count < seq.frg + 1)
|
|
||||||
{
|
|
||||||
///没有足够的包
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length = 0;
|
|
||||||
|
|
||||||
Segment[] kcpSegments = ArrayPool<Segment>.Shared.Rent(seq.frg + 1);
|
|
||||||
|
|
||||||
var index = 0;
|
|
||||||
foreach (var item in rcv_queue)
|
|
||||||
{
|
|
||||||
kcpSegments[index] = item;
|
|
||||||
index++;
|
|
||||||
length += item.len;
|
|
||||||
if (item.frg == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index > 0)
|
|
||||||
{
|
|
||||||
rcv_queue.RemoveRange(0, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
package = new ArraySegment<Segment>(kcpSegments, 0, index);
|
|
||||||
|
|
||||||
peekSize = (int)length;
|
|
||||||
|
|
||||||
if (peekSize <= 0)
|
|
||||||
{
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return peekSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask RecvAsync(IBufferWriter<byte> writer, object options = null)
|
|
||||||
{
|
|
||||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
|
||||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
|
||||||
{
|
|
||||||
WriteRecv(writer, arraySegment.Array[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteRecv(IBufferWriter<byte> writer, Segment seg)
|
|
||||||
{
|
|
||||||
var curCount = (int)seg.len;
|
|
||||||
var target = writer.GetSpan(curCount);
|
|
||||||
seg.data.CopyTo(target);
|
|
||||||
SegmentManager.Free(seg);
|
|
||||||
writer.Advance(curCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask<int> RecvAsync(ArraySegment<byte> buffer, object options = null)
|
|
||||||
{
|
|
||||||
var arraySegment = await recvSignal.ReadAsync().ConfigureAwait(false);
|
|
||||||
int start = buffer.Offset;
|
|
||||||
for (int i = arraySegment.Offset; i < arraySegment.Count; i++)
|
|
||||||
{
|
|
||||||
var target = new Memory<byte>(buffer.Array, start, buffer.Array.Length - start);
|
|
||||||
|
|
||||||
var seg = arraySegment.Array[i];
|
|
||||||
seg.data.CopyTo(target.Span);
|
|
||||||
start += seg.data.Length;
|
|
||||||
|
|
||||||
SegmentManager.Free(seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayPool<Segment>.Shared.Return(arraySegment.Array, true);
|
|
||||||
return start - buffer.Offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask OutputAsync(IBufferWriter<byte> writer, object options = null)
|
|
||||||
{
|
|
||||||
var (Owner, Count) = await outq.ReadAsync().ConfigureAwait(false);
|
|
||||||
WriteOut(writer, Owner, Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void WriteOut(IBufferWriter<byte> writer, BufferOwner Owner, int Count)
|
|
||||||
{
|
|
||||||
var target = writer.GetSpan(Count);
|
|
||||||
Owner.Memory.Span.Slice(0, Count).CopyTo(target);
|
|
||||||
writer.Advance(Count);
|
|
||||||
Owner.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal override BufferOwner CreateBuffer(int needSize)
|
|
||||||
{
|
|
||||||
return MemoryPool<byte>.Shared.Rent(needSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class OutputQ : QueuePipe<(BufferOwner Owner, int Count)>,
|
|
||||||
IKcpCallback
|
|
||||||
{
|
|
||||||
public void Output(BufferOwner buffer, int avalidLength)
|
|
||||||
{
|
|
||||||
Write((buffer, avalidLength));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 3c30d30fa46372948b60bd56eec47fe6
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,50 +0,0 @@
|
|||||||
using System.Buffers;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
public abstract class KcpOutputWriter : IKcpOutputWriter
|
|
||||||
{
|
|
||||||
public int UnflushedBytes { get; set; }
|
|
||||||
public IMemoryOwner<byte> MemoryOwner { get; set; }
|
|
||||||
public void Flush()
|
|
||||||
{
|
|
||||||
Output(MemoryOwner, UnflushedBytes);
|
|
||||||
MemoryOwner = null;
|
|
||||||
UnflushedBytes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Advance(int count)
|
|
||||||
{
|
|
||||||
UnflushedBytes += count;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Memory<byte> GetMemory(int sizeHint = 0)
|
|
||||||
{
|
|
||||||
if (MemoryOwner == null)
|
|
||||||
{
|
|
||||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
|
||||||
}
|
|
||||||
return MemoryOwner.Memory.Slice(UnflushedBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Span<byte> GetSpan(int sizeHint = 0)
|
|
||||||
{
|
|
||||||
if (MemoryOwner == null)
|
|
||||||
{
|
|
||||||
MemoryOwner = MemoryPool<byte>.Shared.Rent(2048);
|
|
||||||
}
|
|
||||||
return MemoryOwner.Memory.Span.Slice(UnflushedBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Socket发送是要pin byte[],为了不阻塞KcpFlush,动态缓存是必须的。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <param name="avalidLength"></param>
|
|
||||||
public abstract void Output(IMemoryOwner<byte> buffer, int avalidLength);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a058d8fa8a92dc94395bfd6e1f6fdb8f
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,402 +0,0 @@
|
|||||||
using System.Buffers.Binary;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 调整了没存布局,直接拷贝块提升性能。
|
|
||||||
/// <para>结构体保存内容只有一个指针,不用担心参数传递过程中的性能</para>
|
|
||||||
/// https://github.com/skywind3000/kcp/issues/118#issuecomment-338133930
|
|
||||||
/// <para>不要对没有初始化的KcpSegment(内部指针为0,所有属性都将指向位置区域) 进行任何赋值操作,可能导致内存损坏。
|
|
||||||
/// 出于性能考虑,没有对此项进行安全检查。</para>
|
|
||||||
/// </summary>
|
|
||||||
public struct KcpSegment : IKcpSegment
|
|
||||||
{
|
|
||||||
internal readonly unsafe byte* ptr;
|
|
||||||
public unsafe KcpSegment(byte* intPtr, uint appendDateSize)
|
|
||||||
{
|
|
||||||
this.ptr = intPtr;
|
|
||||||
len = appendDateSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使用完必须显示释放,否则内存泄漏
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="appendDateSize"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static KcpSegment AllocHGlobal(int appendDateSize)
|
|
||||||
{
|
|
||||||
var total = LocalOffset + HeadOffset + appendDateSize;
|
|
||||||
IntPtr intPtr = Marshal.AllocHGlobal(total);
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
///清零 不知道是不是有更快的清0方法?
|
|
||||||
Span<byte> span = new Span<byte>(intPtr.ToPointer(), total);
|
|
||||||
span.Clear();
|
|
||||||
|
|
||||||
return new KcpSegment((byte*)intPtr.ToPointer(), (uint)appendDateSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 释放非托管内存
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="seg"></param>
|
|
||||||
public static void FreeHGlobal(KcpSegment seg)
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal((IntPtr)seg.ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 以下为本机使用的参数
|
|
||||||
/// <summary>
|
|
||||||
/// offset = 0
|
|
||||||
/// </summary>
|
|
||||||
public uint resendts
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(ptr + 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(ptr + 0) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = 4
|
|
||||||
/// </summary>
|
|
||||||
public uint rto
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(ptr + 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(ptr + 4) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = 8
|
|
||||||
/// </summary>
|
|
||||||
public uint fastack
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(ptr + 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(ptr + 8) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = 12
|
|
||||||
/// </summary>
|
|
||||||
public uint xmit
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(ptr + 12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(ptr + 12) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///以下为需要网络传输的参数
|
|
||||||
public const int LocalOffset = 4 * 4;
|
|
||||||
public const int HeadOffset = KcpConst.IKCP_OVERHEAD;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/>
|
|
||||||
/// </summary>
|
|
||||||
/// https://github.com/skywind3000/kcp/issues/134
|
|
||||||
public uint conv
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(LocalOffset + 0 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(LocalOffset + 0 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 4
|
|
||||||
/// </summary>
|
|
||||||
public byte cmd
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(LocalOffset + 4 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(LocalOffset + 4 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 5
|
|
||||||
/// </summary>
|
|
||||||
public byte frg
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(LocalOffset + 5 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(LocalOffset + 5 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 6
|
|
||||||
/// </summary>
|
|
||||||
public ushort wnd
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(ushort*)(LocalOffset + 6 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(ushort*)(LocalOffset + 6 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 8
|
|
||||||
/// </summary>
|
|
||||||
public uint ts
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(LocalOffset + 8 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(LocalOffset + 8 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para> SendNumber? </para>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 12
|
|
||||||
/// </summary>
|
|
||||||
public uint sn
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(LocalOffset + 12 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(LocalOffset + 12 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 16
|
|
||||||
/// </summary>
|
|
||||||
public uint una
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(LocalOffset + 16 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(LocalOffset + 16 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <para> AppendDateSize </para>
|
|
||||||
/// offset = <see cref="LocalOffset"/> + 20
|
|
||||||
/// </summary>
|
|
||||||
public uint len
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return *(uint*)(LocalOffset + 20 + ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
*(uint*)(LocalOffset + 20 + ptr) = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// https://github.com/skywind3000/kcp/issues/35#issuecomment-263770736
|
|
||||||
public Span<byte> data
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
return new Span<byte>(LocalOffset + HeadOffset + ptr, (int)len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将片段中的要发送的数据拷贝到指定缓冲区
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int Encode(Span<byte> buffer)
|
|
||||||
{
|
|
||||||
var datelen = (int)(HeadOffset + len);
|
|
||||||
|
|
||||||
///备用偏移值 现阶段没有使用
|
|
||||||
const int offset = 0;
|
|
||||||
|
|
||||||
if (KcpConst.IsLittleEndian)
|
|
||||||
{
|
|
||||||
if (BitConverter.IsLittleEndian)
|
|
||||||
{
|
|
||||||
///小端可以一次拷贝
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
///要发送的数据从LocalOffset开始。
|
|
||||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
|
||||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
|
||||||
sendDate.CopyTo(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
|
||||||
buffer[offset + 4] = cmd;
|
|
||||||
buffer[offset + 5] = frg;
|
|
||||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
|
||||||
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
|
||||||
|
|
||||||
data.CopyTo(buffer.Slice(HeadOffset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (BitConverter.IsLittleEndian)
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
|
||||||
buffer[offset + 4] = cmd;
|
|
||||||
buffer[offset + 5] = frg;
|
|
||||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
|
||||||
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
|
||||||
|
|
||||||
data.CopyTo(buffer.Slice(HeadOffset));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
///大端可以一次拷贝
|
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
///要发送的数据从LocalOffset开始。
|
|
||||||
///本结构体调整了要发送字段和单机使用字段的位置,让报头数据和数据连续,节约一次拷贝。
|
|
||||||
Span<byte> sendDate = new Span<byte>(ptr + LocalOffset, datelen);
|
|
||||||
sendDate.CopyTo(buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return datelen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: daab08fc1f00355429c23a1e36993942
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,79 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
public partial class KcpCore<Segment>
|
|
||||||
{
|
|
||||||
public KcpLogMask LogMask { get; set; } = KcpLogMask.IKCP_LOG_PARSE_DATA | KcpLogMask.IKCP_LOG_NEED_SEND | KcpLogMask.IKCP_LOG_DEAD_LINK;
|
|
||||||
|
|
||||||
public virtual bool CanLog(KcpLogMask mask)
|
|
||||||
{
|
|
||||||
if ((mask & LogMask) == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
if (TraceListener != null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
public System.Diagnostics.TraceListener TraceListener { get; set; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public virtual void LogFail(string message)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
TraceListener?.Fail(message);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void LogWriteLine(string message, string category)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
TraceListener?.WriteLine(message, category);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("一定要先判断CanLog 内部判断是否存在TraceListener,避免在没有TraceListener时生成字符串", true)]
|
|
||||||
public virtual void LogWriteLine(string message, KcpLogMask mask)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
if (CanLog(mask))
|
|
||||||
{
|
|
||||||
LogWriteLine(message, mask.ToString());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Flags]
|
|
||||||
public enum KcpLogMask
|
|
||||||
{
|
|
||||||
IKCP_LOG_OUTPUT = 1 << 0,
|
|
||||||
IKCP_LOG_INPUT = 1 << 1,
|
|
||||||
IKCP_LOG_SEND = 1 << 2,
|
|
||||||
IKCP_LOG_RECV = 1 << 3,
|
|
||||||
IKCP_LOG_IN_DATA = 1 << 4,
|
|
||||||
IKCP_LOG_IN_ACK = 1 << 5,
|
|
||||||
IKCP_LOG_IN_PROBE = 1 << 6,
|
|
||||||
IKCP_LOG_IN_WINS = 1 << 7,
|
|
||||||
IKCP_LOG_OUT_DATA = 1 << 8,
|
|
||||||
IKCP_LOG_OUT_ACK = 1 << 9,
|
|
||||||
IKCP_LOG_OUT_PROBE = 1 << 10,
|
|
||||||
IKCP_LOG_OUT_WINS = 1 << 11,
|
|
||||||
|
|
||||||
IKCP_LOG_PARSE_DATA = 1 << 12,
|
|
||||||
IKCP_LOG_NEED_SEND = 1 << 13,
|
|
||||||
IKCP_LOG_DEAD_LINK = 1 << 14,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 39791b0c45bf4864e87385c477d9b50c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,265 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Buffers.Binary;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 动态申请非托管内存
|
|
||||||
/// </summary>
|
|
||||||
public class SimpleSegManager : ISegmentManager<KcpSegment>
|
|
||||||
{
|
|
||||||
public static SimpleSegManager Default { get; } = new SimpleSegManager();
|
|
||||||
public KcpSegment Alloc(int appendDateSize)
|
|
||||||
{
|
|
||||||
return KcpSegment.AllocHGlobal(appendDateSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Free(KcpSegment seg)
|
|
||||||
{
|
|
||||||
KcpSegment.FreeHGlobal(seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Kcp : Kcp<KcpSegment>
|
|
||||||
{
|
|
||||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
|
||||||
: base(conv_, callback, rentable)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KcpIO : KcpIO<KcpSegment>
|
|
||||||
{
|
|
||||||
public KcpIO(uint conv_)
|
|
||||||
: base(conv_)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 申请固定大小非托管内存。使用这个就不能SetMtu了,大小已经写死。
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>需要大量测试</remarks>
|
|
||||||
public unsafe class UnSafeSegManager : ISegmentManager<KcpSegment>
|
|
||||||
{
|
|
||||||
public static UnSafeSegManager Default { get; } = new UnSafeSegManager();
|
|
||||||
/// <summary>
|
|
||||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
|
||||||
/// </summary>
|
|
||||||
public const int blockSize = 512 * 3;
|
|
||||||
public HashSet<IntPtr> header = new HashSet<IntPtr>();
|
|
||||||
public Stack<IntPtr> blocks = new Stack<IntPtr>();
|
|
||||||
public readonly object locker = new object();
|
|
||||||
public UnSafeSegManager()
|
|
||||||
{
|
|
||||||
Alloc();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Alloc()
|
|
||||||
{
|
|
||||||
int count = 50;
|
|
||||||
IntPtr intPtr = Marshal.AllocHGlobal(blockSize * count);
|
|
||||||
header.Add(intPtr);
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
blocks.Push(intPtr + blockSize * i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~UnSafeSegManager()
|
|
||||||
{
|
|
||||||
foreach (var item in header)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public KcpSegment Alloc(int appendDateSize)
|
|
||||||
{
|
|
||||||
lock (locker)
|
|
||||||
{
|
|
||||||
var total = KcpSegment.LocalOffset + KcpSegment.HeadOffset + appendDateSize;
|
|
||||||
if (total > blockSize)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocks.Count > 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Alloc();
|
|
||||||
}
|
|
||||||
|
|
||||||
var ptr = blocks.Pop();
|
|
||||||
Span<byte> span = new Span<byte>(ptr.ToPointer(), blockSize);
|
|
||||||
span.Clear();
|
|
||||||
return new KcpSegment((byte*)ptr.ToPointer(), (uint)appendDateSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Free(KcpSegment seg)
|
|
||||||
{
|
|
||||||
IntPtr ptr = (IntPtr)seg.ptr;
|
|
||||||
blocks.Push(ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Kcp : Kcp<KcpSegment>
|
|
||||||
{
|
|
||||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
|
||||||
: base(conv_, callback, rentable)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KcpIO : KcpIO<KcpSegment>
|
|
||||||
{
|
|
||||||
public KcpIO(uint conv_)
|
|
||||||
: base(conv_)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使用内存池,而不是非托管内存,有内存alloc,但是不多。可以解决Marshal.AllocHGlobal 内核调用带来的性能问题
|
|
||||||
/// </summary>
|
|
||||||
public class PoolSegManager : ISegmentManager<PoolSegManager.Seg>
|
|
||||||
{
|
|
||||||
public static PoolSegManager Default { get; } = new PoolSegManager();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 因为默认mtu是1400,并且内存需要内存行/内存页对齐。这里直接512对齐。
|
|
||||||
/// </summary>
|
|
||||||
public const int blockSize = 512 * 3;
|
|
||||||
public class Seg : IKcpSegment
|
|
||||||
{
|
|
||||||
byte[] cache;
|
|
||||||
public Seg(int blockSize)
|
|
||||||
{
|
|
||||||
cache = Buffers.ArrayPool<byte>.Shared.Rent(blockSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
///以下为需要网络传输的参数
|
|
||||||
public const int LocalOffset = 4 * 4;
|
|
||||||
public const int HeadOffset = Kcp.IKCP_OVERHEAD;
|
|
||||||
|
|
||||||
public byte cmd { get; set; }
|
|
||||||
public uint conv { get; set; }
|
|
||||||
public Span<byte> data => cache.AsSpan().Slice(0, (int)len);
|
|
||||||
public uint fastack { get; set; }
|
|
||||||
public byte frg { get; set; }
|
|
||||||
public uint len { get; internal set; }
|
|
||||||
public uint resendts { get; set; }
|
|
||||||
public uint rto { get; set; }
|
|
||||||
public uint sn { get; set; }
|
|
||||||
public uint ts { get; set; }
|
|
||||||
public uint una { get; set; }
|
|
||||||
public ushort wnd { get; set; }
|
|
||||||
public uint xmit { get; set; }
|
|
||||||
|
|
||||||
public int Encode(Span<byte> buffer)
|
|
||||||
{
|
|
||||||
var datelen = (int)(HeadOffset + len);
|
|
||||||
|
|
||||||
///备用偏移值 现阶段没有使用
|
|
||||||
const int offset = 0;
|
|
||||||
|
|
||||||
if (BitConverter.IsLittleEndian)
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset), conv);
|
|
||||||
buffer[offset + 4] = cmd;
|
|
||||||
buffer[offset + 5] = frg;
|
|
||||||
BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(offset + 6), wnd);
|
|
||||||
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 8), ts);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 12), sn);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 16), una);
|
|
||||||
BinaryPrimitives.WriteUInt32LittleEndian(buffer.Slice(offset + 20), len);
|
|
||||||
|
|
||||||
data.CopyTo(buffer.Slice(HeadOffset));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset), conv);
|
|
||||||
buffer[offset + 4] = cmd;
|
|
||||||
buffer[offset + 5] = frg;
|
|
||||||
BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(offset + 6), wnd);
|
|
||||||
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 8), ts);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 12), sn);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 16), una);
|
|
||||||
BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(offset + 20), len);
|
|
||||||
|
|
||||||
data.CopyTo(buffer.Slice(HeadOffset));
|
|
||||||
}
|
|
||||||
|
|
||||||
return datelen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConcurrentStack<Seg> Pool = new ConcurrentStack<Seg>();
|
|
||||||
public Seg Alloc(int appendDateSize)
|
|
||||||
{
|
|
||||||
if (appendDateSize > blockSize)
|
|
||||||
{
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
if (Pool.TryPop(out var ret))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = new Seg(blockSize);
|
|
||||||
}
|
|
||||||
ret.len = (uint)appendDateSize;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Free(Seg seg)
|
|
||||||
{
|
|
||||||
seg.cmd = 0;
|
|
||||||
seg.conv = 0;
|
|
||||||
seg.fastack = 0;
|
|
||||||
seg.frg = 0;
|
|
||||||
seg.len = 0;
|
|
||||||
seg.resendts = 0;
|
|
||||||
seg.rto = 0;
|
|
||||||
seg.sn = 0;
|
|
||||||
seg.ts = 0;
|
|
||||||
seg.una = 0;
|
|
||||||
seg.wnd = 0;
|
|
||||||
seg.xmit = 0;
|
|
||||||
Pool.Push(seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Kcp : Kcp<Seg>
|
|
||||||
{
|
|
||||||
public Kcp(uint conv_, IKcpCallback callback, IRentable rentable = null)
|
|
||||||
: base(conv_, callback, rentable)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class KcpIO : KcpIO<Seg>
|
|
||||||
{
|
|
||||||
public KcpIO(uint conv_)
|
|
||||||
: base(conv_)
|
|
||||||
{
|
|
||||||
SegmentManager = Default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: e7f0d71aa1362b6488c3173d525fd284
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,65 +0,0 @@
|
|||||||
using System.Buffers;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Cysharp.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp.Simple
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 简单例子。
|
|
||||||
/// </summary>
|
|
||||||
public class SimpleKcpClient : IKcpCallback
|
|
||||||
{
|
|
||||||
UdpClient client;
|
|
||||||
|
|
||||||
public SimpleKcpClient(int port)
|
|
||||||
: this(port, null)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleKcpClient(int port, IPEndPoint endPoint)
|
|
||||||
{
|
|
||||||
client = new UdpClient(port);
|
|
||||||
kcp = new SimpleSegManager.Kcp(2001, this);
|
|
||||||
this.EndPoint = endPoint;
|
|
||||||
BeginRecv();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleSegManager.Kcp kcp { get; }
|
|
||||||
public IPEndPoint EndPoint { get; set; }
|
|
||||||
|
|
||||||
public void Output(IMemoryOwner<byte> buffer, int avalidLength)
|
|
||||||
{
|
|
||||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
|
||||||
client.SendAsync(s, s.Length, EndPoint);
|
|
||||||
buffer.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SendAsync(byte[] datagram, int bytes)
|
|
||||||
{
|
|
||||||
kcp.Send(datagram.AsSpan().Slice(0, bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async UniTask<byte[]> ReceiveAsync()
|
|
||||||
{
|
|
||||||
var (buffer, avalidLength) = kcp.TryRecv();
|
|
||||||
while (buffer == null)
|
|
||||||
{
|
|
||||||
await Task.Delay(10);
|
|
||||||
(buffer, avalidLength) = kcp.TryRecv();
|
|
||||||
}
|
|
||||||
|
|
||||||
var s = buffer.Memory.Span.Slice(0, avalidLength).ToArray();
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void BeginRecv()
|
|
||||||
{
|
|
||||||
var res = await client.ReceiveAsync();
|
|
||||||
EndPoint = res.RemoteEndPoint;
|
|
||||||
kcp.Input(res.Buffer);
|
|
||||||
BeginRecv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ff16ed9b89525df4aa6501c0edc679d5
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,48 +0,0 @@
|
|||||||
using System.Net.Sockets.Kcp.Simple;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
namespace TestServer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 简单例子。
|
|
||||||
/// </summary>
|
|
||||||
class SimpleKcpServer
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
Console.WriteLine("Hello World!");
|
|
||||||
|
|
||||||
SimpleKcpClient kcpClient = new SimpleKcpClient(40001);
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
kcpClient.kcp.Update(DateTimeOffset.UtcNow);
|
|
||||||
await Task.Delay(10);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StartRecv(kcpClient);
|
|
||||||
Console.ReadLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async void StartRecv(SimpleKcpClient client)
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var res = await client.ReceiveAsync();
|
|
||||||
var str = System.Text.Encoding.UTF8.GetString(res);
|
|
||||||
if ("发送一条消息" == str)
|
|
||||||
{
|
|
||||||
Console.WriteLine(str);
|
|
||||||
|
|
||||||
var buffer = System.Text.Encoding.UTF8.GetBytes("回复一条消息");
|
|
||||||
client.SendAsync(buffer, buffer.Length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 97e221b7388e412da59b0f4d200cb891
|
|
||||||
timeCreated: 1682095200
|
|
@@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
//[assembly: InternalsVisibleTo("UnitTestProject1")]
|
|
||||||
|
|
||||||
namespace System.Net.Sockets.Kcp
|
|
||||||
{
|
|
||||||
public static class KcpExtension_FDF71D0BC31D49C48EEA8FAA51F017D4
|
|
||||||
{
|
|
||||||
private static readonly DateTime utc_time = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
||||||
[Obsolete("", true)]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint ConvertTime(this in DateTime time)
|
|
||||||
{
|
|
||||||
return (uint)(Convert.ToInt64(time.Subtract(utc_time).TotalMilliseconds) & 0xffffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly DateTimeOffset utc1970 = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint ConvertTimeOld(this in DateTimeOffset time)
|
|
||||||
{
|
|
||||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint ConvertTime2(this in DateTimeOffset time)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
return (uint)(time.ToUnixTimeMilliseconds() & 0xffffffff);
|
|
||||||
#else
|
|
||||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint ConvertTime(this in DateTimeOffset time)
|
|
||||||
{
|
|
||||||
#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER
|
|
||||||
return (uint)(time.ToUnixTimeMilliseconds());
|
|
||||||
#else
|
|
||||||
return (uint)(Convert.ToInt64(time.Subtract(utc1970).TotalMilliseconds) & 0xffffffff);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static string ToLogString<T>(this T segment, bool local = false)
|
|
||||||
where T : IKcpSegment
|
|
||||||
{
|
|
||||||
if (local)
|
|
||||||
{
|
|
||||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd} [ LocalValue: xmit:{segment.xmit} fastack:{segment.fastack} rto:{segment.rto} ]";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return $"sn:{segment.sn,2} una:{segment.una,2} frg:{segment.frg,2} cmd:{segment.cmd,2} len:{segment.len,2} wnd:{segment.wnd}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static int Encode<T>(this T Seg, IBufferWriter<byte> writer)
|
|
||||||
where T : IKcpSegment
|
|
||||||
{
|
|
||||||
var totalLength = (int)(KcpSegment.HeadOffset + Seg.len);
|
|
||||||
var span = writer.GetSpan(totalLength);
|
|
||||||
Seg.Encode(span);
|
|
||||||
writer.Advance(totalLength);
|
|
||||||
return totalLength;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,11 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: cd298c4aa310a7e448c4694ddc41337e
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
@@ -1,287 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Kcp 网络频道。
|
|
||||||
/// </summary>
|
|
||||||
private sealed class KcpNetworkChannel : NetworkChannelBase
|
|
||||||
{
|
|
||||||
private readonly AsyncCallback _connectCallback;
|
|
||||||
private readonly AsyncCallback _sendCallback;
|
|
||||||
private readonly AsyncCallback _receiveCallback;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public override ServiceType ServiceType => ServiceType.Kcp;
|
|
||||||
|
|
||||||
|
|
||||||
public KcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
|
|
||||||
: base(name, networkChannelHelper)
|
|
||||||
{
|
|
||||||
_connectCallback = ConnectCallback;
|
|
||||||
_sendCallback = SendCallback;
|
|
||||||
_receiveCallback = ReceiveCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
public override void Connect(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
base.Connect(ipAddress, port, userData);
|
|
||||||
MSocket = new Socket(ipAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Initialize network channel failure.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkChannelHelper.PrepareForConnecting();
|
|
||||||
ConnectAsync(ipAddress, port, userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginConnect(ipAddress, port, _connectCallback, new ConnectState(MSocket, userData));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ProcessSend()
|
|
||||||
{
|
|
||||||
if (base.ProcessSend())
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
ConnectState socketUserData = (ConnectState)ar.AsyncState;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socketUserData.Socket.EndConnect(ar);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkChannelConnected != null)
|
|
||||||
{
|
|
||||||
NetworkChannelConnected(this, socketUserData.UserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Active = true;
|
|
||||||
ReceiveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginSend(MSendState.Stream.GetBuffer(), (int)MSendState.Stream.Position,
|
|
||||||
(int)(MSendState.Stream.Length - MSendState.Stream.Position), SocketFlags.None, _sendCallback,
|
|
||||||
MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesSent = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesSent = socket.EndSend(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Stream.Position += bytesSent;
|
|
||||||
if (MSendState.Stream.Position < MSendState.Stream.Length)
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount++;
|
|
||||||
MSendState.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginReceive(MReceiveState.Stream.GetBuffer(), (int)MReceiveState.Stream.Position,
|
|
||||||
(int)(MReceiveState.Stream.Length - MReceiveState.Stream.Position), SocketFlags.None,
|
|
||||||
_receiveCallback, MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesReceived = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesReceived = socket.EndReceive(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesReceived <= 0)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position += bytesReceived;
|
|
||||||
if (MReceiveState.Stream.Position < MReceiveState.Stream.Length)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position = 0L;
|
|
||||||
|
|
||||||
bool processSuccess = false;
|
|
||||||
if (MReceiveState.PacketHeader != null)
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacket();
|
|
||||||
MReceivedPacketCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacketHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processSuccess)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 70ff4fca885c401ea776223622deab75
|
|
||||||
timeCreated: 1682092071
|
|
@@ -1,54 +0,0 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
public class NetUtil
|
|
||||||
{
|
|
||||||
public static bool IsHaveIpV6Address(IPAddress[] ipAddresses, ref IPAddress[] outIPs)
|
|
||||||
{
|
|
||||||
int v6Count = 0;
|
|
||||||
for (int i = 0; i < ipAddresses.Length; i++)
|
|
||||||
{
|
|
||||||
if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily))
|
|
||||||
{
|
|
||||||
v6Count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v6Count > 0)
|
|
||||||
{
|
|
||||||
outIPs = new IPAddress[v6Count];
|
|
||||||
int resIndex = 0;
|
|
||||||
for (int i = 0; i < ipAddresses.Length; i++)
|
|
||||||
{
|
|
||||||
if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily))
|
|
||||||
{
|
|
||||||
outIPs[resIndex++] = ipAddresses[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IPEndPoint GetEndPoint(string server, int port)
|
|
||||||
{
|
|
||||||
IPAddress[] ps = Dns.GetHostAddresses(server);
|
|
||||||
IPAddress[] finalIps = ps;
|
|
||||||
if (Socket.OSSupportsIPv6 && NetUtil.IsHaveIpV6Address(ps, ref finalIps))
|
|
||||||
{
|
|
||||||
Log.Error("socket use addr ipV6: {0}, IP count:{1} AddressFamily[{2}]", server, finalIps.Length, finalIps[0].AddressFamily);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finalIps.Length > 0)
|
|
||||||
{
|
|
||||||
return new IPEndPoint(finalIps[0], port);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 26846cca9e0f4d3c965d58882c1b503e
|
|
||||||
timeCreated: 1681991073
|
|
@@ -1,144 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using GameBase;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络组件。
|
|
||||||
/// </summary>
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public sealed class Network : UnitySingleton<Network>
|
|
||||||
{
|
|
||||||
private NetworkManager m_NetworkManager = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道数量。
|
|
||||||
/// </summary>
|
|
||||||
public int NetworkChannelCount => m_NetworkManager.NetworkChannelCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 游戏框架组件初始化。
|
|
||||||
/// </summary>
|
|
||||||
public override void Awake()
|
|
||||||
{
|
|
||||||
base.Awake();
|
|
||||||
|
|
||||||
// m_NetworkManager = GameFrameworkEntry.GetModule<INetworkManager>();
|
|
||||||
m_NetworkManager = new NetworkManager();
|
|
||||||
if (m_NetworkManager == null)
|
|
||||||
{
|
|
||||||
Log.Fatal("Network manager is invalid.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_NetworkManager.NetworkConnected += OnNetworkConnected;
|
|
||||||
m_NetworkManager.NetworkClosed += OnNetworkClosed;
|
|
||||||
m_NetworkManager.NetworkMissHeartBeat += OnNetworkMissHeartBeat;
|
|
||||||
m_NetworkManager.NetworkError += OnNetworkError;
|
|
||||||
m_NetworkManager.NetworkCustomError += OnNetworkCustomError;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Update()
|
|
||||||
{
|
|
||||||
m_NetworkManager.Update(GameTime.deltaTime, GameTime.unscaledDeltaTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查是否存在网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>是否存在网络频道。</returns>
|
|
||||||
public bool HasNetworkChannel(string name)
|
|
||||||
{
|
|
||||||
return m_NetworkManager.HasNetworkChannel(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>要获取的网络频道。</returns>
|
|
||||||
public INetworkChannel GetNetworkChannel(string name)
|
|
||||||
{
|
|
||||||
return m_NetworkManager.GetNetworkChannel(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>所有网络频道。</returns>
|
|
||||||
public INetworkChannel[] StaticGetAllNetworkChannels()
|
|
||||||
{
|
|
||||||
return m_NetworkManager.GetAllNetworkChannels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>所有网络频道。</returns>
|
|
||||||
public INetworkChannel[] GetAllNetworkChannels()
|
|
||||||
{
|
|
||||||
return m_NetworkManager.GetAllNetworkChannels();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="results">所有网络频道。</param>
|
|
||||||
public void GetAllNetworkChannels(List<INetworkChannel> results)
|
|
||||||
{
|
|
||||||
m_NetworkManager.GetAllNetworkChannels(results);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="serviceType">网络服务类型。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
/// <returns>要创建的网络频道。</returns>
|
|
||||||
public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType,
|
|
||||||
INetworkChannelHelper networkChannelHelper)
|
|
||||||
{
|
|
||||||
return m_NetworkManager.CreateNetworkChannel(name, serviceType, networkChannelHelper);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 销毁网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channelName">网络频道名称。</param>
|
|
||||||
/// <returns>是否销毁网络频道成功。</returns>
|
|
||||||
public bool DestroyNetworkChannel(string channelName)
|
|
||||||
{
|
|
||||||
return m_NetworkManager.DestroyNetworkChannel(channelName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkConnected(INetworkChannel channel, object userdata)
|
|
||||||
{
|
|
||||||
GameEvent.Send(NetworkEvent.NetworkConnectedEvent, channel, userdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkClosed(INetworkChannel channel)
|
|
||||||
{
|
|
||||||
GameEvent.Send(NetworkEvent.NetworkClosedEvent, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount)
|
|
||||||
{
|
|
||||||
GameEvent.Send(NetworkEvent.NetworkMissHeartBeatEvent, channel, missCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError,
|
|
||||||
string errorMessage)
|
|
||||||
{
|
|
||||||
GameEvent.Send(NetworkEvent.NetworkErrorEvent, channel, networkErrorCode, socketError, errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkCustomError(INetworkChannel channel, object userData)
|
|
||||||
{
|
|
||||||
GameEvent.Send(NetworkEvent.NetworkCustomErrorEvent, channel, userData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8edbc7d090fd4bb1ad2c6aa967ff0b76
|
|
||||||
timeCreated: 1682044812
|
|
@@ -1,53 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络错误码。
|
|
||||||
/// </summary>
|
|
||||||
public enum NetworkErrorCode : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 未知错误。
|
|
||||||
/// </summary>
|
|
||||||
Unknown = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 地址族错误。
|
|
||||||
/// </summary>
|
|
||||||
AddressFamilyError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Socket 错误。
|
|
||||||
/// </summary>
|
|
||||||
SocketError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接错误。
|
|
||||||
/// </summary>
|
|
||||||
ConnectError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 发送错误。
|
|
||||||
/// </summary>
|
|
||||||
SendError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 接收错误。
|
|
||||||
/// </summary>
|
|
||||||
ReceiveError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 序列化错误。
|
|
||||||
/// </summary>
|
|
||||||
SerializeError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包头错误。
|
|
||||||
/// </summary>
|
|
||||||
DeserializePacketHeaderError,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化消息包错误。
|
|
||||||
/// </summary>
|
|
||||||
DeserializePacketError
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 0bb8ca2ece7d49d28b1b4fd1bfa027e6
|
|
||||||
timeCreated: 1681993877
|
|
@@ -1,30 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
public class NetworkEvent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网网络连接成功事件。
|
|
||||||
/// </summary>
|
|
||||||
public static int NetworkConnectedEvent = StringId.StringToHash("NetworkEvent.NetworkConnectedEvent");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络连接关闭事件。
|
|
||||||
/// </summary>
|
|
||||||
public static int NetworkClosedEvent = StringId.StringToHash("NetworkEvent.NetworkClosedEvent");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
public static int NetworkErrorEvent = StringId.StringToHash("NetworkEvent.NetworkErrorEvent");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户自定义网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
public static int NetworkCustomErrorEvent = StringId.StringToHash("NetworkEvent.NetworkCustomErrorEvent");
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络心跳包丢失事件。
|
|
||||||
/// </summary>
|
|
||||||
public static int NetworkMissHeartBeatEvent = StringId.StringToHash("NetworkEvent.NetworkMissHeartBeatEvent");
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 8e3b5eb5b76244498181c6117f0e0b1a
|
|
||||||
timeCreated: 1681993978
|
|
@@ -1,23 +0,0 @@
|
|||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
private sealed class ConnectState
|
|
||||||
{
|
|
||||||
private readonly Socket _socket;
|
|
||||||
private readonly object _userData;
|
|
||||||
|
|
||||||
public ConnectState(Socket socket, object userData)
|
|
||||||
{
|
|
||||||
_socket = socket;
|
|
||||||
_userData = userData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Socket Socket => _socket;
|
|
||||||
|
|
||||||
public object UserData => _userData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 36ed7026c9b6441cb8a6438b2048d7f6
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,39 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
private sealed class HeartBeatState
|
|
||||||
{
|
|
||||||
private float _heartBeatElapseSeconds;
|
|
||||||
private int _missHeartBeatCount;
|
|
||||||
|
|
||||||
public HeartBeatState()
|
|
||||||
{
|
|
||||||
_heartBeatElapseSeconds = 0f;
|
|
||||||
_missHeartBeatCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float HeartBeatElapseSeconds
|
|
||||||
{
|
|
||||||
get => _heartBeatElapseSeconds;
|
|
||||||
set => _heartBeatElapseSeconds = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int MissHeartBeatCount
|
|
||||||
{
|
|
||||||
get => _missHeartBeatCount;
|
|
||||||
set => _missHeartBeatCount = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset(bool resetHeartBeatElapseSeconds)
|
|
||||||
{
|
|
||||||
if (resetHeartBeatElapseSeconds)
|
|
||||||
{
|
|
||||||
_heartBeatElapseSeconds = 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
_missHeartBeatCount = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 5368d77f60554db99e9c4d0862660442
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,684 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using GameProto;
|
|
||||||
using Google.Protobuf;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络频道基类。
|
|
||||||
/// </summary>
|
|
||||||
private abstract class NetworkChannelBase : INetworkChannel, IDisposable
|
|
||||||
{
|
|
||||||
private const float DefaultHeartBeatInterval = 30f;
|
|
||||||
|
|
||||||
private readonly string _name;
|
|
||||||
protected readonly Queue<CSPkg> SendPacketPool;
|
|
||||||
protected readonly INetworkChannelHelper NetworkChannelHelper;
|
|
||||||
protected AddressFamily MAddressFamily;
|
|
||||||
protected bool MResetHeartBeatElapseSecondsWhenReceivePacket;
|
|
||||||
protected float MHeartBeatInterval;
|
|
||||||
protected Socket MSocket;
|
|
||||||
protected readonly SendState MSendState;
|
|
||||||
protected readonly ReceiveState MReceiveState;
|
|
||||||
protected readonly HeartBeatState MHeartBeatState;
|
|
||||||
protected int MSentPacketCount;
|
|
||||||
protected int MReceivedPacketCount;
|
|
||||||
protected bool Active;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public Action<NetworkChannelBase, object> NetworkChannelConnected;
|
|
||||||
public Action<NetworkChannelBase> NetworkChannelClosed;
|
|
||||||
public Action<NetworkChannelBase, int> NetworkChannelMissHeartBeat;
|
|
||||||
public Action<NetworkChannelBase, NetworkErrorCode, SocketError, string> NetworkChannelError;
|
|
||||||
public Action<NetworkChannelBase, object> NetworkChannelCustomError;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 消息注册Map。
|
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<int, List<CsMsgDelegate>> _msgHandlerMap = new Dictionary<int, List<CsMsgDelegate>>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 委托缓存堆栈。
|
|
||||||
/// </summary>
|
|
||||||
private readonly Queue<List<CsMsgDelegate>> _cacheHandlerQueue = new Queue<List<CsMsgDelegate>>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 消息包缓存堆栈。
|
|
||||||
/// </summary>
|
|
||||||
private readonly Queue<CSPkg> _packsQueue = new Queue<CSPkg>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道基类的新实例。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper)
|
|
||||||
{
|
|
||||||
_name = name ?? string.Empty;
|
|
||||||
SendPacketPool = new Queue<CSPkg>();
|
|
||||||
NetworkChannelHelper = networkChannelHelper;
|
|
||||||
MAddressFamily = AddressFamily.Unknown;
|
|
||||||
MResetHeartBeatElapseSecondsWhenReceivePacket = false;
|
|
||||||
MHeartBeatInterval = DefaultHeartBeatInterval;
|
|
||||||
MSocket = null;
|
|
||||||
MSendState = new SendState();
|
|
||||||
MReceiveState = new ReceiveState();
|
|
||||||
MHeartBeatState = new HeartBeatState();
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
Active = false;
|
|
||||||
_disposed = false;
|
|
||||||
|
|
||||||
NetworkChannelConnected = null;
|
|
||||||
NetworkChannelClosed = null;
|
|
||||||
NetworkChannelMissHeartBeat = null;
|
|
||||||
NetworkChannelError = null;
|
|
||||||
NetworkChannelCustomError = null;
|
|
||||||
|
|
||||||
networkChannelHelper.Initialize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道名称。
|
|
||||||
/// </summary>
|
|
||||||
public string Name => _name;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道所使用的 Socket。
|
|
||||||
/// </summary>
|
|
||||||
public Socket Socket => MSocket;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取是否已连接。
|
|
||||||
/// </summary>
|
|
||||||
public bool Connected
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (MSocket != null)
|
|
||||||
{
|
|
||||||
return MSocket.Connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public abstract ServiceType ServiceType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络地址类型。
|
|
||||||
/// </summary>
|
|
||||||
public AddressFamily AddressFamily => MAddressFamily;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取要发送的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
public int SendPacketCount => SendPacketPool.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取累计发送的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
public int SentPacketCount => MSentPacketCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取已接收未处理的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
public int ReceivePacketCount => _cacheHandlerQueue.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取累计已接收的消息包数量。
|
|
||||||
/// </summary>
|
|
||||||
public int ReceivedPacketCount => MReceivedPacketCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取或设置当收到消息包时是否重置心跳流逝时间。
|
|
||||||
/// </summary>
|
|
||||||
public bool ResetHeartBeatElapseSecondsWhenReceivePacket
|
|
||||||
{
|
|
||||||
get => MResetHeartBeatElapseSecondsWhenReceivePacket;
|
|
||||||
set => MResetHeartBeatElapseSecondsWhenReceivePacket = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取丢失心跳的次数。
|
|
||||||
/// </summary>
|
|
||||||
public int MissHeartBeatCount => MHeartBeatState.MissHeartBeatCount;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取或设置心跳间隔时长,以秒为单位。
|
|
||||||
/// </summary>
|
|
||||||
public float HeartBeatInterval
|
|
||||||
{
|
|
||||||
get => MHeartBeatInterval;
|
|
||||||
set => MHeartBeatInterval = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取心跳等待时长,以秒为单位。
|
|
||||||
/// </summary>
|
|
||||||
public float HeartBeatElapseSeconds => MHeartBeatState.HeartBeatElapseSeconds;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络频道轮询。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
|
|
||||||
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
|
|
||||||
public virtual void Update(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
if (MSocket == null || !Active)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessSend();
|
|
||||||
ProcessReceive();
|
|
||||||
if (MSocket == null || !Active)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HandleCsMsgOnUpdate();
|
|
||||||
|
|
||||||
if (MHeartBeatInterval > 0f)
|
|
||||||
{
|
|
||||||
bool sendHeartBeat = false;
|
|
||||||
int missHeartBeatCount = 0;
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
if (MSocket == null || !Active)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MHeartBeatState.HeartBeatElapseSeconds += realElapseSeconds;
|
|
||||||
if (MHeartBeatState.HeartBeatElapseSeconds >= MHeartBeatInterval)
|
|
||||||
{
|
|
||||||
sendHeartBeat = true;
|
|
||||||
missHeartBeatCount = MHeartBeatState.MissHeartBeatCount;
|
|
||||||
MHeartBeatState.HeartBeatElapseSeconds = 0f;
|
|
||||||
MHeartBeatState.MissHeartBeatCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sendHeartBeat && NetworkChannelHelper.SendHeartBeat())
|
|
||||||
{
|
|
||||||
if (missHeartBeatCount > 0 && NetworkChannelMissHeartBeat != null)
|
|
||||||
{
|
|
||||||
NetworkChannelMissHeartBeat(this, missHeartBeatCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭网络频道。
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Shutdown()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
NetworkChannelHelper.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 注册网络消息包处理函数。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msgId">网络消息包id。</param>
|
|
||||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
|
||||||
/// <param name="checkRepeat">是否检测重复。</param>
|
|
||||||
public void RegisterMsgHandler(int msgId, CsMsgDelegate msgDelegate,bool checkRepeat = true)
|
|
||||||
{
|
|
||||||
if (msgDelegate == null)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Msg handler is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_msgHandlerMap)
|
|
||||||
{
|
|
||||||
if (!_msgHandlerMap.TryGetValue(msgId, out var listHandle))
|
|
||||||
{
|
|
||||||
listHandle = new List<CsMsgDelegate>();
|
|
||||||
_msgHandlerMap[msgId] = listHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listHandle != null)
|
|
||||||
{
|
|
||||||
if (!listHandle.Contains(msgDelegate))
|
|
||||||
{
|
|
||||||
listHandle.Add(msgDelegate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (checkRepeat)
|
|
||||||
{
|
|
||||||
Log.Error("-------------repeat RegCmdHandle MsgId:{0}-----------",msgId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 移除网络消息包处理函数。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msgId">网络消息包id。</param>
|
|
||||||
/// <param name="msgDelegate">要注册的网络消息包处理函数。</param>
|
|
||||||
public void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate)
|
|
||||||
{
|
|
||||||
lock (_msgHandlerMap)
|
|
||||||
{
|
|
||||||
if (!_msgHandlerMap.TryGetValue(msgId, out List<CsMsgDelegate> listHandle))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listHandle != null)
|
|
||||||
{
|
|
||||||
listHandle.Remove(msgDelegate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
public void Connect(string ipAddress, int port)
|
|
||||||
{
|
|
||||||
IPAddress address = IPAddress.Parse(ipAddress);
|
|
||||||
Connect(address, port, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
public void Connect(IPAddress ipAddress, int port)
|
|
||||||
{
|
|
||||||
Connect(ipAddress, port, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
public virtual void Connect(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
if (MSocket != null)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
MSocket = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ipAddress.AddressFamily)
|
|
||||||
{
|
|
||||||
case System.Net.Sockets.AddressFamily.InterNetwork:
|
|
||||||
MAddressFamily = AddressFamily.IPv4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case System.Net.Sockets.AddressFamily.InterNetworkV6:
|
|
||||||
MAddressFamily = AddressFamily.IPv6;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
string errorMessage = Utility.Text.Format("Not supported address family '{0}'.", ipAddress.AddressFamily);
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.AddressFamilyError, SocketError.Success, errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Reset();
|
|
||||||
MReceiveState.PrepareForPacketHeader(NetworkChannelHelper.PacketHeaderLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭连接并释放所有相关资源。
|
|
||||||
/// </summary>
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
lock (this)
|
|
||||||
{
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Active = false;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.Shutdown(SocketShutdown.Both);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
MSocket.Close();
|
|
||||||
MSocket = null;
|
|
||||||
|
|
||||||
if (NetworkChannelClosed != null)
|
|
||||||
{
|
|
||||||
NetworkChannelClosed(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_packsQueue)
|
|
||||||
{
|
|
||||||
_packsQueue.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_msgHandlerMap)
|
|
||||||
{
|
|
||||||
_msgHandlerMap.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_cacheHandlerQueue)
|
|
||||||
{
|
|
||||||
_cacheHandlerQueue.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向远程主机发送消息包。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
|
||||||
public bool Send(CSPkg packet)
|
|
||||||
{
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "You must connect first.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Active)
|
|
||||||
{
|
|
||||||
string errorMessage = "Socket is not active.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packet == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Packet is invalid.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, SocketError.Success, errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Enqueue(packet);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 向远程主机发送消息包并注册消息回调。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet">要发送的消息包。</param>
|
|
||||||
/// <param name="resHandler">要注册的回调。</param>
|
|
||||||
/// <param name="needShowWaitUI">是否需要等待UI。</param>
|
|
||||||
/// <returns>消息包是否发送成功。</returns>
|
|
||||||
public bool Send(CSPkg packet, CsMsgDelegate resHandler, bool needShowWaitUI = false)
|
|
||||||
{
|
|
||||||
RegisterMsgHandler((int)packet.Head.MsgId,resHandler,false);
|
|
||||||
return Send(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 释放资源。
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 释放资源。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing">释放资源标记。</param>
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
MSendState.Dispose();
|
|
||||||
MReceiveState.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool ProcessSend()
|
|
||||||
{
|
|
||||||
if (MSendState.Stream.Length > 0 || SendPacketPool.Count <= 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (SendPacketPool.Count > 0)
|
|
||||||
{
|
|
||||||
CSPkg csPkg = null;
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
csPkg = SendPacketPool.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool serializeResult = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
serializeResult = NetworkChannelHelper.Serialize(csPkg, MSendState.Stream);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SerializeError, socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!serializeResult)
|
|
||||||
{
|
|
||||||
string errorMessage = "Serialized packet failure.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SerializeError, SocketError.Success, errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Stream.Position = 0L;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void ProcessReceive()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool ProcessPacketHeader()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
IPacketHeader packetHeader = NetworkChannelHelper.DeserializePacketHeader(MReceiveState.Stream, out var customErrorData);
|
|
||||||
|
|
||||||
if (customErrorData != null && NetworkChannelCustomError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelCustomError(this, customErrorData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packetHeader == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Packet header is invalid.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.DeserializePacketHeaderError, SocketError.Success, errorMessage);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.PrepareForPacket(packetHeader);
|
|
||||||
if (packetHeader.PacketLength <= 0)
|
|
||||||
{
|
|
||||||
bool processSuccess = ProcessPacket();
|
|
||||||
MReceivedPacketCount++;
|
|
||||||
return processSuccess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.DeserializePacketHeaderError, socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool ProcessPacket()
|
|
||||||
{
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(MResetHeartBeatElapseSecondsWhenReceivePacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CSPkg csPkg = NetworkChannelHelper.DeserializePacket(MReceiveState.PacketHeader, MReceiveState.Stream, out var customErrorData);
|
|
||||||
|
|
||||||
if (customErrorData != null && NetworkChannelCustomError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelCustomError(this, customErrorData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (csPkg != null)
|
|
||||||
{
|
|
||||||
lock (_cacheHandlerQueue)
|
|
||||||
{
|
|
||||||
if (_msgHandlerMap.TryGetValue((int)csPkg.Head.MsgId, out var listHandle))
|
|
||||||
{
|
|
||||||
_cacheHandlerQueue.Enqueue(listHandle);
|
|
||||||
|
|
||||||
_packsQueue.Enqueue(csPkg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.PrepareForPacketHeader(NetworkChannelHelper.PacketHeaderLength);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.DeserializePacketError, socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 主线程从消息包缓存堆栈/委托缓存堆栈中出列。
|
|
||||||
/// </summary>
|
|
||||||
private void HandleCsMsgOnUpdate()
|
|
||||||
{
|
|
||||||
if (_cacheHandlerQueue.Count <= 0 || _packsQueue.Count <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (CsMsgDelegate handle in _cacheHandlerQueue.Dequeue())
|
|
||||||
{
|
|
||||||
var pack = _packsQueue.Peek();
|
|
||||||
|
|
||||||
if (pack != null)
|
|
||||||
{
|
|
||||||
handle(pack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_packsQueue.Dequeue();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 399b677044924c6bb2a4e5d56628ca42
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
private sealed class ReceiveState : IDisposable
|
|
||||||
{
|
|
||||||
private const int DefaultBufferLength = 1024 * 64;
|
|
||||||
private MemoryStream _stream;
|
|
||||||
private IPacketHeader _packetHeader;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public ReceiveState()
|
|
||||||
{
|
|
||||||
_stream = new MemoryStream(DefaultBufferLength);
|
|
||||||
_packetHeader = null;
|
|
||||||
_disposed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryStream Stream
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IPacketHeader PacketHeader
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _packetHeader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PrepareForPacketHeader(int packetHeaderLength)
|
|
||||||
{
|
|
||||||
Reset(packetHeaderLength, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PrepareForPacket(IPacketHeader packetHeader)
|
|
||||||
{
|
|
||||||
if (packetHeader == null)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Packet header is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Reset(packetHeader.PacketLength, packetHeader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
if (_stream != null)
|
|
||||||
{
|
|
||||||
_stream.Dispose();
|
|
||||||
_stream = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Reset(int targetLength, IPacketHeader packetHeader)
|
|
||||||
{
|
|
||||||
if (targetLength < 0)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Target length is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
_stream.Position = 0L;
|
|
||||||
_stream.SetLength(targetLength);
|
|
||||||
_packetHeader = packetHeader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 89feb1bba2164efea12041106391c284
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,60 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
private sealed class SendState : IDisposable
|
|
||||||
{
|
|
||||||
private const int DefaultBufferLength = 1024 * 64;
|
|
||||||
private MemoryStream _stream;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public SendState()
|
|
||||||
{
|
|
||||||
_stream = new MemoryStream(DefaultBufferLength);
|
|
||||||
_disposed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MemoryStream Stream
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _stream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_stream.Position = 0L;
|
|
||||||
_stream.SetLength(0L);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
if (_stream != null)
|
|
||||||
{
|
|
||||||
_stream.Dispose();
|
|
||||||
_stream = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: df8ba0ec679b463e880f8f599ba23d77
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,281 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// TCP 网络频道。
|
|
||||||
/// </summary>
|
|
||||||
private sealed class TcpNetworkChannel : NetworkChannelBase
|
|
||||||
{
|
|
||||||
private readonly AsyncCallback _connectCallback;
|
|
||||||
private readonly AsyncCallback _sendCallback;
|
|
||||||
private readonly AsyncCallback _receiveCallback;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道的新实例。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
public TcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
|
|
||||||
: base(name, networkChannelHelper)
|
|
||||||
{
|
|
||||||
_connectCallback = ConnectCallback;
|
|
||||||
_sendCallback = SendCallback;
|
|
||||||
_receiveCallback = ReceiveCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public override ServiceType ServiceType
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ServiceType.Tcp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
public override void Connect(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
base.Connect(ipAddress, port, userData);
|
|
||||||
MSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Initialize network channel failure.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkChannelHelper.PrepareForConnecting();
|
|
||||||
ConnectAsync(ipAddress, port, userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ProcessSend()
|
|
||||||
{
|
|
||||||
if (base.ProcessSend())
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginConnect(ipAddress, port, _connectCallback, new ConnectState(MSocket, userData));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
ConnectState socketUserData = (ConnectState)ar.AsyncState;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socketUserData.Socket.EndConnect(ar);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkChannelConnected != null)
|
|
||||||
{
|
|
||||||
NetworkChannelConnected(this, socketUserData.UserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Active = true;
|
|
||||||
ReceiveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginSend(MSendState.Stream.GetBuffer(), (int)MSendState.Stream.Position, (int)(MSendState.Stream.Length - MSendState.Stream.Position), SocketFlags.None, _sendCallback, MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesSent = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesSent = socket.EndSend(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Stream.Position += bytesSent;
|
|
||||||
if (MSendState.Stream.Position < MSendState.Stream.Length)
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount++;
|
|
||||||
MSendState.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginReceive(MReceiveState.Stream.GetBuffer(), (int)MReceiveState.Stream.Position, (int)(MReceiveState.Stream.Length - MReceiveState.Stream.Position), SocketFlags.None, _receiveCallback, MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesReceived = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesReceived = socket.EndReceive(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesReceived <= 0)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position += bytesReceived;
|
|
||||||
if (MReceiveState.Stream.Position < MReceiveState.Stream.Length)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position = 0L;
|
|
||||||
|
|
||||||
bool processSuccess = false;
|
|
||||||
if (MReceiveState.PacketHeader != null)
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacket();
|
|
||||||
MReceivedPacketCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacketHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processSuccess)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: a9660c1a740a423d9c855619aedbebd5
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,257 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 使用同步接收的 TCP 网络频道。
|
|
||||||
/// </summary>
|
|
||||||
private sealed class TcpWithSyncReceiveNetworkChannel : NetworkChannelBase
|
|
||||||
{
|
|
||||||
private readonly AsyncCallback _connectCallback;
|
|
||||||
private readonly AsyncCallback _sendCallback;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道的新实例。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
public TcpWithSyncReceiveNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
|
|
||||||
: base(name, networkChannelHelper)
|
|
||||||
{
|
|
||||||
_connectCallback = ConnectCallback;
|
|
||||||
_sendCallback = SendCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public override ServiceType ServiceType
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ServiceType.TcpWithSyncReceive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
public override void Connect(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
base.Connect(ipAddress, port, userData);
|
|
||||||
MSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Initialize network channel failure.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkChannelHelper.PrepareForConnecting();
|
|
||||||
ConnectAsync(ipAddress, port, userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ProcessSend()
|
|
||||||
{
|
|
||||||
if (base.ProcessSend())
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ProcessReceive()
|
|
||||||
{
|
|
||||||
base.ProcessReceive();
|
|
||||||
while (MSocket.Available > 0)
|
|
||||||
{
|
|
||||||
if (!ReceiveSync())
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginConnect(ipAddress, port, _connectCallback, new ConnectState(MSocket, userData));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
ConnectState socketUserData = (ConnectState)ar.AsyncState;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socketUserData.Socket.EndConnect(ar);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkChannelConnected != null)
|
|
||||||
{
|
|
||||||
NetworkChannelConnected(this, socketUserData.UserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginSend(MSendState.Stream.GetBuffer(), (int)MSendState.Stream.Position, (int)(MSendState.Stream.Length - MSendState.Stream.Position), SocketFlags.None, _sendCallback, MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesSent = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesSent = socket.EndSend(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Stream.Position += bytesSent;
|
|
||||||
if (MSendState.Stream.Position < MSendState.Stream.Length)
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount++;
|
|
||||||
MSendState.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ReceiveSync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int bytesReceived = MSocket.Receive(MReceiveState.Stream.GetBuffer(), (int)MReceiveState.Stream.Position, (int)(MReceiveState.Stream.Length - MReceiveState.Stream.Position), SocketFlags.None);
|
|
||||||
if (bytesReceived <= 0)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position += bytesReceived;
|
|
||||||
if (MReceiveState.Stream.Position < MReceiveState.Stream.Length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position = 0L;
|
|
||||||
|
|
||||||
bool processSuccess = false;
|
|
||||||
if (MReceiveState.PacketHeader != null)
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacket();
|
|
||||||
MReceivedPacketCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacketHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
return processSuccess;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError, socketException != null ? socketException.SocketErrorCode : SocketError.Success, exception.ToString());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: ce9312d4603b4eda99cec01fa82364af
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,291 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
internal sealed partial class NetworkManager
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Udp 网络频道。
|
|
||||||
/// </summary>
|
|
||||||
private sealed class UdpNetworkChannel : NetworkChannelBase
|
|
||||||
{
|
|
||||||
private readonly AsyncCallback _connectCallback;
|
|
||||||
private readonly AsyncCallback _sendCallback;
|
|
||||||
private readonly AsyncCallback _receiveCallback;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public override ServiceType ServiceType => ServiceType.Udp;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络频道的新实例。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
public UdpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper)
|
|
||||||
: base(name, networkChannelHelper)
|
|
||||||
{
|
|
||||||
_connectCallback = ConnectCallback;
|
|
||||||
_sendCallback = SendCallback;
|
|
||||||
_receiveCallback = ReceiveCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 连接到远程主机。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ipAddress">远程主机的 IP 地址。</param>
|
|
||||||
/// <param name="port">远程主机的端口号。</param>
|
|
||||||
/// <param name="userData">用户自定义数据。</param>
|
|
||||||
public override void Connect(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
base.Connect(ipAddress, port, userData);
|
|
||||||
MSocket = new Socket(ipAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
|
||||||
if (MSocket == null)
|
|
||||||
{
|
|
||||||
string errorMessage = "Initialize network channel failure.";
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SocketError, SocketError.Success, errorMessage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new GameFrameworkException(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkChannelHelper.PrepareForConnecting();
|
|
||||||
ConnectAsync(ipAddress, port, userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectAsync(IPAddress ipAddress, int port, object userData)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginConnect(ipAddress, port, _connectCallback, new ConnectState(MSocket, userData));
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool ProcessSend()
|
|
||||||
{
|
|
||||||
if (base.ProcessSend())
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConnectCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
ConnectState socketUserData = (ConnectState)ar.AsyncState;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
socketUserData.Socket.EndConnect(ar);
|
|
||||||
}
|
|
||||||
catch (ObjectDisposedException)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ConnectError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount = 0;
|
|
||||||
MReceivedPacketCount = 0;
|
|
||||||
|
|
||||||
lock (SendPacketPool)
|
|
||||||
{
|
|
||||||
SendPacketPool.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (MHeartBeatState)
|
|
||||||
{
|
|
||||||
MHeartBeatState.Reset(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NetworkChannelConnected != null)
|
|
||||||
{
|
|
||||||
NetworkChannelConnected(this, socketUserData.UserData);
|
|
||||||
}
|
|
||||||
|
|
||||||
Active = true;
|
|
||||||
ReceiveAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginSend(MSendState.Stream.GetBuffer(), (int)MSendState.Stream.Position,
|
|
||||||
(int)(MSendState.Stream.Length - MSendState.Stream.Position), SocketFlags.None, _sendCallback,
|
|
||||||
MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesSent = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesSent = socket.EndSend(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.SendError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSendState.Stream.Position += bytesSent;
|
|
||||||
if (MSendState.Stream.Position < MSendState.Stream.Length)
|
|
||||||
{
|
|
||||||
SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MSentPacketCount++;
|
|
||||||
MSendState.Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
MSocket.BeginReceive(MReceiveState.Stream.GetBuffer(), (int)MReceiveState.Stream.Position,
|
|
||||||
(int)(MReceiveState.Stream.Length - MReceiveState.Stream.Position), SocketFlags.None,
|
|
||||||
_receiveCallback, MSocket);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReceiveCallback(IAsyncResult ar)
|
|
||||||
{
|
|
||||||
Socket socket = (Socket)ar.AsyncState;
|
|
||||||
if (!socket.Connected)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bytesReceived = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bytesReceived = socket.EndReceive(ar);
|
|
||||||
}
|
|
||||||
catch (Exception exception)
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
if (NetworkChannelError != null)
|
|
||||||
{
|
|
||||||
SocketException socketException = exception as SocketException;
|
|
||||||
NetworkChannelError(this, NetworkErrorCode.ReceiveError,
|
|
||||||
socketException?.SocketErrorCode ?? SocketError.Success,
|
|
||||||
exception.ToString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bytesReceived <= 0)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position += bytesReceived;
|
|
||||||
if (MReceiveState.Stream.Position < MReceiveState.Stream.Length)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MReceiveState.Stream.Position = 0L;
|
|
||||||
|
|
||||||
bool processSuccess = false;
|
|
||||||
if (MReceiveState.PacketHeader != null)
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacket();
|
|
||||||
MReceivedPacketCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
processSuccess = ProcessPacketHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processSuccess)
|
|
||||||
{
|
|
||||||
ReceiveAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: cb75f9187f154d83a9fd92d8e1ab318d
|
|
||||||
timeCreated: 1682091504
|
|
@@ -1,317 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net.Sockets;
|
|
||||||
using GameBase;
|
|
||||||
using GameProto;
|
|
||||||
|
|
||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息委托。
|
|
||||||
/// </summary>
|
|
||||||
public delegate void CsMsgDelegate(CSPkg csPkg);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络管理器。
|
|
||||||
/// </summary>
|
|
||||||
internal sealed partial class NetworkManager : Singleton<NetworkManager>, INetworkManager
|
|
||||||
{
|
|
||||||
private readonly Dictionary<string, NetworkChannelBase> _networkChannels;
|
|
||||||
|
|
||||||
private Action<INetworkChannel, object> _networkConnectedEventHandler;
|
|
||||||
private Action<INetworkChannel> _networkClosedEventHandler;
|
|
||||||
private Action<INetworkChannel, int> _networkMissHeartBeatEventHandler;
|
|
||||||
private Action<INetworkChannel, NetworkErrorCode, SocketError, string> _networkErrorEventHandler;
|
|
||||||
private Action<INetworkChannel, object> _networkCustomErrorEventHandler;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化网络管理器的新实例。
|
|
||||||
/// </summary>
|
|
||||||
public NetworkManager()
|
|
||||||
{
|
|
||||||
_networkChannels = new Dictionary<string, NetworkChannelBase>(StringComparer.Ordinal);
|
|
||||||
_networkConnectedEventHandler = null;
|
|
||||||
_networkClosedEventHandler = null;
|
|
||||||
_networkMissHeartBeatEventHandler = null;
|
|
||||||
_networkErrorEventHandler = null;
|
|
||||||
_networkCustomErrorEventHandler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道数量。
|
|
||||||
/// </summary>
|
|
||||||
public int NetworkChannelCount => _networkChannels.Count;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络连接成功事件。
|
|
||||||
/// </summary>
|
|
||||||
public event Action<INetworkChannel, object> NetworkConnected
|
|
||||||
{
|
|
||||||
add => _networkConnectedEventHandler += value;
|
|
||||||
remove => _networkConnectedEventHandler -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络连接关闭事件。
|
|
||||||
/// </summary>
|
|
||||||
public event Action<INetworkChannel> NetworkClosed
|
|
||||||
{
|
|
||||||
add => _networkClosedEventHandler += value;
|
|
||||||
remove => _networkClosedEventHandler -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络心跳包丢失事件。
|
|
||||||
/// </summary>
|
|
||||||
public event Action<INetworkChannel, int> NetworkMissHeartBeat
|
|
||||||
{
|
|
||||||
add => _networkMissHeartBeatEventHandler += value;
|
|
||||||
remove => _networkMissHeartBeatEventHandler -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
public event Action<INetworkChannel, NetworkErrorCode, SocketError, string> NetworkError
|
|
||||||
{
|
|
||||||
add => _networkErrorEventHandler += value;
|
|
||||||
remove => _networkErrorEventHandler -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户自定义网络错误事件。
|
|
||||||
/// </summary>
|
|
||||||
public event Action<INetworkChannel, object> NetworkCustomError
|
|
||||||
{
|
|
||||||
add => _networkCustomErrorEventHandler += value;
|
|
||||||
remove => _networkCustomErrorEventHandler -= value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络管理器轮询。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
|
|
||||||
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
|
|
||||||
public void Update(float elapseSeconds, float realElapseSeconds)
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
|
||||||
{
|
|
||||||
networkChannel.Value.Update(elapseSeconds, realElapseSeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 关闭并清理网络管理器。
|
|
||||||
/// </summary>
|
|
||||||
public void Shutdown()
|
|
||||||
{
|
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
|
||||||
{
|
|
||||||
NetworkChannelBase networkChannelBase = networkChannel.Value;
|
|
||||||
networkChannelBase.NetworkChannelConnected -= OnNetworkChannelConnected;
|
|
||||||
networkChannelBase.NetworkChannelClosed -= OnNetworkChannelClosed;
|
|
||||||
networkChannelBase.NetworkChannelMissHeartBeat -= OnNetworkChannelMissHeartBeat;
|
|
||||||
networkChannelBase.NetworkChannelError -= OnNetworkChannelError;
|
|
||||||
networkChannelBase.NetworkChannelCustomError -= OnNetworkChannelCustomError;
|
|
||||||
networkChannelBase.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
_networkChannels.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查是否存在网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>是否存在网络频道。</returns>
|
|
||||||
public bool HasNetworkChannel(string name)
|
|
||||||
{
|
|
||||||
return _networkChannels.ContainsKey(name ?? string.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>要获取的网络频道。</returns>
|
|
||||||
public INetworkChannel GetNetworkChannel(string name)
|
|
||||||
{
|
|
||||||
if (_networkChannels.TryGetValue(name ?? string.Empty, out var networkChannel))
|
|
||||||
{
|
|
||||||
return networkChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>所有网络频道。</returns>
|
|
||||||
public INetworkChannel[] GetAllNetworkChannels()
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
INetworkChannel[] results = new INetworkChannel[_networkChannels.Count];
|
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
|
||||||
{
|
|
||||||
results[index++] = networkChannel.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="results">所有网络频道。</param>
|
|
||||||
public void GetAllNetworkChannels(List<INetworkChannel> results)
|
|
||||||
{
|
|
||||||
if (results == null)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Results is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
results.Clear();
|
|
||||||
foreach (KeyValuePair<string, NetworkChannelBase> networkChannel in _networkChannels)
|
|
||||||
{
|
|
||||||
results.Add(networkChannel.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 创建网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <param name="serviceType">网络服务类型。</param>
|
|
||||||
/// <param name="networkChannelHelper">网络频道辅助器。</param>
|
|
||||||
/// <returns>要创建的网络频道。</returns>
|
|
||||||
public INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType,
|
|
||||||
INetworkChannelHelper networkChannelHelper)
|
|
||||||
{
|
|
||||||
if (networkChannelHelper == null)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Network channel helper is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (networkChannelHelper.PacketHeaderLength < 0)
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException("Packet header length is invalid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasNetworkChannel(name))
|
|
||||||
{
|
|
||||||
throw new GameFrameworkException(Utility.Text.Format("Already exist network channel '{0}'.",
|
|
||||||
name ?? string.Empty));
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkChannelBase networkChannel = null;
|
|
||||||
switch (serviceType)
|
|
||||||
{
|
|
||||||
case ServiceType.Tcp:
|
|
||||||
networkChannel = new TcpNetworkChannel(name, networkChannelHelper);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ServiceType.TcpWithSyncReceive:
|
|
||||||
networkChannel = new TcpWithSyncReceiveNetworkChannel(name, networkChannelHelper);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ServiceType.Udp:
|
|
||||||
networkChannel = new UdpNetworkChannel(name, networkChannelHelper);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ServiceType.Kcp:
|
|
||||||
networkChannel = new KcpNetworkChannel(name, networkChannelHelper);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new GameFrameworkException(Utility.Text.Format("Not supported service type '{0}'.",
|
|
||||||
serviceType));
|
|
||||||
}
|
|
||||||
|
|
||||||
networkChannel.NetworkChannelConnected += OnNetworkChannelConnected;
|
|
||||||
networkChannel.NetworkChannelClosed += OnNetworkChannelClosed;
|
|
||||||
networkChannel.NetworkChannelMissHeartBeat += OnNetworkChannelMissHeartBeat;
|
|
||||||
networkChannel.NetworkChannelError += OnNetworkChannelError;
|
|
||||||
networkChannel.NetworkChannelCustomError += OnNetworkChannelCustomError;
|
|
||||||
_networkChannels.Add(name, networkChannel);
|
|
||||||
return networkChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 销毁网络频道。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">网络频道名称。</param>
|
|
||||||
/// <returns>是否销毁网络频道成功。</returns>
|
|
||||||
public bool DestroyNetworkChannel(string name)
|
|
||||||
{
|
|
||||||
if (_networkChannels.TryGetValue(name ?? string.Empty, out var networkChannel))
|
|
||||||
{
|
|
||||||
networkChannel.NetworkChannelConnected -= OnNetworkChannelConnected;
|
|
||||||
networkChannel.NetworkChannelClosed -= OnNetworkChannelClosed;
|
|
||||||
networkChannel.NetworkChannelMissHeartBeat -= OnNetworkChannelMissHeartBeat;
|
|
||||||
networkChannel.NetworkChannelError -= OnNetworkChannelError;
|
|
||||||
networkChannel.NetworkChannelCustomError -= OnNetworkChannelCustomError;
|
|
||||||
networkChannel.Shutdown();
|
|
||||||
return name != null && _networkChannels.Remove(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkChannelConnected(NetworkChannelBase networkChannel, object userData)
|
|
||||||
{
|
|
||||||
if (_networkConnectedEventHandler != null)
|
|
||||||
{
|
|
||||||
lock (_networkConnectedEventHandler)
|
|
||||||
{
|
|
||||||
_networkConnectedEventHandler(networkChannel, userData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkChannelClosed(NetworkChannelBase networkChannel)
|
|
||||||
{
|
|
||||||
if (_networkClosedEventHandler != null)
|
|
||||||
{
|
|
||||||
lock (_networkClosedEventHandler)
|
|
||||||
{
|
|
||||||
_networkClosedEventHandler(networkChannel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkChannelMissHeartBeat(NetworkChannelBase networkChannel, int missHeartBeatCount)
|
|
||||||
{
|
|
||||||
if (_networkMissHeartBeatEventHandler != null)
|
|
||||||
{
|
|
||||||
lock (_networkMissHeartBeatEventHandler)
|
|
||||||
{
|
|
||||||
_networkMissHeartBeatEventHandler(networkChannel, missHeartBeatCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkChannelError(NetworkChannelBase networkChannel, NetworkErrorCode errorCode,
|
|
||||||
SocketError socketErrorCode, string errorMessage)
|
|
||||||
{
|
|
||||||
if (_networkErrorEventHandler != null)
|
|
||||||
{
|
|
||||||
lock (_networkErrorEventHandler)
|
|
||||||
{
|
|
||||||
_networkErrorEventHandler(networkChannel, errorCode, socketErrorCode, errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnNetworkChannelCustomError(NetworkChannelBase networkChannel, object customErrorData)
|
|
||||||
{
|
|
||||||
if (_networkCustomErrorEventHandler != null)
|
|
||||||
{
|
|
||||||
lock (_networkCustomErrorEventHandler)
|
|
||||||
{
|
|
||||||
_networkCustomErrorEventHandler(networkChannel, customErrorData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 96c0584a53cd4b8e88949b77eac8e1ce
|
|
||||||
timeCreated: 1681994450
|
|
@@ -1,28 +0,0 @@
|
|||||||
namespace TEngine
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络服务类型。
|
|
||||||
/// </summary>
|
|
||||||
public enum ServiceType : byte
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// TCP 网络服务。
|
|
||||||
/// </summary>
|
|
||||||
Tcp = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 使用同步接收的 TCP 网络服务。
|
|
||||||
/// </summary>
|
|
||||||
TcpWithSyncReceive = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UDP 网络服务。
|
|
||||||
/// </summary>
|
|
||||||
Udp = 2,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// KCP 网络服务。
|
|
||||||
/// </summary>
|
|
||||||
Kcp = 3,
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: c80c2ca9fea74504a4a79c2ecd01d065
|
|
||||||
timeCreated: 1681993754
|
|
@@ -1,47 +0,0 @@
|
|||||||
using TEngine;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包头。
|
|
||||||
/// </summary>
|
|
||||||
public class PacketHeader : IPacketHeader, IMemory
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包Id。
|
|
||||||
/// </summary>
|
|
||||||
public short Id
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包长度。
|
|
||||||
/// </summary>
|
|
||||||
public int PacketLength
|
|
||||||
{
|
|
||||||
get;
|
|
||||||
set;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 网络消息包是否合法。
|
|
||||||
/// </summary>
|
|
||||||
public bool IsValid
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return PacketLength >= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 清除网络消息包头。
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
PacketLength = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 30edc133b73141598b5351d4afc2ad93
|
|
||||||
timeCreated: 1682046771
|
|
@@ -1,30 +0,0 @@
|
|||||||
using System;
|
|
||||||
using GameProto;
|
|
||||||
|
|
||||||
namespace GameLogic
|
|
||||||
{
|
|
||||||
public partial class ProtobufUtility
|
|
||||||
{
|
|
||||||
public static UInt32 MsgEcho = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据msgId来生成一个数据包
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msgId"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static CSPkg BuildCsMsg(int msgId)
|
|
||||||
{
|
|
||||||
CSPkg tmp = new CSPkg();
|
|
||||||
tmp.Head = new CSPkgHead();
|
|
||||||
tmp.Head.MsgId = (UInt16)msgId;
|
|
||||||
tmp.Head.Echo = GetNextEcho();
|
|
||||||
// tmp.Body.create(msgId);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static UInt32 GetNextEcho()
|
|
||||||
{
|
|
||||||
return ++MsgEcho;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 4d4427af9be14c56b01e4674853c09a8
|
|
||||||
timeCreated: 1684334469
|
|
@@ -1,217 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using GameProto;
|
|
||||||
using Google.Protobuf;
|
|
||||||
using TEngine;
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ProtoBuf工具
|
|
||||||
/// </summary>
|
|
||||||
public partial class ProtobufUtility
|
|
||||||
{
|
|
||||||
private const int BufferHead = 4;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 消息压入内存流。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
public static void ToStream(object message, MemoryStream stream)
|
|
||||||
{
|
|
||||||
((IMessage)message).WriteTo(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 消息压入内存流。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
public static void ToStream(object message, Stream stream)
|
|
||||||
{
|
|
||||||
((IMessage)message).WriteTo(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 消息压入内存流。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="packet"></param>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
public static void ToStreamWithHead(CSPkg packet, MemoryStream stream)
|
|
||||||
{
|
|
||||||
byte[] data = packet.ToByteArray();
|
|
||||||
byte[] head = BitConverter.GetBytes(data.Length);
|
|
||||||
byte[] ret = head.Concat(data).ToArray();
|
|
||||||
stream.Write(ret);
|
|
||||||
((MemoryStream)stream).SetLength(BufferHead + data.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 比特流解析。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="bytes"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="count"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static object FromBytes(Type type, byte[] bytes, int index, int count)
|
|
||||||
{
|
|
||||||
object message = Activator.CreateInstance(type);
|
|
||||||
((IMessage)message).MergeFrom(bytes, index, count);
|
|
||||||
ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
if (iSupportInitialize == null)
|
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
iSupportInitialize.EndInit();
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 比特流解析。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="instance"></param>
|
|
||||||
/// <param name="bytes"></param>
|
|
||||||
/// <param name="index"></param>
|
|
||||||
/// <param name="count"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static object FromBytes(object instance, byte[] bytes, int index, int count)
|
|
||||||
{
|
|
||||||
object message = instance;
|
|
||||||
((IMessage)message).MergeFrom(bytes, index, count);
|
|
||||||
ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
if (iSupportInitialize == null)
|
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
iSupportInitialize.EndInit();
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从内存流取出。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static object FromStream(Type type, MemoryStream stream)
|
|
||||||
{
|
|
||||||
object message = Activator.CreateInstance(type);
|
|
||||||
((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length);
|
|
||||||
ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
if (iSupportInitialize == null)
|
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
iSupportInitialize.EndInit();
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从内存流取出。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <param name="stream"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static object FromStream(object message, MemoryStream stream)
|
|
||||||
{
|
|
||||||
// TODO 这个message最好从池中获取,减少gc
|
|
||||||
((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length);
|
|
||||||
ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
if (iSupportInitialize == null)
|
|
||||||
{
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
iSupportInitialize.EndInit();
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static byte[] ToBytes(object message)
|
|
||||||
{
|
|
||||||
return ((IMessage)message).ToByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 序列化protobuf
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="message"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static byte[] Serialize(object message)
|
|
||||||
{
|
|
||||||
return ((IMessage)message).ToByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化protobuf
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="dataBytes"></param>
|
|
||||||
/// <param name="offset"></param>
|
|
||||||
/// <param name="bodyCount"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static T Deserialize<T>(byte[] dataBytes, int offset, int bodyCount) where T : IMessage, new()
|
|
||||||
{
|
|
||||||
T msg = new T();
|
|
||||||
msg = (T)msg.Descriptor.Parser.ParseFrom(dataBytes, offset, bodyCount);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 反序列化protobuf
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataBytes"></param>
|
|
||||||
/// <param name="offset"></param>
|
|
||||||
/// <param name="bodyCount"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static CSPkg Deserialize(byte[] dataBytes, int offset, int bodyCount)
|
|
||||||
{
|
|
||||||
var msg = (CSPkg)CSPkg.Descriptor.Parser.ParseFrom(dataBytes, offset, bodyCount);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetHighOrder(int cmdMerge)
|
|
||||||
{
|
|
||||||
return cmdMerge >> 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetLowOrder(int cmdMerge)
|
|
||||||
{
|
|
||||||
return cmdMerge & 65535;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly StringBuilder StringBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
public static string GetBuffer(byte[] buffer)
|
|
||||||
{
|
|
||||||
StringBuilder.Length = 0;
|
|
||||||
StringBuilder.Append("[");
|
|
||||||
for (int i = 0; i < buffer.Length; i++)
|
|
||||||
{
|
|
||||||
if (i == buffer.Length - 1)
|
|
||||||
{
|
|
||||||
StringBuilder.Append(buffer[i]);
|
|
||||||
StringBuilder.Append("]");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringBuilder.Append(buffer[i]);
|
|
||||||
StringBuilder.Append(",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return StringBuilder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void PrintBuffer(byte[] buffer)
|
|
||||||
{
|
|
||||||
Log.Debug(GetBuffer(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 972ca4545003463d8710de956f0fde66
|
|
||||||
timeCreated: 1682047511
|
|
@@ -1,138 +0,0 @@
|
|||||||
// using System;
|
|
||||||
// using System.ComponentModel;
|
|
||||||
// using System.IO;
|
|
||||||
// using ProtoBuf;
|
|
||||||
// using ProtoBuf.Meta;
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// ProtoBuf工具
|
|
||||||
// /// </summary>
|
|
||||||
// public class ProtobufUtils
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// 消息压入内存流。
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="message"></param>
|
|
||||||
// /// <param name="stream"></param>
|
|
||||||
// public static void ToStream(object message, MemoryStream stream)
|
|
||||||
// {
|
|
||||||
// ((IMessage)message).WriteTo(stream);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 比特流解析。
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="type"></param>
|
|
||||||
// /// <param name="bytes"></param>
|
|
||||||
// /// <param name="index"></param>
|
|
||||||
// /// <param name="count"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static object FromBytes(Type type, byte[] bytes, int index, int count)
|
|
||||||
// {
|
|
||||||
// object message = Activator.CreateInstance(type);
|
|
||||||
// ((IMessage)message).MergeFrom(bytes, index, count);
|
|
||||||
// ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
// if (iSupportInitialize == null)
|
|
||||||
// {
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// iSupportInitialize.EndInit();
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 比特流解析。
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="instance"></param>
|
|
||||||
// /// <param name="bytes"></param>
|
|
||||||
// /// <param name="index"></param>
|
|
||||||
// /// <param name="count"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static object FromBytes(object instance, byte[] bytes, int index, int count)
|
|
||||||
// {
|
|
||||||
// object message = instance;
|
|
||||||
// ((IMessage)message).MergeFrom(bytes, index, count);
|
|
||||||
// ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
// if (iSupportInitialize == null)
|
|
||||||
// {
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// iSupportInitialize.EndInit();
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 从内存流取出。
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="type"></param>
|
|
||||||
// /// <param name="stream"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static object FromStream(Type type, MemoryStream stream)
|
|
||||||
// {
|
|
||||||
// object message = Activator.CreateInstance(type);
|
|
||||||
// ((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length);
|
|
||||||
// ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
// if (iSupportInitialize == null)
|
|
||||||
// {
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// iSupportInitialize.EndInit();
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 从内存流取出。
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="message"></param>
|
|
||||||
// /// <param name="stream"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static object FromStream(object message, MemoryStream stream)
|
|
||||||
// {
|
|
||||||
// // TODO 这个message最好从池中获取,减少gc
|
|
||||||
// ((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length);
|
|
||||||
// ISupportInitialize iSupportInitialize = message as ISupportInitialize;
|
|
||||||
// if (iSupportInitialize == null)
|
|
||||||
// {
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// iSupportInitialize.EndInit();
|
|
||||||
// return message;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 序列化protobuf
|
|
||||||
// /// </summary>
|
|
||||||
// /// <param name="message"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static byte[] Serialize(object message)
|
|
||||||
// {
|
|
||||||
// return ((IMessage)message).ToByteArray();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /// <summary>
|
|
||||||
// /// 反序列化protobuf
|
|
||||||
// /// </summary>
|
|
||||||
// /// <typeparam name="T"></typeparam>
|
|
||||||
// /// <param name="dataBytes"></param>
|
|
||||||
// /// <returns></returns>
|
|
||||||
// public static T Deserialize<T>(byte[] dataBytes) where T : IMessage, new()
|
|
||||||
// {
|
|
||||||
// T msg = new T();
|
|
||||||
// msg = (T)msg.Descriptor.Parser.ParseFrom(dataBytes);
|
|
||||||
// return msg;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static int GetHighOrder(int cmdMerge)
|
|
||||||
// {
|
|
||||||
// return cmdMerge >> 16;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public static int GetLowOrder(int cmdMerge)
|
|
||||||
// {
|
|
||||||
// return cmdMerge & 65535;
|
|
||||||
// }
|
|
||||||
// }
|
|
@@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: 26e2c268a764bad4eb7412ad65f822e2
|
|
||||||
timeCreated: 1682047511
|
|
Reference in New Issue
Block a user