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