From 4f908d46afa3b71e6a77a448a696ae6b6bf500ee Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Fri, 21 Apr 2023 10:29:42 +0800 Subject: [PATCH] [+] Network Module [+] Network Module --- .../Runtime/GameFramework}/Network.meta | 0 .../GameFramework/Network/AddressFamily.cs | 23 + .../Network/AddressFamily.cs.meta | 3 + .../GameFramework/Network/Interface.meta | 3 + .../Network/Interface/INetworkChannel.cs | 170 +++++ .../Network/Interface/INetworkChannel.cs.meta | 3 + .../Interface/INetworkChannelHelper.cs | 66 ++ .../Interface/INetworkChannelHelper.cs.meta | 3 + .../Network/Interface/INetworkManager.cs | 84 +++ .../Network/Interface/INetworkManager.cs.meta | 3 + .../Network/Interface/IPacketHeader.cs | 16 + .../Network/Interface/IPacketHeader.cs.meta | 3 + .../Runtime/GameFramework}/Network/NetUtil.cs | 4 +- .../GameFramework}/Network/NetUtil.cs.meta | 0 .../GameFramework/Network/NetworkErrorCode.cs | 53 ++ .../Network/NetworkErrorCode.cs.meta | 3 + .../GameFramework/Network/NetworkEvent.cs | 30 + .../Network/NetworkEvent.cs.meta | 3 + .../Network/NetworkManager.ConnectState.cs | 23 + .../NetworkManager.ConnectState.cs.meta | 3 + .../Network/NetworkManager.HeartBeatState.cs | 39 ++ .../NetworkManager.HeartBeatState.cs.meta | 3 + .../NetworkManager.NetworkChannelBase.cs | 663 ++++++++++++++++++ .../NetworkManager.NetworkChannelBase.cs.meta | 3 + .../Network/NetworkManager.ReceiveState.cs | 91 +++ .../NetworkManager.ReceiveState.cs.meta | 3 + .../Network/NetworkManager.SendState.cs | 60 ++ .../Network/NetworkManager.SendState.cs.meta | 3 + .../NetworkManager.TcpNetworkChannel.cs | 281 ++++++++ .../NetworkManager.TcpNetworkChannel.cs.meta | 3 + ...anager.TcpWithSyncReceiveNetworkChannel.cs | 257 +++++++ ...r.TcpWithSyncReceiveNetworkChannel.cs.meta | 3 + .../GameFramework/Network/NetworkManager.cs | 302 ++++++++ .../Network/NetworkManager.cs.meta | 3 + .../Runtime/GameFramework/Network/Packet.cs | 18 + .../GameFramework/Network/Packet.cs.meta | 3 + .../GameFramework/Network/ServiceType.cs | 28 + .../GameFramework/Network/ServiceType.cs.meta | 3 + 38 files changed, 2260 insertions(+), 2 deletions(-) rename Assets/{GameScripts/HotFix/GameLogic => TEngine/Runtime/GameFramework}/Network.meta (100%) create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs.meta rename Assets/{GameScripts/HotFix/GameLogic => TEngine/Runtime/GameFramework}/Network/NetUtil.cs (86%) rename Assets/{GameScripts/HotFix/GameLogic => TEngine/Runtime/GameFramework}/Network/NetUtil.cs.meta (100%) create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Packet.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/Packet.cs.meta create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs create mode 100644 Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs.meta diff --git a/Assets/GameScripts/HotFix/GameLogic/Network.meta b/Assets/TEngine/Runtime/GameFramework/Network.meta similarity index 100% rename from Assets/GameScripts/HotFix/GameLogic/Network.meta rename to Assets/TEngine/Runtime/GameFramework/Network.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs b/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs new file mode 100644 index 00000000..abc8568c --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs @@ -0,0 +1,23 @@ +namespace TEngine +{ + /// + /// 网络地址类型。 + /// + public enum AddressFamily : byte + { + /// + /// 未知。 + /// + Unknown = 0, + + /// + /// IP 版本 4。 + /// + IPv4, + + /// + /// IP 版本 6。 + /// + IPv6 + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs.meta new file mode 100644 index 00000000..fb25e3eb --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/AddressFamily.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dd8e805430d24bdb8c2679b59ba7c2d6 +timeCreated: 1681993653 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface.meta b/Assets/TEngine/Runtime/GameFramework/Network/Interface.meta new file mode 100644 index 00000000..31761b46 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9aefe7f3e0fd485091a20579b29872a7 +timeCreated: 1681994393 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs new file mode 100644 index 00000000..9d214314 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs @@ -0,0 +1,170 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine +{ + /// + /// 网络频道接口。 + /// + public interface INetworkChannel + { + /// + /// 获取网络频道名称。 + /// + string Name + { + get; + } + + /// + /// 获取网络频道所使用的 Socket。 + /// + Socket Socket + { + get; + } + + /// + /// 获取是否已连接。 + /// + bool Connected + { + get; + } + + /// + /// 获取网络服务类型。 + /// + ServiceType ServiceType + { + get; + } + + /// + /// 获取网络地址类型。 + /// + AddressFamily AddressFamily + { + get; + } + + /// + /// 获取要发送的消息包数量。 + /// + int SendPacketCount + { + get; + } + + /// + /// 获取累计发送的消息包数量。 + /// + int SentPacketCount + { + get; + } + + /// + /// 获取已接收未处理的消息包数量。 + /// + int ReceivePacketCount + { + get; + } + + /// + /// 获取累计已接收的消息包数量。 + /// + int ReceivedPacketCount + { + get; + } + + /// + /// 获取或设置当收到消息包时是否重置心跳流逝时间。 + /// + bool ResetHeartBeatElapseSecondsWhenReceivePacket + { + get; + set; + } + + /// + /// 获取丢失心跳的次数。 + /// + int MissHeartBeatCount + { + get; + } + + /// + /// 获取或设置心跳间隔时长,以秒为单位。 + /// + float HeartBeatInterval + { + get; + set; + } + + /// + /// 获取心跳等待时长,以秒为单位。 + /// + float HeartBeatElapseSeconds + { + get; + } + + /// + /// 注册网络消息包处理函数。 + /// + /// 网络消息包id。 + /// 要注册的网络消息包处理函数。 + void RegisterMsgHandler(int msgId, CsMsgDelegate msgDelegate); + + /// + /// 移除网络消息包处理函数。 + /// + /// 网络消息包id。 + /// 要注册的网络消息包处理函数。 + void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate); + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + void Connect(IPAddress ipAddress, int port); + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + void Connect(IPAddress ipAddress, int port, object userData); + + /// + /// 关闭网络频道。 + /// + void Close(); + + /// + /// 向远程主机发送消息包。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + /// 消息包是否发送成功。 + bool Send(T packet) where T : Packet; + + /// + /// 向远程主机发送消息包并注册消息回调。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + /// 要注册的回调。 + /// 是否需要等待UI。 + /// 消息包是否发送成功。 + bool Send(T pack, CsMsgDelegate resHandler, bool needShowWaitUI = false) where T : Packet; + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs.meta new file mode 100644 index 00000000..24e3bb66 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 09b554ed41c546a1bc40e5be392f836a +timeCreated: 1681993830 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs new file mode 100644 index 00000000..7853a5d3 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs @@ -0,0 +1,66 @@ +using System.IO; + +namespace TEngine +{ + /// + /// 网络频道辅助器接口。 + /// + public interface INetworkChannelHelper + { + /// + /// 获取消息包头长度。 + /// + int PacketHeaderLength + { + get; + } + + /// + /// 初始化网络频道辅助器。 + /// + /// 网络频道。 + void Initialize(INetworkChannel networkChannel); + + /// + /// 关闭并清理网络频道辅助器。 + /// + void Shutdown(); + + /// + /// 准备进行连接。 + /// + void PrepareForConnecting(); + + /// + /// 发送心跳消息包。 + /// + /// 是否发送心跳消息包成功。 + bool SendHeartBeat(); + + /// + /// 序列化消息包。 + /// + /// 消息包类型。 + /// 要序列化的消息包。 + /// 要序列化的目标流。 + /// 是否序列化成功。 + bool Serialize(T packet, Stream destination) where T : Packet; + + /// + /// 反序列化消息包头。 + /// + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包头。 + IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData); + + /// + /// 反序列化消息包。 + /// + /// 消息包头。 + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包。 + Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData); + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs.meta new file mode 100644 index 00000000..d1dc4b97 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannelHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1f847791db5a4e6cbbc39dd56a6888d9 +timeCreated: 1681993713 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs new file mode 100644 index 00000000..131a1409 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; + +namespace TEngine +{ + /// + /// 网络管理器接口。 + /// + public interface INetworkManager + { + /// + /// 获取网络频道数量。 + /// + int NetworkChannelCount { get; } + + /// + /// 网络连接成功事件。 + /// + event Action NetworkConnected; + + /// + /// 网络连接关闭事件。 + /// + event Action NetworkClosed; + + /// + /// 网络心跳包丢失事件。 + /// + event Action NetworkMissHeartBeat; + + /// + /// 网络错误事件。 + /// + event Action NetworkError; + + /// + /// 用户自定义网络错误事件。 + /// + event Action NetworkCustomError; + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + bool HasNetworkChannel(string name); + + /// + /// 获取网络频道。 + /// + /// 网络频道名称。 + /// 要获取的网络频道。 + INetworkChannel GetNetworkChannel(string name); + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + INetworkChannel[] GetAllNetworkChannels(); + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + void GetAllNetworkChannels(List results); + + /// + /// 创建网络频道。 + /// + /// 网络频道名称。 + /// 网络服务类型。 + /// 网络频道辅助器。 + /// 要创建的网络频道。 + INetworkChannel CreateNetworkChannel(string name, ServiceType serviceType, INetworkChannelHelper networkChannelHelper); + + /// + /// 销毁网络频道。 + /// + /// 网络频道名称。 + /// 是否销毁网络频道成功。 + bool DestroyNetworkChannel(string name); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs.meta new file mode 100644 index 00000000..50ce8e95 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7de8458fc50e4cd3b49ef73d35ad9763 +timeCreated: 1681993806 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs b/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs new file mode 100644 index 00000000..9fad5e0b --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs @@ -0,0 +1,16 @@ +namespace TEngine +{ + /// + /// 网络消息包头接口。 + /// + public interface IPacketHeader + { + /// + /// 获取网络消息包长度。 + /// + int PacketLength + { + get; + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs.meta new file mode 100644 index 00000000..ba80f456 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/IPacketHeader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7659e4a617d84126b0a548077fb4fdcc +timeCreated: 1681994226 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/NetUtil.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetUtil.cs similarity index 86% rename from Assets/GameScripts/HotFix/GameLogic/Network/NetUtil.cs rename to Assets/TEngine/Runtime/GameFramework/Network/NetUtil.cs index 7f7476f5..9af05c04 100644 --- a/Assets/GameScripts/HotFix/GameLogic/Network/NetUtil.cs +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetUtil.cs @@ -10,7 +10,7 @@ namespace TEngine int v6Count = 0; for (int i = 0; i < ipAddresses.Length; i++) { - if (AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily)) + if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily)) { v6Count++; } @@ -22,7 +22,7 @@ namespace TEngine int resIndex = 0; for (int i = 0; i < ipAddresses.Length; i++) { - if (AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily)) + if (System.Net.Sockets.AddressFamily.InterNetworkV6.Equals(ipAddresses[i].AddressFamily)) { outIPs[resIndex++] = ipAddresses[i]; } diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/NetUtil.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetUtil.cs.meta similarity index 100% rename from Assets/GameScripts/HotFix/GameLogic/Network/NetUtil.cs.meta rename to Assets/TEngine/Runtime/GameFramework/Network/NetUtil.cs.meta diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs new file mode 100644 index 00000000..de374509 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs @@ -0,0 +1,53 @@ +namespace TEngine +{ + /// + /// 网络错误码。 + /// + public enum NetworkErrorCode : byte + { + /// + /// 未知错误。 + /// + Unknown = 0, + + /// + /// 地址族错误。 + /// + AddressFamilyError, + + /// + /// Socket 错误。 + /// + SocketError, + + /// + /// 连接错误。 + /// + ConnectError, + + /// + /// 发送错误。 + /// + SendError, + + /// + /// 接收错误。 + /// + ReceiveError, + + /// + /// 序列化错误。 + /// + SerializeError, + + /// + /// 反序列化消息包头错误。 + /// + DeserializePacketHeaderError, + + /// + /// 反序列化消息包错误。 + /// + DeserializePacketError + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs.meta new file mode 100644 index 00000000..382bac9b --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkErrorCode.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0bb8ca2ece7d49d28b1b4fd1bfa027e6 +timeCreated: 1681993877 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs new file mode 100644 index 00000000..4e7e02b1 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs @@ -0,0 +1,30 @@ +namespace TEngine +{ + public class NetworkEvent + { + /// + /// 网网络连接成功事件。 + /// + public static int NetworkConnectedEvent = StringId.StringToHash("NetworkEvent.NetworkConnectedEvent"); + + /// + /// 网络连接关闭事件。 + /// + public static int NetworkClosedEvent = StringId.StringToHash("NetworkEvent.NetworkClosedEvent"); + + /// + /// 网络错误事件。 + /// + public static int NetworkErrorEvent = StringId.StringToHash("NetworkEvent.NetworkErrorEvent"); + + /// + /// 用户自定义网络错误事件。 + /// + public static int NetworkCustomErrorEvent = StringId.StringToHash("NetworkEvent.NetworkCustomErrorEvent"); + + /// + /// 网络心跳包丢失事件。 + /// + public static int NetworkMissHeartBeatEvent = StringId.StringToHash("NetworkEvent.NetworkMissHeartBeatEvent"); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs.meta new file mode 100644 index 00000000..3015a9a1 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e3b5eb5b76244498181c6117f0e0b1a +timeCreated: 1681993978 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs new file mode 100644 index 00000000..ec88050e --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs @@ -0,0 +1,23 @@ +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; + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs.meta new file mode 100644 index 00000000..db4864bc --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ConnectState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 36ed7026c9b6441cb8a6438b2048d7f6 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs new file mode 100644 index 00000000..7c74d2bc --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs @@ -0,0 +1,39 @@ +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; + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs.meta new file mode 100644 index 00000000..b6d8af6f --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.HeartBeatState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5368d77f60554db99e9c4d0862660442 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs new file mode 100644 index 00000000..fbab56b1 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs @@ -0,0 +1,663 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; + +namespace TEngine +{ + internal sealed partial class NetworkManager + { + /// + /// 网络频道基类。 + /// + private abstract class NetworkChannelBase : INetworkChannel, IDisposable + { + private const float DefaultHeartBeatInterval = 30f; + + private readonly string _name; + protected readonly Queue 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 NetworkChannelConnected; + public Action NetworkChannelClosed; + public Action NetworkChannelMissHeartBeat; + public Action NetworkChannelError; + public Action NetworkChannelCustomError; + + /// + /// 消息注册Map。 + /// + private readonly Dictionary> _msgHandlerMap = new Dictionary>(); + + /// + /// 委托缓存堆栈。 + /// + private readonly Queue> _cacheHandlerQueue = new Queue>(); + + /// + /// 消息包缓存堆栈。 + /// + private readonly Queue _packsQueue = new Queue(); + + /// + /// 初始化网络频道基类的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public NetworkChannelBase(string name, INetworkChannelHelper networkChannelHelper) + { + _name = name ?? string.Empty; + SendPacketPool = new Queue(); + 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); + } + + /// + /// 获取网络频道名称。 + /// + public string Name => _name; + + /// + /// 获取网络频道所使用的 Socket。 + /// + public Socket Socket => MSocket; + + /// + /// 获取是否已连接。 + /// + public bool Connected + { + get + { + if (MSocket != null) + { + return MSocket.Connected; + } + + return false; + } + } + + /// + /// 获取网络服务类型。 + /// + public abstract ServiceType ServiceType { get; } + + /// + /// 获取网络地址类型。 + /// + public AddressFamily AddressFamily => MAddressFamily; + + /// + /// 获取要发送的消息包数量。 + /// + public int SendPacketCount => SendPacketPool.Count; + + /// + /// 获取累计发送的消息包数量。 + /// + public int SentPacketCount => MSentPacketCount; + + /// + /// 获取已接收未处理的消息包数量。 + /// + public int ReceivePacketCount => _cacheHandlerQueue.Count; + + /// + /// 获取累计已接收的消息包数量。 + /// + public int ReceivedPacketCount => MReceivedPacketCount; + + /// + /// 获取或设置当收到消息包时是否重置心跳流逝时间。 + /// + public bool ResetHeartBeatElapseSecondsWhenReceivePacket + { + get => MResetHeartBeatElapseSecondsWhenReceivePacket; + set => MResetHeartBeatElapseSecondsWhenReceivePacket = value; + } + + /// + /// 获取丢失心跳的次数。 + /// + public int MissHeartBeatCount => MHeartBeatState.MissHeartBeatCount; + + /// + /// 获取或设置心跳间隔时长,以秒为单位。 + /// + public float HeartBeatInterval + { + get => MHeartBeatInterval; + set => MHeartBeatInterval = value; + } + + /// + /// 获取心跳等待时长,以秒为单位。 + /// + public float HeartBeatElapseSeconds => MHeartBeatState.HeartBeatElapseSeconds; + + /// + /// 网络频道轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + 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); + } + } + } + } + + /// + /// 关闭网络频道。 + /// + public virtual void Shutdown() + { + Close(); + NetworkChannelHelper.Shutdown(); + } + + /// + /// 注册网络消息包处理函数。 + /// + /// 网络消息包id。 + /// 要注册的网络消息包处理函数。 + public void RegisterMsgHandler(int msgId, CsMsgDelegate msgDelegate) + { + if (msgDelegate == null) + { + throw new GameFrameworkException("Msg handler is invalid."); + } + + if (!_msgHandlerMap.TryGetValue(msgId, out var listHandle)) + { + listHandle = new List(); + _msgHandlerMap[msgId] = listHandle; + } + + if (listHandle != null) + { + if (!listHandle.Contains(msgDelegate)) + { + listHandle.Add(msgDelegate); + } + else + { + Log.Error("-------------repeat RegCmdHandle MsgId:{0}-----------",msgId); + } + } + } + + /// + /// 移除网络消息包处理函数。 + /// + /// 网络消息包id。 + /// 要注册的网络消息包处理函数。 + public void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate) + { + if (!_msgHandlerMap.TryGetValue(msgId, out List listHandle)) + { + return; + } + + if (listHandle != null) + { + listHandle.Remove(msgDelegate); + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + public void Connect(IPAddress ipAddress, int port) + { + Connect(ipAddress, port, null); + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + 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); + } + + /// + /// 关闭连接并释放所有相关资源。 + /// + 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); + } + } + } + + /// + /// 向远程主机发送消息包。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + public bool Send(T packet) where T : 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; + } + + /// + /// 向远程主机发送消息包并注册消息回调。 + /// + /// 消息包类型。 + /// 要发送的消息包。 + /// 要注册的回调。 + /// 是否需要等待UI。 + /// 消息包是否发送成功。 + public bool Send(T pack, CsMsgDelegate resHandler, bool needShowWaitUI = false) where T : Packet + { + //TODO + return true; + } + + /// + /// 释放资源。 + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// 释放资源。 + /// + /// 释放资源标记。 + 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) + { + Packet packet = null; + lock (SendPacketPool) + { + packet = SendPacketPool.Dequeue(); + } + + bool serializeResult = false; + try + { + serializeResult = NetworkChannelHelper.Serialize(packet, 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 + { + Packet packet = NetworkChannelHelper.DeserializePacket(MReceiveState.PacketHeader, MReceiveState.Stream, out var customErrorData); + + if (customErrorData != null && NetworkChannelCustomError != null) + { + NetworkChannelCustomError(this, customErrorData); + } + + if (packet != null) + { + lock (_cacheHandlerQueue) + { + if (_msgHandlerMap.TryGetValue((int)packet.Id, out var listHandle)) + { + _cacheHandlerQueue.Enqueue(listHandle); + + _packsQueue.Enqueue(packet); + } + } + } + + 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; + } + + /// + /// 主线程从消息包缓存堆栈/委托缓存堆栈中出列。 + /// + 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); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs.meta new file mode 100644 index 00000000..88784748 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 399b677044924c6bb2a4e5d56628ca42 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs new file mode 100644 index 00000000..e24fd5fc --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs @@ -0,0 +1,91 @@ +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; + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs.meta new file mode 100644 index 00000000..371a0f75 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.ReceiveState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 89feb1bba2164efea12041106391c284 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs new file mode 100644 index 00000000..58264efd --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs @@ -0,0 +1,60 @@ +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; + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs.meta new file mode 100644 index 00000000..69f06ffa --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.SendState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df8ba0ec679b463e880f8f599ba23d77 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs new file mode 100644 index 00000000..1582976d --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs @@ -0,0 +1,281 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine +{ + internal sealed partial class NetworkManager + { + /// + /// TCP 网络频道。 + /// + private sealed class TcpNetworkChannel : NetworkChannelBase + { + private readonly AsyncCallback _connectCallback; + private readonly AsyncCallback _sendCallback; + private readonly AsyncCallback _receiveCallback; + + /// + /// 初始化网络频道的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public TcpNetworkChannel(string name, INetworkChannelHelper networkChannelHelper) + : base(name, networkChannelHelper) + { + _connectCallback = ConnectCallback; + _sendCallback = SendCallback; + _receiveCallback = ReceiveCallback; + } + + /// + /// 获取网络服务类型。 + /// + public override ServiceType ServiceType + { + get + { + return ServiceType.Tcp; + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + 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; + } + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs.meta new file mode 100644 index 00000000..577bdaaa --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpNetworkChannel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a9660c1a740a423d9c855619aedbebd5 +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs new file mode 100644 index 00000000..a6133c31 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs @@ -0,0 +1,257 @@ +using System; +using System.Net; +using System.Net.Sockets; + +namespace TEngine +{ + internal sealed partial class NetworkManager + { + /// + /// 使用同步接收的 TCP 网络频道。 + /// + private sealed class TcpWithSyncReceiveNetworkChannel : NetworkChannelBase + { + private readonly AsyncCallback _connectCallback; + private readonly AsyncCallback _sendCallback; + + /// + /// 初始化网络频道的新实例。 + /// + /// 网络频道名称。 + /// 网络频道辅助器。 + public TcpWithSyncReceiveNetworkChannel(string name, INetworkChannelHelper networkChannelHelper) + : base(name, networkChannelHelper) + { + _connectCallback = ConnectCallback; + _sendCallback = SendCallback; + } + + /// + /// 获取网络服务类型。 + /// + public override ServiceType ServiceType + { + get + { + return ServiceType.TcpWithSyncReceive; + } + } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + /// 用户自定义数据。 + 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; + } + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta new file mode 100644 index 00000000..65365f14 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.TcpWithSyncReceiveNetworkChannel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ce9312d4603b4eda99cec01fa82364af +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs new file mode 100644 index 00000000..5fe1be1f --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; + +namespace TEngine +{ + /// + /// 网络消息委托。 + /// + public delegate void CsMsgDelegate(Packet packet); + + /// + /// 网络管理器。 + /// + internal sealed partial class NetworkManager : GameFrameworkModule, INetworkManager + { + private readonly Dictionary _networkChannels; + + private Action _networkConnectedEventHandler; + private Action _networkClosedEventHandler; + private Action _networkMissHeartBeatEventHandler; + private Action _networkErrorEventHandler; + private Action _networkCustomErrorEventHandler; + + /// + /// 初始化网络管理器的新实例。 + /// + public NetworkManager() + { + _networkChannels = new Dictionary(StringComparer.Ordinal); + _networkConnectedEventHandler = null; + _networkClosedEventHandler = null; + _networkMissHeartBeatEventHandler = null; + _networkErrorEventHandler = null; + _networkCustomErrorEventHandler = null; + } + + /// + /// 获取网络频道数量。 + /// + public int NetworkChannelCount => _networkChannels.Count; + + /// + /// 网络连接成功事件。 + /// + public event Action NetworkConnected + { + add => _networkConnectedEventHandler += value; + remove => _networkConnectedEventHandler -= value; + } + + /// + /// 网络连接关闭事件。 + /// + public event Action NetworkClosed + { + add => _networkClosedEventHandler += value; + remove => _networkClosedEventHandler -= value; + } + + /// + /// 网络心跳包丢失事件。 + /// + public event Action NetworkMissHeartBeat + { + add => _networkMissHeartBeatEventHandler += value; + remove => _networkMissHeartBeatEventHandler -= value; + } + + /// + /// 网络错误事件。 + /// + public event Action NetworkError + { + add => _networkErrorEventHandler += value; + remove => _networkErrorEventHandler -= value; + } + + /// + /// 用户自定义网络错误事件。 + /// + public event Action NetworkCustomError + { + add => _networkCustomErrorEventHandler += value; + remove => _networkCustomErrorEventHandler -= value; + } + + /// + /// 网络管理器轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + internal override void Update(float elapseSeconds, float realElapseSeconds) + { + foreach (KeyValuePair networkChannel in _networkChannels) + { + networkChannel.Value.Update(elapseSeconds, realElapseSeconds); + } + } + + /// + /// 关闭并清理网络管理器。 + /// + internal override void Shutdown() + { + foreach (KeyValuePair 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(); + } + + /// + /// 检查是否存在网络频道。 + /// + /// 网络频道名称。 + /// 是否存在网络频道。 + public bool HasNetworkChannel(string name) + { + return _networkChannels.ContainsKey(name ?? string.Empty); + } + + /// + /// 获取网络频道。 + /// + /// 网络频道名称。 + /// 要获取的网络频道。 + public INetworkChannel GetNetworkChannel(string name) + { + if (_networkChannels.TryGetValue(name ?? string.Empty, out var networkChannel)) + { + return networkChannel; + } + + return null; + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public INetworkChannel[] GetAllNetworkChannels() + { + int index = 0; + INetworkChannel[] results = new INetworkChannel[_networkChannels.Count]; + foreach (KeyValuePair networkChannel in _networkChannels) + { + results[index++] = networkChannel.Value; + } + + return results; + } + + /// + /// 获取所有网络频道。 + /// + /// 所有网络频道。 + public void GetAllNetworkChannels(List results) + { + if (results == null) + { + throw new GameFrameworkException("Results is invalid."); + } + + results.Clear(); + foreach (KeyValuePair networkChannel in _networkChannels) + { + results.Add(networkChannel.Value); + } + } + + /// + /// 创建网络频道。 + /// + /// 网络频道名称。 + /// 网络服务类型。 + /// 网络频道辅助器。 + /// 要创建的网络频道。 + 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; + + 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; + } + + /// + /// 销毁网络频道。 + /// + /// 网络频道名称。 + /// 是否销毁网络频道成功。 + 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); + } + } + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs.meta new file mode 100644 index 00000000..93ac413c --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 96c0584a53cd4b8e88949b77eac8e1ce +timeCreated: 1681994450 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs b/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs new file mode 100644 index 00000000..abae2275 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs @@ -0,0 +1,18 @@ +namespace TEngine +{ + /// + /// 网络消息包基类。 + /// + public abstract class Packet : IMemory + { + /// + /// 获取类型编号。 + /// + public abstract int Id { get; } + + /// + /// 清理引用。 + /// + public abstract void Clear(); + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs.meta new file mode 100644 index 00000000..1aabb55e --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/Packet.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b840df90fc73484d94cf06e87190f9e2 +timeCreated: 1681994166 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs b/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs new file mode 100644 index 00000000..5affc3e7 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs @@ -0,0 +1,28 @@ +namespace TEngine +{ + /// + /// 网络服务类型。 + /// + public enum ServiceType : byte + { + /// + /// TCP 网络服务。 + /// + Tcp = 0, + + /// + /// 使用同步接收的 TCP 网络服务。 + /// + TcpWithSyncReceive = 1, + + /// + /// UDP 网络服务。 + /// + Udp = 2, + + /// + /// KCP 网络服务。 + /// + Kcp = 3, + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs.meta b/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs.meta new file mode 100644 index 00000000..dbbcc036 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Network/ServiceType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c80c2ca9fea74504a4a79c2ecd01d065 +timeCreated: 1681993754 \ No newline at end of file