Remove old networkmodule

Remove old networkmodule
This commit is contained in:
ALEXTANG
2023-07-12 17:35:05 +08:00
parent 4c39ab79ca
commit 5e69129667
90 changed files with 0 additions and 8982 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 22d1bcdd317c6f64daeb9bf9742495f2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3301ba180cdc446bbdf823c860ae7a68
timeCreated: 1682045195

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 145b951be40d41dea06e76bd967a5d15
timeCreated: 1682045847

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 3e4e637f3da340dd9512150c3e2ff087
timeCreated: 1684334948

View File

@@ -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
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8c5441725c9f4d98a7790dc76a6a0c48
timeCreated: 1684331687

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7d667fb84fed4c5f93a06c464585512f
timeCreated: 1684333223

View File

@@ -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()
{
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: bf86ce2ddfb5429abecfb06257c86acd
timeCreated: 1682045961

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: e77590d750cf480baae2468038654bc0
timeCreated: 1681991042

View File

@@ -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
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: dd8e805430d24bdb8c2679b59ba7c2d6
timeCreated: 1681993653

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9aefe7f3e0fd485091a20579b29872a7
timeCreated: 1681994393

View File

@@ -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);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 09b554ed41c546a1bc40e5be392f836a
timeCreated: 1681993830

View File

@@ -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);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1f847791db5a4e6cbbc39dd56a6888d9
timeCreated: 1681993713

View File

@@ -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);
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7de8458fc50e4cd3b49ef73d35ad9763
timeCreated: 1681993806

View File

@@ -1,16 +0,0 @@
namespace TEngine
{
/// <summary>
/// 网络消息包头接口。
/// </summary>
public interface IPacketHeader
{
/// <summary>
/// 获取网络消息包长度。
/// </summary>
int PacketLength
{
get;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7659e4a617d84126b0a548077fb4fdcc
timeCreated: 1681994226

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 6d988d26345f402f9177488d8921184f
timeCreated: 1682092063

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 6dff8f4f5dd94df49bf6d616fb11cc9c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e3c2f9de3c34ffc409549b86ac3fc530
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b0ca98e42147b3948bc8d30f24e9c366
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a9259bb6b548e46459cc766d8fe2faeb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: b3840e90e37f0194bbfafb564ff80d45
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: fcc0172c723e55a4389ad2f62b68a3fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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));
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3c30d30fa46372948b60bd56eec47fe6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a058d8fa8a92dc94395bfd6e1f6fdb8f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: daab08fc1f00355429c23a1e36993942
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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,
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 39791b0c45bf4864e87385c477d9b50c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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;
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e7f0d71aa1362b6488c3173d525fd284
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ff16ed9b89525df4aa6501c0edc679d5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 97e221b7388e412da59b0f4d200cb891
timeCreated: 1682095200

View File

@@ -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;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: cd298c4aa310a7e448c4694ddc41337e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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();
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 70ff4fca885c401ea776223622deab75
timeCreated: 1682092071

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 26846cca9e0f4d3c965d58882c1b503e
timeCreated: 1681991073

View File

@@ -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);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8edbc7d090fd4bb1ad2c6aa967ff0b76
timeCreated: 1682044812

View File

@@ -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
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0bb8ca2ece7d49d28b1b4fd1bfa027e6
timeCreated: 1681993877

View File

@@ -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");
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8e3b5eb5b76244498181c6117f0e0b1a
timeCreated: 1681993978

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 36ed7026c9b6441cb8a6438b2048d7f6
timeCreated: 1681994450

View File

@@ -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;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5368d77f60554db99e9c4d0862660442
timeCreated: 1681994450

View File

@@ -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);
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 399b677044924c6bb2a4e5d56628ca42
timeCreated: 1681994450

View File

@@ -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;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 89feb1bba2164efea12041106391c284
timeCreated: 1681994450

View File

@@ -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;
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: df8ba0ec679b463e880f8f599ba23d77
timeCreated: 1681994450

View File

@@ -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;
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a9660c1a740a423d9c855619aedbebd5
timeCreated: 1681994450

View File

@@ -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;
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ce9312d4603b4eda99cec01fa82364af
timeCreated: 1681994450

View File

@@ -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();
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: cb75f9187f154d83a9fd92d8e1ab318d
timeCreated: 1682091504

View File

@@ -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);
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 96c0584a53cd4b8e88949b77eac8e1ce
timeCreated: 1681994450

View File

@@ -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,
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c80c2ca9fea74504a4a79c2ecd01d065
timeCreated: 1681993754

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 30edc133b73141598b5351d4afc2ad93
timeCreated: 1682046771

View File

@@ -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;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4d4427af9be14c56b01e4674853c09a8
timeCreated: 1684334469

View File

@@ -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));
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 972ca4545003463d8710de956f0fde66
timeCreated: 1682047511

View File

@@ -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;
// }
// }

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 26e2c268a764bad4eb7412ad65f822e2
timeCreated: 1682047511