From ec8fb24256391c6d54df58d0e3345216c89c4f69 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Fri, 21 Apr 2023 11:39:51 +0800 Subject: [PATCH] [+] Network Module [+] Network Module --- .../GameScripts/HotFix/GameLogic/Network.meta | 3 + .../HotFix/GameLogic/Network/HeartBeat.cs | 19 ++ .../GameLogic/Network/HeartBeat.cs.meta | 3 + .../GameLogic/Network/NetworkChannelHelper.cs | 254 ++++++++++++++++++ .../Network/NetworkChannelHelper.cs.meta | 3 + .../HotFix/GameLogic/Network/PacketBase.cs | 24 ++ .../GameLogic/Network/PacketBase.cs.meta | 3 + .../HotFix/GameLogic/Network/PacketHeader.cs | 47 ++++ .../GameLogic/Network/PacketHeader.cs.meta | 3 + .../HotFix/GameLogic/Network/ProtoPacket.cs | 15 ++ .../GameLogic/Network/ProtoPacket.cs.meta | 3 + .../HotFix/GameLogic/Network/ProtobufUtils.cs | 138 ++++++++++ .../GameLogic/Network/ProtobufUtils.cs.meta | 3 + .../Network/Interface/INetworkChannel.cs | 7 + .../NetworkManager.NetworkChannelBase.cs | 11 + 15 files changed, 536 insertions(+) create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs.meta create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs create mode 100644 Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs.meta diff --git a/Assets/GameScripts/HotFix/GameLogic/Network.meta b/Assets/GameScripts/HotFix/GameLogic/Network.meta new file mode 100644 index 00000000..13955a39 --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 145b951be40d41dea06e76bd967a5d15 +timeCreated: 1682045847 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs b/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs new file mode 100644 index 00000000..13288a2c --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs @@ -0,0 +1,19 @@ +using System; +using ProtoBuf; + +namespace GameLogic +{ + [Serializable, ProtoContract(Name = @"HeartBeat")] + public class HeartBeat : PacketBase + { + public HeartBeat() + { + } + + public override int Id => 1; + + public override void Clear() + { + } + } +} \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs.meta new file mode 100644 index 00000000..56003068 --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/HeartBeat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d8b9b60d2abf409ca9dd2ce00506ac79 +timeCreated: 1682046644 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs b/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs new file mode 100644 index 00000000..25e546ed --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs @@ -0,0 +1,254 @@ +using ProtoBuf; +using ProtoBuf.Meta; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Reflection; +using TEngine; + +namespace GameLogic +{ + public class NetworkChannelHelper : INetworkChannelHelper, IMemory + { + private readonly Dictionary _serverToClientPacketTypes = new Dictionary(); + private readonly MemoryStream _cachedStream = new MemoryStream(1024 * 8); + private INetworkChannel _networkChannel = null; + + /// + /// 获取消息包头长度。 + /// 4。 + /// + public int PacketHeaderLength => sizeof(int); + + /// + /// 初始化网络频道辅助器。 + /// + /// 网络频道。 + public void Initialize(INetworkChannel networkChannel) + { + _networkChannel = networkChannel; + + // 反射注册包和包处理函数。 + Type packetBaseType = typeof(ProtoPacket); + Assembly assembly = Assembly.GetExecutingAssembly(); + Type[] types = assembly.GetTypes(); + for (int i = 0; i < types.Length; i++) + { + if (!types[i].IsClass || types[i].IsAbstract) + { + continue; + } + + if (types[i].BaseType == packetBaseType) + { + PacketBase packetBase = (PacketBase)Activator.CreateInstance(types[i]); + Type packetType = GetServerToClientPacketType(packetBase.Id); + if (packetType != null) + { + Log.Warning("Already exist packet type '{0}', check '{1}' or '{2}'?.", packetBase.Id.ToString(), packetType.Name, packetBase.GetType().Name); + continue; + } + + _serverToClientPacketTypes.Add(packetBase.Id, types[i]); + } + } + + GameEvent.AddEventListener(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected); + GameEvent.AddEventListener(NetworkEvent.NetworkClosedEvent, OnNetworkClosed); + GameEvent.AddEventListener(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat); + GameEvent.AddEventListener(NetworkEvent.NetworkErrorEvent, OnNetworkError); + GameEvent.AddEventListener(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError); + } + + /// + /// 关闭并清理网络频道辅助器。 + /// + public void Shutdown() + { + GameEvent.RemoveEventListener(NetworkEvent.NetworkConnectedEvent, OnNetworkConnected); + GameEvent.RemoveEventListener(NetworkEvent.NetworkClosedEvent, OnNetworkClosed); + GameEvent.RemoveEventListener(NetworkEvent.NetworkMissHeartBeatEvent, OnNetworkMissHeartBeat); + GameEvent.RemoveEventListener(NetworkEvent.NetworkErrorEvent, OnNetworkError); + GameEvent.RemoveEventListener(NetworkEvent.NetworkCustomErrorEvent, OnNetworkCustomError); + + _networkChannel = null; + } + + /// + /// 准备进行连接。 + /// + public void PrepareForConnecting() + { + _networkChannel.Socket.ReceiveBufferSize = 1024 * 64; + _networkChannel.Socket.SendBufferSize = 1024 * 64; + } + + /// + /// 发送心跳消息包。 + /// + /// 是否发送心跳消息包成功。 + public bool SendHeartBeat() + { + _networkChannel.Send(MemoryPool.Acquire()); + return true; + } + + /// + /// 序列化消息包。 + /// + /// 消息包类型。 + /// 要序列化的消息包。 + /// 要序列化的目标流。 + /// 是否序列化成功。 + public bool Serialize(T packet, Stream destination) where T : Packet + { + PacketBase packetImpl = packet as PacketBase; + if (packetImpl == null) + { + Log.Warning("Packet is invalid."); + return false; + } + + _cachedStream.SetLength(_cachedStream.Capacity); // 此行防止 Array.Copy 的数据无法写入 + _cachedStream.Position = 0L; + + PacketHeader packetHeader = MemoryPool.Acquire(); + Serializer.Serialize(_cachedStream, packetHeader); + MemoryPool.Release(packetHeader); + + Serializer.SerializeWithLengthPrefix(_cachedStream, packet, PrefixStyle.Fixed32); + MemoryPool.Release((IMemory)packet); + + _cachedStream.WriteTo(destination); + return true; + } + + /// + /// 反序列化消息包头。 + /// + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包头。 + public IPacketHeader DeserializePacketHeader(Stream source, out object customErrorData) + { + // 注意:此函数并不在主线程调用! + customErrorData = null; + return (IPacketHeader)RuntimeTypeModel.Default.Deserialize(source, MemoryPool.Acquire(), typeof(PacketHeader)); + } + + /// + /// 反序列化消息包。 + /// + /// 消息包头。 + /// 要反序列化的来源流。 + /// 用户自定义错误数据。 + /// 反序列化后的消息包。 + public Packet DeserializePacket(IPacketHeader packetHeader, Stream source, out object customErrorData) + { + // 注意:此函数并不在主线程调用! + customErrorData = null; + + PacketHeader scPacketHeader = packetHeader as PacketHeader; + if (scPacketHeader == null) + { + Log.Warning("Packet header is invalid."); + return null; + } + + Packet packet = null; + if (scPacketHeader.IsValid) + { + Type packetType = GetServerToClientPacketType(scPacketHeader.Id); + if (packetType != null) + { + packet = (Packet)RuntimeTypeModel.Default.DeserializeWithLengthPrefix(source, MemoryPool.Acquire(packetType), packetType, PrefixStyle.Fixed32, 0); + } + else + { + Log.Warning("Can not deserialize packet for packet id '{0}'.", scPacketHeader.Id.ToString()); + } + } + else + { + Log.Warning("Packet header is invalid."); + } + + MemoryPool.Release(scPacketHeader); + return packet; + } + + private Type GetServerToClientPacketType(int id) + { + if (_serverToClientPacketTypes.TryGetValue(id, out var type)) + { + return type; + } + + return null; + } + + private void OnNetworkConnected(INetworkChannel channel, object userdata) + { + if (channel != _networkChannel) + { + return; + } + + Log.Info("Network channel '{0}' connected, local address '{1}', remote address '{2}'.", + channel.Name, channel.Socket.LocalEndPoint.ToString(), + channel.Socket.RemoteEndPoint.ToString()); + } + + private void OnNetworkClosed(INetworkChannel channel) + { + if (channel != _networkChannel) + { + return; + } + + Log.Info("Network channel '{0}' closed.", channel.Name); + } + + private void OnNetworkMissHeartBeat(INetworkChannel channel, int missCount) + { + if (channel != _networkChannel) + { + return; + } + + Log.Info("Network channel '{0}' miss heart beat '{1}' times.", channel.Name, missCount.ToString()); + + if (missCount < 2) + { + return; + } + + channel.Close(); + } + + private void OnNetworkError(INetworkChannel channel, NetworkErrorCode networkErrorCode, SocketError socketError, string errorMessage) + { + if (channel != _networkChannel) + { + return; + } + + Log.Info("Network channel '{0}' error, error code is '{1}', error message is '{2}'.", channel.Name, networkErrorCode.ToString(), errorMessage); + + channel.Close(); + } + + private void OnNetworkCustomError(INetworkChannel channel, object userData) + { + if (channel != _networkChannel) + { + return; + } + } + + public void Clear() + { + } + } +} \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs.meta new file mode 100644 index 00000000..e3d8f91d --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/NetworkChannelHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: bf86ce2ddfb5429abecfb06257c86acd +timeCreated: 1682045961 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs b/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs new file mode 100644 index 00000000..0f66dbc4 --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs @@ -0,0 +1,24 @@ +using TEngine; + +namespace GameLogic +{ + /// + /// 网络消息包基类。 + /// + public abstract class PacketBase : Packet + { + /// + /// 网络消息包Id。 + /// + public int ProtoId; + + /// + /// 网络消息包包体。 + /// + public byte[] ProtoBody; + + public void Close() + { + } + } +} \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs.meta new file mode 100644 index 00000000..2db1a1bf --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/PacketBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: acea4283f57644b6b4452354d23f2803 +timeCreated: 1682045887 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs b/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs new file mode 100644 index 00000000..104a6f96 --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs @@ -0,0 +1,47 @@ +using TEngine; + +namespace GameLogic +{ + /// + /// 网络消息包头。 + /// + public class PacketHeader : IPacketHeader, IMemory + { + /// + /// 网络消息包Id。 + /// + public short Id + { + get; + set; + } + + /// + /// 网络消息包长度。 + /// + public int PacketLength + { + get; + set; + } + + /// + /// 网络消息包是否合法。 + /// + public bool IsValid + { + get + { + return PacketLength >= 0; + } + } + + /// + /// 清除网络消息包头。 + /// + public void Clear() + { + PacketLength = 0; + } + } +} \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs.meta new file mode 100644 index 00000000..5bc102de --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/PacketHeader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 30edc133b73141598b5351d4afc2ad93 +timeCreated: 1682046771 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs b/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs new file mode 100644 index 00000000..a7270f2c --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs @@ -0,0 +1,15 @@ +namespace GameLogic +{ + /// + /// 网络消息包。 + /// + public partial class ProtoPacket : PacketBase + { + public override int Id => 1; + + public override void Clear() + { + Close(); + } + } +} diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs.meta new file mode 100644 index 00000000..17a8736e --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/ProtoPacket.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: da70995486db4799baba570b1767ab86 +timeCreated: 1682045865 \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs b/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs new file mode 100644 index 00000000..67bf6893 --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs @@ -0,0 +1,138 @@ +// using System; +// using System.ComponentModel; +// using System.IO; +// using ProtoBuf; +// using ProtoBuf.Meta; +// +// /// +// /// ProtoBuf工具 +// /// +// public class ProtobufUtils +// { +// /// +// /// 消息压入内存流。 +// /// +// /// +// /// +// public static void ToStream(object message, MemoryStream stream) +// { +// ((IMessage)message).WriteTo(stream); +// } +// +// /// +// /// 比特流解析。 +// /// +// /// +// /// +// /// +// /// +// /// +// public static object FromBytes(Type type, byte[] bytes, int index, int count) +// { +// object message = Activator.CreateInstance(type); +// ((IMessage)message).MergeFrom(bytes, index, count); +// ISupportInitialize iSupportInitialize = message as ISupportInitialize; +// if (iSupportInitialize == null) +// { +// return message; +// } +// +// iSupportInitialize.EndInit(); +// return message; +// } +// +// /// +// /// 比特流解析。 +// /// +// /// +// /// +// /// +// /// +// /// +// public static object FromBytes(object instance, byte[] bytes, int index, int count) +// { +// object message = instance; +// ((IMessage)message).MergeFrom(bytes, index, count); +// ISupportInitialize iSupportInitialize = message as ISupportInitialize; +// if (iSupportInitialize == null) +// { +// return message; +// } +// +// iSupportInitialize.EndInit(); +// return message; +// } +// +// /// +// /// 从内存流取出。 +// /// +// /// +// /// +// /// +// public static object FromStream(Type type, MemoryStream stream) +// { +// object message = Activator.CreateInstance(type); +// ((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length); +// ISupportInitialize iSupportInitialize = message as ISupportInitialize; +// if (iSupportInitialize == null) +// { +// return message; +// } +// +// iSupportInitialize.EndInit(); +// return message; +// } +// +// /// +// /// 从内存流取出。 +// /// +// /// +// /// +// /// +// public static object FromStream(object message, MemoryStream stream) +// { +// // TODO 这个message最好从池中获取,减少gc +// ((IMessage)message).MergeFrom(stream.GetBuffer(), (int)stream.Position, (int)stream.Length); +// ISupportInitialize iSupportInitialize = message as ISupportInitialize; +// if (iSupportInitialize == null) +// { +// return message; +// } +// +// iSupportInitialize.EndInit(); +// return message; +// } +// +// /// +// /// 序列化protobuf +// /// +// /// +// /// +// public static byte[] Serialize(object message) +// { +// return ((IMessage)message).ToByteArray(); +// } +// +// /// +// /// 反序列化protobuf +// /// +// /// +// /// +// /// +// public static T Deserialize(byte[] dataBytes) where T : IMessage, new() +// { +// T msg = new T(); +// msg = (T)msg.Descriptor.Parser.ParseFrom(dataBytes); +// return msg; +// } +// +// public static int GetHighOrder(int cmdMerge) +// { +// return cmdMerge >> 16; +// } +// +// public static int GetLowOrder(int cmdMerge) +// { +// return cmdMerge & 65535; +// } +// } \ No newline at end of file diff --git a/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs.meta b/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs.meta new file mode 100644 index 00000000..400e157c --- /dev/null +++ b/Assets/GameScripts/HotFix/GameLogic/Network/ProtobufUtils.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 972ca4545003463d8710de956f0fde66 +timeCreated: 1682047511 \ 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 index 9d214314..0d337942 100644 --- a/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs +++ b/Assets/TEngine/Runtime/GameFramework/Network/Interface/INetworkChannel.cs @@ -129,6 +129,13 @@ namespace TEngine /// 要注册的网络消息包处理函数。 void RemoveMsgHandler(int msgId, CsMsgDelegate msgDelegate); + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + void Connect(string ipAddress, int port); + /// /// 连接到远程主机。 /// diff --git a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs index fbab56b1..1a5b7282 100644 --- a/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs +++ b/Assets/TEngine/Runtime/GameFramework/Network/NetworkManager.NetworkChannelBase.cs @@ -274,6 +274,17 @@ namespace TEngine listHandle.Remove(msgDelegate); } } + + /// + /// 连接到远程主机。 + /// + /// 远程主机的 IP 地址。 + /// 远程主机的端口号。 + public void Connect(string ipAddress, int port) + { + IPAddress address = IPAddress.Parse(ipAddress); + Connect(address, port, null); + } /// /// 连接到远程主机。