diff --git a/.vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 b/.vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2
new file mode 100644
index 0000000..59f948d
Binary files /dev/null and b/.vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 differ
diff --git a/.vs/HaoYueNet/project-colors.json b/.vs/HaoYueNet/project-colors.json
new file mode 100644
index 0000000..fc2c392
--- /dev/null
+++ b/.vs/HaoYueNet/project-colors.json
@@ -0,0 +1,36 @@
+{
+ "Version": 1,
+ "ProjectMap": {
+ "96350e74-b813-4138-a9e8-3cc3cbf71343": {
+ "ProjectGuid": "96350e74-b813-4138-a9e8-3cc3cbf71343",
+ "DisplayName": "ClientNetwork",
+ "ColorIndex": 0
+ },
+ "506cfdce-c2b6-42d1-aa92-b09223fa53ed": {
+ "ProjectGuid": "506cfdce-c2b6-42d1-aa92-b09223fa53ed",
+ "DisplayName": "HaoYueNet.ServerNetwork",
+ "ColorIndex": 1
+ },
+ "5792a0c9-3f6d-48a6-855f-ce04dba33ebe": {
+ "ProjectGuid": "5792a0c9-3f6d-48a6-855f-ce04dba33ebe",
+ "DisplayName": "ServerNetwork",
+ "ColorIndex": 2
+ },
+ "22ed65d3-e2fb-49e4-bdf5-eb2a70774c2e": {
+ "ProjectGuid": "22ed65d3-e2fb-49e4-bdf5-eb2a70774c2e",
+ "DisplayName": "HaoYueNet.ServerNetwork",
+ "ColorIndex": 3
+ },
+ "c75bfb8d-294a-43f0-8b2e-d37dba12be8b": {
+ "ProjectGuid": "c75bfb8d-294a-43f0-8b2e-d37dba12be8b",
+ "DisplayName": "ClientNetwork",
+ "ColorIndex": 4
+ },
+ "696f6aae-2d41-48f7-8fcd-e95cfe2a6519": {
+ "ProjectGuid": "696f6aae-2d41-48f7-8fcd-e95cfe2a6519",
+ "DisplayName": "HaoYueNet.ClientNetwork",
+ "ColorIndex": 5
+ }
+ },
+ "NextColorIndex": 6
+}
\ No newline at end of file
diff --git a/.vs/HaoYueNet/v17/.futdcache.v1 b/.vs/HaoYueNet/v17/.futdcache.v1
new file mode 100644
index 0000000..6d16ed6
Binary files /dev/null and b/.vs/HaoYueNet/v17/.futdcache.v1 differ
diff --git a/HaoYueNet.sln b/HaoYueNet.sln
new file mode 100644
index 0000000..3bcf399
--- /dev/null
+++ b/HaoYueNet.sln
@@ -0,0 +1,53 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simple", "Simple", "{A2CAD164-0816-4D1D-9793-1B1F398C9D29}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NetLib", "NetLib", "{D0066C06-A89A-4E05-80E0-D8232FB0FF3C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HaoYueNet.ServerNetwork", "NetLib\HaoYueNet.ServerNetwork\HaoYueNet.ServerNetwork.csproj", "{22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HaoYueNet.ClientNetwork", "NetLib\HaoYueNet.ClientNetwork\HaoYueNet.ClientNetwork.csproj", "{696F6AAE-2D41-48F7-8FCD-E95CFE2A6519}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleClient", "Simple\SimpleClient\SimpleClient.csproj", "{C558ACA4-E59E-4F4D-94B7-2B78DA272CF3}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleServer", "Simple\SimpleServer\SimpleServer.csproj", "{5559B94A-DA9E-4CF7-B799-2BCAA1F11F49}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {696F6AAE-2D41-48F7-8FCD-E95CFE2A6519}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {696F6AAE-2D41-48F7-8FCD-E95CFE2A6519}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {696F6AAE-2D41-48F7-8FCD-E95CFE2A6519}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {696F6AAE-2D41-48F7-8FCD-E95CFE2A6519}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C558ACA4-E59E-4F4D-94B7-2B78DA272CF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C558ACA4-E59E-4F4D-94B7-2B78DA272CF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C558ACA4-E59E-4F4D-94B7-2B78DA272CF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C558ACA4-E59E-4F4D-94B7-2B78DA272CF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5559B94A-DA9E-4CF7-B799-2BCAA1F11F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5559B94A-DA9E-4CF7-B799-2BCAA1F11F49}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5559B94A-DA9E-4CF7-B799-2BCAA1F11F49}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5559B94A-DA9E-4CF7-B799-2BCAA1F11F49}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {22ED65D3-E2FB-49E4-BDF5-EB2A70774C2E} = {D0066C06-A89A-4E05-80E0-D8232FB0FF3C}
+ {696F6AAE-2D41-48F7-8FCD-E95CFE2A6519} = {D0066C06-A89A-4E05-80E0-D8232FB0FF3C}
+ {C558ACA4-E59E-4F4D-94B7-2B78DA272CF3} = {A2CAD164-0816-4D1D-9793-1B1F398C9D29}
+ {5559B94A-DA9E-4CF7-B799-2BCAA1F11F49} = {A2CAD164-0816-4D1D-9793-1B1F398C9D29}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {22107F03-013F-4871-AC8E-F082694E2679}
+ EndGlobalSection
+EndGlobal
diff --git a/NetLib/HaoYueNet.ClientNetwork/HaoYueNet.ClientNetwork.csproj b/NetLib/HaoYueNet.ClientNetwork/HaoYueNet.ClientNetwork.csproj
new file mode 100644
index 0000000..2f87e16
--- /dev/null
+++ b/NetLib/HaoYueNet.ClientNetwork/HaoYueNet.ClientNetwork.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/NetLib/HaoYueNet.ClientNetwork/NetworkHelperCore.cs b/NetLib/HaoYueNet.ClientNetwork/NetworkHelperCore.cs
new file mode 100644
index 0000000..364bcef
--- /dev/null
+++ b/NetLib/HaoYueNet.ClientNetwork/NetworkHelperCore.cs
@@ -0,0 +1,360 @@
+using HunterProtobufCore;
+using ProtoBuf;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace HaoYueNet.ClientNetwork
+{
+ public class NetworkHelperCore
+ {
+ private Socket client;
+
+ ///
+ /// 心跳包数据
+ ///
+ private byte[] HeartbeatData = new byte[5] { 0x05, 0x00, 0x00, 0x00, 0x00 };
+
+ ////响应倒计时计数最大值
+ //private static int MaxRevIndexNum = 6;
+
+ ////发送倒计时计数最大值
+ //private static int MaxSendIndexNum = 3;
+
+ //响应倒计时计数最大值
+ private static int MaxRevIndexNum = 50;
+
+ //发送倒计时计数最大值
+ private static int MaxSendIndexNum = 3;
+
+ //响应倒计时计数
+ private static int RevIndex=0;
+ //发送倒计时计数
+ private static int SendIndex=0;
+
+ //计时器间隔
+ private static int TimerInterval = 3000;
+
+ private System.Timers.Timer _heartTimer;
+
+ public void Init(string IP, int port)
+ {
+
+ LogOut("==>初始化网络核心");
+
+ RevIndex = MaxRevIndexNum;
+ SendIndex = MaxSendIndexNum;
+
+ client = new Socket(SocketType.Stream, ProtocolType.Tcp);
+ //IPAddress ip = IPAddress.Parse(IP);
+ //IPEndPoint point = new IPEndPoint(ip, port);
+
+ LogOut("连接到IP "+ IP + ":"+ port);
+
+ //带回调的
+ //client.BeginConnect(ip, port, new AsyncCallback(CallBackMethod) , client);
+ try
+ {
+ client.Connect(IP, port);
+ Thread thread = new Thread(Recive);
+ thread.IsBackground = true;
+ thread.Start(client);
+ LogOut("连接成功!");
+
+ _heartTimer = new System.Timers.Timer();
+ _heartTimer.Interval = TimerInterval;
+ _heartTimer.Elapsed += CheckUpdatetimer_Elapsed;
+ _heartTimer.AutoReset = true;
+ _heartTimer.Enabled = true;
+ LogOut("开启心跳包检测");
+
+ OnConnected(true);
+ }
+ catch
+ {
+ OnConnected(false);
+ }
+ }
+
+ ~NetworkHelperCore()
+ {
+ client.Close();
+ }
+
+ private void SendToSocket(byte[] data)
+ {
+ data = SendDataWithHead(data);
+ try
+ {
+ SendWithIndex(data);
+ }
+ catch (Exception ex)
+ {
+ //连接断开
+ OnCloseReady();
+ return;
+ }
+ LogOut("发送消息,消息长度=> "+data.Length);
+ }
+
+ private void SendHeartbeat()
+ {
+ try
+ {
+ SendWithIndex(HeartbeatData);
+ }
+ catch (Exception ex)
+ {
+ //连接断开
+ OnCloseReady();
+ return;
+ }
+ LogOut("发送心跳包");
+ }
+
+ ///
+ /// 发送数据并计数
+ ///
+ ///
+ private void SendWithIndex(byte[] data)
+ {
+ //增加发送计数
+ SendIndex = MaxSendIndexNum;
+ //发送数据
+ client.Send(data);
+ }
+
+ //拼接头长度
+ private byte[] SendDataWithHead(byte[] message)
+ {
+
+ MemoryStream memoryStream = new MemoryStream();//创建一个内存流
+
+ byte[] BagHead = BitConverter.GetBytes(message.Length + 4);//往字节数组中写入包头(包头自身的长度和消息体的长度)的长度
+
+ memoryStream.Write(BagHead, 0, BagHead.Length);//将包头写入内存流
+
+ memoryStream.Write(message, 0, message.Length);//将消息体写入内存流
+
+ byte[] HeadAndBody = memoryStream.ToArray();//将内存流中的数据写入字节数组
+
+ memoryStream.Close();//关闭内存
+ memoryStream.Dispose();//释放资源
+
+ return HeadAndBody;
+ }
+
+ ///
+ /// 供外部调用 发送消息
+ ///
+ ///
+ /// 序列化之后的数据
+ public void SendToServer(int CMDID,byte[] data)
+ {
+ LogOut("准备数据 CMDID=> "+CMDID);
+ HunterNet_C2S _c2sdata = new HunterNet_C2S();
+ _c2sdata.HunterNetCore_CmdID = CMDID;
+ _c2sdata.HunterNetCore_Data = data;
+ byte[] _finaldata = Serizlize(_c2sdata);
+ SendToSocket(_finaldata);
+ }
+
+ public delegate void OnDataCallBack_Data(int CMDID, int ERRCODE, byte[] data);
+
+ public event OnDataCallBack_Data OnDataCallBack;
+
+ public delegate void delegate_NoData();
+
+ public delegate void delegate_Bool(bool IsConnected);
+
+ public event delegate_NoData OnClose;
+
+ public event delegate_Bool OnConnected;
+
+ public delegate void delegate_str(string Msg);
+
+ ///
+ /// 网络库调试日志输出
+ ///
+ public event delegate_str OnLogOut;
+
+ /////
+ ///// 用于调用者回调的虚函数
+ /////
+ /////
+ //public virtual void DataCallBack(int CMDID,int ERRCODE,byte[] data)
+ //{
+
+ //}
+
+ /////
+ ///// 断开连接
+ /////
+ /////
+ //public virtual void OnClose()
+ //{
+
+ //}
+
+ ///
+ /// 做好处理的连接管理
+ ///
+ private void OnCloseReady()
+ {
+ LogOut("关闭心跳包计数");
+ _heartTimer.Enabled = false;
+ LogOut("关闭连接");
+ OnClose();
+ //关闭Socket连接
+ client.Close();
+ }
+
+
+ ///
+ /// 主动关闭连接
+ ///
+ public void CloseConntect()
+ {
+ OnCloseReady();
+ }
+
+ private void DataCallBackReady(byte[] data)
+ {
+
+ //增加接收计数
+ RevIndex = MaxRevIndexNum;
+
+ //不处理心跳包
+ if (data.Length == 1 && data[0] == 0x00)
+ {
+ LogOut("收到心跳包");
+ return;
+ }
+
+ HunterNet_S2C _c2s = DeSerizlize(data);
+
+ OnDataCallBack((int)_c2s.HunterNetCore_CmdID, (int)_c2s.HunterNetCore_ERRORCode, _c2s.HunterNetCore_Data);
+ }
+
+ private void Recive(object o)
+ {
+ var client = o as Socket;
+ MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
+
+ while (true)
+ {
+ byte[] buffer = new byte[1024 * 1024 * 2];
+ int effective=0;
+ try
+ {
+ effective = client.Receive(buffer);
+ if (effective == 0)
+ {
+ continue;
+ }
+ }
+ catch(Exception ex)
+ {
+ //远程主机强迫关闭了一个现有的连接
+ OnCloseReady();
+ return;
+ //断开连接
+ }
+
+
+ memoryStream.Write(buffer, 0, effective);//将接受到的数据写入内存流中
+ byte[] getData = memoryStream.ToArray();//将内存流中的消息体写入字节数组
+ int StartIndex = 0;//设置一个读取数据的起始下标
+
+ while (true)
+ {
+
+
+ if (effective > 0)//如果接受到的消息不为0(不为空)
+ {
+ int HeadLength = 0;//包头长度(包头+包体)
+ if (getData.Length - StartIndex < 4)//包头接受不完整
+ {
+ HeadLength = -1;
+ }
+ else
+ {
+ //如果包头接受完整 转换成int类型的数值
+ HeadLength = BitConverter.ToInt32(getData, StartIndex);
+ }
+ //包头接受完整但是消息体不完整 //包头接受不完整
+ //↓↓↓↓↓↓↓↓ ↓↓↓
+ if (getData.Length - StartIndex < HeadLength || HeadLength == -1)
+ {
+ memoryStream.Close();//关闭内存流
+ memoryStream.Dispose();//释放内存资源
+ memoryStream = new MemoryStream();//创建新的内存流
+ memoryStream.Write(getData, StartIndex, getData.Length - StartIndex);//从新将接受的消息写入内存流
+ break;
+ }
+ else
+ {
+ //把头去掉,就可以吃了,蛋白质是牛肉的六倍
+ DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray());
+ StartIndex += HeadLength;//当读取一条完整的数据后,读取数据的起始下标应为当前接受到的消息体的长度(当前数据的尾部或下一条消息的首部)
+ }
+ }
+ }
+
+ }
+ }
+
+ private void CheckUpdatetimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ //接收服务器数据计数
+ RevIndex--;
+ if (RevIndex <= 0)
+ {
+ //判定掉线
+ OnCloseReady();
+ return;
+ }
+
+ //发送计数
+ SendIndex--;
+ if (SendIndex <= 0)//需要发送心跳包了
+ {
+ //重置倒计时计数
+ SendIndex = MaxSendIndexNum;
+
+ SendHeartbeat();
+ }
+ }
+
+ public static byte[] Serizlize(T MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ Serializer.Serialize(ms, MsgObj);
+ byte[] data1 = ms.ToArray();
+ return data1;
+ }
+ }
+
+ public static T DeSerizlize(byte[] MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream(MsgObj))
+ {
+ var ds_obj = Serializer.Deserialize(ms);
+ return ds_obj;
+ }
+ }
+
+ public void LogOut(string Msg)
+ {
+ //Console.WriteLine(Msg);
+ OnLogOut(Msg);
+ }
+ }
+}
diff --git a/NetLib/HaoYueNet.ClientNetwork/protobuf_HunterNetCore.cs b/NetLib/HaoYueNet.ClientNetwork/protobuf_HunterNetCore.cs
new file mode 100644
index 0000000..740871f
--- /dev/null
+++ b/NetLib/HaoYueNet.ClientNetwork/protobuf_HunterNetCore.cs
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+// Option: missing-value detection (*Specified/ShouldSerialize*/Reset*) enabled
+
+// Generated from: proto/protobuf_HunterNetCore.proto
+namespace HunterProtobufCore
+{
+ [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"HunterNet_C2S")]
+ public partial class HunterNet_C2S : global::ProtoBuf.IExtensible
+ {
+ public HunterNet_C2S() {}
+
+ private int? _HunterNetCore_CmdID;
+ [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"HunterNetCore_CmdID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_CmdID
+ {
+ get { return _HunterNetCore_CmdID; }
+ set { _HunterNetCore_CmdID = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_CmdIDSpecified
+ {
+ get { return this._HunterNetCore_CmdID != null; }
+ set { if (value == (this._HunterNetCore_CmdID== null)) this._HunterNetCore_CmdID = value ? this.HunterNetCore_CmdID : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_CmdID() { return HunterNetCore_CmdIDSpecified; }
+ private void ResetHunterNetCore_CmdID() { HunterNetCore_CmdIDSpecified = false; }
+
+ private byte[] _HunterNetCore_Data;
+ [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"HunterNetCore_Data", DataFormat = global::ProtoBuf.DataFormat.Default)]
+ public byte[] HunterNetCore_Data
+ {
+ get { return _HunterNetCore_Data; }
+ set { _HunterNetCore_Data = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_DataSpecified
+ {
+ get { return this._HunterNetCore_Data != null; }
+ set { if (value == (this._HunterNetCore_Data== null)) this._HunterNetCore_Data = value ? this.HunterNetCore_Data : (byte[])null; }
+ }
+ private bool ShouldSerializeHunterNetCore_Data() { return HunterNetCore_DataSpecified; }
+ private void ResetHunterNetCore_Data() { HunterNetCore_DataSpecified = false; }
+
+ private global::ProtoBuf.IExtension extensionObject;
+ global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+ { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
+ }
+
+ [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"HunterNet_S2C")]
+ public partial class HunterNet_S2C : global::ProtoBuf.IExtensible
+ {
+ public HunterNet_S2C() {}
+
+ private int? _HunterNetCore_CmdID;
+ [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"HunterNetCore_CmdID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_CmdID
+ {
+ get { return _HunterNetCore_CmdID; }
+ set { _HunterNetCore_CmdID = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_CmdIDSpecified
+ {
+ get { return this._HunterNetCore_CmdID != null; }
+ set { if (value == (this._HunterNetCore_CmdID== null)) this._HunterNetCore_CmdID = value ? this.HunterNetCore_CmdID : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_CmdID() { return HunterNetCore_CmdIDSpecified; }
+ private void ResetHunterNetCore_CmdID() { HunterNetCore_CmdIDSpecified = false; }
+
+ private int? _HunterNetCore_ERRORCode;
+ [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"HunterNetCore_ERRORCode", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_ERRORCode
+ {
+ get { return _HunterNetCore_ERRORCode; }
+ set { _HunterNetCore_ERRORCode = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_ERRORCodeSpecified
+ {
+ get { return this._HunterNetCore_ERRORCode != null; }
+ set { if (value == (this._HunterNetCore_ERRORCode== null)) this._HunterNetCore_ERRORCode = value ? this.HunterNetCore_ERRORCode : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_ERRORCode() { return HunterNetCore_ERRORCodeSpecified; }
+ private void ResetHunterNetCore_ERRORCode() { HunterNetCore_ERRORCodeSpecified = false; }
+
+ private byte[] _HunterNetCore_Data;
+ [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"HunterNetCore_Data", DataFormat = global::ProtoBuf.DataFormat.Default)]
+ public byte[] HunterNetCore_Data
+ {
+ get { return _HunterNetCore_Data; }
+ set { _HunterNetCore_Data = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_DataSpecified
+ {
+ get { return this._HunterNetCore_Data != null; }
+ set { if (value == (this._HunterNetCore_Data== null)) this._HunterNetCore_Data = value ? this.HunterNetCore_Data : (byte[])null; }
+ }
+ private bool ShouldSerializeHunterNetCore_Data() { return HunterNetCore_DataSpecified; }
+ private void ResetHunterNetCore_Data() { HunterNetCore_DataSpecified = false; }
+
+ private global::ProtoBuf.IExtension extensionObject;
+ global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+ { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
+ }
+
+}
\ No newline at end of file
diff --git a/NetLib/HaoYueNet.ServerNetwork/HaoYueNet.ServerNetwork.csproj b/NetLib/HaoYueNet.ServerNetwork/HaoYueNet.ServerNetwork.csproj
new file mode 100644
index 0000000..2f87e16
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/HaoYueNet.ServerNetwork.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/NetLib/HaoYueNet.ServerNetwork/NetWork/AsyncUserToken.cs b/NetLib/HaoYueNet.ServerNetwork/NetWork/AsyncUserToken.cs
new file mode 100644
index 0000000..3be9e65
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/NetWork/AsyncUserToken.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+
+namespace HaoYueNet.ServerNetwork
+{
+ public class AsyncUserToken
+ {
+ ///
+ /// 客户端IP地址
+ ///
+ public IPAddress IPAddress { get; set; }
+
+ ///
+ /// 远程地址
+ ///
+ public EndPoint Remote { get; set; }
+
+ ///
+ /// 通信SOKET
+ ///
+ public Socket Socket { get; set; }
+
+ ///
+ /// 连接时间
+ ///
+ public DateTime ConnectTime { get; set; }
+
+ ///
+ /// 所属用户信息
+ ///
+ public object UserInfo { get; set; }
+
+ ///
+ /// 数据缓存区
+ ///
+ public List Buffer { get; set; }
+
+ public AsyncUserToken()
+ {
+ this.Buffer = new List();
+ }
+
+ ///
+ /// 响应倒计时计数
+ ///
+ public int RevIndex { get; set; } = 0;
+ ///
+ /// 发送倒计时计数
+ ///
+ public int SendIndex { get; set; } = 0;
+ }
+}
diff --git a/NetLib/HaoYueNet.ServerNetwork/NetWork/BufferManager.cs b/NetLib/HaoYueNet.ServerNetwork/NetWork/BufferManager.cs
new file mode 100644
index 0000000..9114fc5
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/NetWork/BufferManager.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+
+namespace HaoYueNet.ServerNetwork
+{
+ public class BufferManager
+ {
+ int m_numBytes; // the total number of bytes controlled by the buffer pool
+ byte[] m_buffer; // the underlying byte array maintained by the Buffer Manager
+ Stack m_freeIndexPool; //
+ int m_currentIndex;
+ int m_bufferSize;
+
+ public BufferManager(int totalBytes, int bufferSize)
+ {
+ m_numBytes = totalBytes;
+ m_currentIndex = 0;
+ m_bufferSize = bufferSize;
+ m_freeIndexPool = new Stack();
+ }
+
+ // Allocates buffer space used by the buffer pool
+ public void InitBuffer()
+ {
+ // create one big large buffer and divide that
+ // out to each SocketAsyncEventArg object
+ m_buffer = new byte[m_numBytes];
+ }
+
+ // Assigns a buffer from the buffer pool to the
+ // specified SocketAsyncEventArgs object
+ //
+ // true if the buffer was successfully set, else false
+ public bool SetBuffer(SocketAsyncEventArgs args)
+ {
+
+ if (m_freeIndexPool.Count > 0)
+ {
+ args.SetBuffer(m_buffer, m_freeIndexPool.Pop(), m_bufferSize);
+ }
+ else
+ {
+ if ((m_numBytes - m_bufferSize) < m_currentIndex)
+ {
+ return false;
+ }
+ args.SetBuffer(m_buffer, m_currentIndex, m_bufferSize);
+ m_currentIndex += m_bufferSize;
+ }
+ return true;
+ }
+
+ // Removes the buffer from a SocketAsyncEventArg object.
+ // This frees the buffer back to the buffer pool
+ public void FreeBuffer(SocketAsyncEventArgs args)
+ {
+ m_freeIndexPool.Push(args.Offset);
+ args.SetBuffer(null, 0, 0);
+ }
+ }
+}
diff --git a/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketEventPool.cs b/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketEventPool.cs
new file mode 100644
index 0000000..8f226ae
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketEventPool.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+
+namespace HaoYueNet.ServerNetwork
+{
+
+ public class SocketEventPool
+ {
+ Stack m_pool;
+
+ public SocketEventPool(int capacity)
+ {
+ m_pool = new Stack(capacity);
+ }
+
+ public void Push(SocketAsyncEventArgs item)
+ {
+ if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); }
+ lock (m_pool)
+ {
+ m_pool.Push(item);
+ }
+ }
+
+ // Removes a SocketAsyncEventArgs instance from the pool
+ // and returns the object removed from the pool
+ public SocketAsyncEventArgs Pop()
+ {
+ lock (m_pool)
+ {
+ return m_pool.Pop();
+ }
+ }
+
+ // The number of SocketAsyncEventArgs instances in the pool
+ public int Count
+ {
+ get { return m_pool.Count; }
+ }
+
+ public void Clear()
+ {
+ m_pool.Clear();
+ }
+ }
+}
diff --git a/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketManager.cs b/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketManager.cs
new file mode 100644
index 0000000..a74626e
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/NetWork/SocketManager.cs
@@ -0,0 +1,838 @@
+using HunterProtobufCore;
+using ProtoBuf;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Sockets;
+using System.Text;
+using System.Threading;
+
+
+namespace HaoYueNet.ServerNetwork
+{
+ public class SocketManager
+ {
+
+ ///
+ /// 心跳包数据
+ ///
+ private byte[] HeartbeatData = new byte[5] { 0x05, 0x00, 0x00, 0x00, 0x00 };
+ //响应倒计时计数最大值
+ //public int MaxRevIndexNum { get; set; } = 5;
+ ////发送倒计时计数最大值
+ //public int MaxSendIndexNum { get; set; } = 3;
+
+ //响应倒计时计数最大值
+ public int MaxRevIndexNum { get; set; } = 50;
+ //发送倒计时计数最大值
+ public int MaxSendIndexNum { get; set; } = 3;
+
+ //计时器间隔
+ private static int TimerInterval = 3000;
+
+ ///
+ /// 心跳包计数器
+ ///
+ private System.Timers.Timer _heartTimer;
+
+ public int m_maxConnectNum; //最大连接数
+ public int m_revBufferSize; //最大接收字节数
+ public BufferManager m_bufferManager;
+ public const int opsToAlloc = 2;
+ Socket listenSocket; //监听Socket
+ public SocketEventPool m_pool;
+
+ public SocketEventPool m_Sendpool;
+ public TokenMsgPool msg_pool;
+ //public Dictionary _DictAsyncUserTokenSendSAEA = new Dictionary();
+ public int m_clientCount; //连接的客户端数量
+ public Semaphore m_maxNumberAcceptedClients;
+
+ List m_clients; //客户端列表
+ public Dictionary _DictSocketAsyncUserToken = new Dictionary();
+
+ #region 定义委托
+
+ ///
+ /// 客户端连接数量变化时触发
+ ///
+ /// 当前增加客户的个数(用户退出时为负数,增加时为正数,一般为1)
+ /// 增加用户的信息
+ public delegate void OnClientNumberChange(int num, AsyncUserToken token);
+
+ ///
+ /// 接收到客户端的数据
+ ///
+ /// 客户端
+ /// 客户端数据
+ public delegate void OnReceiveData(AsyncUserToken token, byte[] buff);
+
+ #endregion
+
+ #region 定义事件
+ ///
+ /// 客户端连接数量变化事件
+ ///
+ public event OnClientNumberChange ClientNumberChange;
+
+ ///
+ /// 接收到客户端的数据事件
+ ///
+ public event OnReceiveData ReceiveClientData;
+
+
+ #endregion
+
+ #region 定义属性
+
+ ///
+ /// 获取客户端列表
+ ///
+ public List ClientList { get { return m_clients; } }
+ #endregion
+
+ ///
+ /// 构造函数
+ ///
+ /// 最大连接数
+ /// 缓存区大小
+ public SocketManager(int numConnections, int receiveBufferSize)
+ {
+ m_clientCount = 0;
+ m_maxConnectNum = numConnections;
+ m_revBufferSize = receiveBufferSize;
+ // allocate buffers such that the maximum number of sockets can have one outstanding read and
+ //write posted to the socket simultaneously
+ m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToAlloc, receiveBufferSize);
+
+ m_pool = new SocketEventPool(numConnections);
+ m_Sendpool = new SocketEventPool(numConnections);
+
+ msg_pool = new TokenMsgPool(numConnections);
+
+ m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
+ }
+
+ ///
+ /// 初始化
+ ///
+ public void Init()
+ {
+ // Allocates one large byte buffer which all I/O operations use a piece of. This gaurds
+ // against memory fragmentation
+ m_bufferManager.InitBuffer();
+ m_clients = new List();
+ // preallocate pool of SocketAsyncEventArgs objects
+ SocketAsyncEventArgs readWriteEventArg;
+
+ for (int i = 0; i < m_maxConnectNum; i++)
+ {
+ readWriteEventArg = new SocketAsyncEventArgs();
+ readWriteEventArg.Completed += new EventHandler(IO_Completed);
+ readWriteEventArg.UserToken = new AsyncUserToken();
+
+ // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
+ m_bufferManager.SetBuffer(readWriteEventArg);
+ // add SocketAsyncEventArg to the pool
+ m_pool.Push(readWriteEventArg);
+ }
+
+ //尝试
+ for (int i = 0; i < m_maxConnectNum; i++)
+ {
+ readWriteEventArg = new SocketAsyncEventArgs();
+ readWriteEventArg.Completed += new EventHandler(IO_Completed2);
+ readWriteEventArg.UserToken = new AsyncUserToken();
+
+ // assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
+ m_bufferManager.SetBuffer(readWriteEventArg);
+ // add SocketAsyncEventArg to the pool
+ m_Sendpool.Push(readWriteEventArg);
+ }
+ }
+
+
+ ///
+ /// 启动服务
+ ///
+ ///
+ public bool Start(IPEndPoint localEndPoint)
+ {
+ try
+ {
+ m_clients.Clear();
+ listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+ listenSocket.Bind(localEndPoint);
+ // start the server with a listen backlog of 100 connections
+ listenSocket.Listen(m_maxConnectNum);
+ // post accepts on the listening socket
+ StartAccept(null);
+
+ _heartTimer = new System.Timers.Timer();
+ _heartTimer.Interval = TimerInterval;
+ _heartTimer.Elapsed += CheckUpdatetimer_Elapsed;
+ _heartTimer.AutoReset = true;
+ _heartTimer.Enabled = true;
+ //Console.WriteLine("开启心跳包定时器");
+
+ return true;
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// 停止服务
+ ///
+ public void Stop()
+ {
+ foreach (AsyncUserToken token in m_clients)
+ {
+ try
+ {
+ token.Socket.Shutdown(SocketShutdown.Both);
+ }
+ catch (Exception) { }
+ }
+ try
+ {
+ listenSocket.Shutdown(SocketShutdown.Both);
+ }
+ catch (Exception) { }
+
+ listenSocket.Close();
+ int c_count = m_clients.Count;
+ lock (m_clients) { m_clients.Clear(); }
+ //补充处理
+ lock (_DictSocketAsyncUserToken) { _DictSocketAsyncUserToken.Clear(); }
+
+ if (ClientNumberChange != null)
+ ClientNumberChange(-c_count, null);
+ }
+
+
+ public void CloseClient(AsyncUserToken token)
+ {
+ try
+ {
+ token.Socket.Shutdown(SocketShutdown.Both);
+ }
+ catch (Exception) { }
+ }
+
+
+ // Begins an operation to accept a connection request from the client
+ //
+ // The context object to use when issuing
+ // the accept operation on the server's listening socket
+ public void StartAccept(SocketAsyncEventArgs acceptEventArg)
+ {
+ if (acceptEventArg == null)
+ {
+ acceptEventArg = new SocketAsyncEventArgs();
+ acceptEventArg.Completed += new EventHandler(AcceptEventArg_Completed);
+ }
+ else
+ {
+ // socket must be cleared since the context object is being reused
+ acceptEventArg.AcceptSocket = null;
+ }
+
+ m_maxNumberAcceptedClients.WaitOne();
+ if (!listenSocket.AcceptAsync(acceptEventArg))
+ {
+ ProcessAccept(acceptEventArg);
+ }
+ }
+
+ // This method is the callback method associated with Socket.AcceptAsync
+ // operations and is invoked when an accept operation is complete
+ //
+ void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
+ {
+ ProcessAccept(e);
+ }
+
+ private void ProcessAccept(SocketAsyncEventArgs e)
+ {
+ try
+ {
+ Interlocked.Increment(ref m_clientCount);
+ // Get the socket for the accepted client connection and put it into the
+ //ReadEventArg object user token
+ SocketAsyncEventArgs readEventArgs = m_pool.Pop();
+ AsyncUserToken userToken = (AsyncUserToken)readEventArgs.UserToken;
+ userToken.Socket = e.AcceptSocket;
+ userToken.ConnectTime = DateTime.Now;
+ userToken.Remote = e.AcceptSocket.RemoteEndPoint;
+ userToken.IPAddress = ((IPEndPoint)(e.AcceptSocket.RemoteEndPoint)).Address;
+
+
+ userToken.RevIndex = MaxRevIndexNum;
+ userToken.SendIndex = MaxSendIndexNum;
+
+ lock (m_clients) { m_clients.Add(userToken); }
+
+ //补充处理
+ lock (_DictSocketAsyncUserToken) { _DictSocketAsyncUserToken.Add(userToken.Socket, userToken); }
+
+ if (ClientNumberChange != null)
+ ClientNumberChange(1, userToken);
+ if (!e.AcceptSocket.ReceiveAsync(readEventArgs))
+ {
+ ProcessReceive(readEventArgs);
+ }
+ }
+ catch (Exception me)
+ {
+ //RuncomLib.Log.LogUtils.Info(me.Message + "\r\n" + me.StackTrace);
+ }
+
+ // Accept the next connection request
+ if (e.SocketError == SocketError.OperationAborted) return;
+ StartAccept(e);
+ }
+
+ void IO_Completed(object sender, SocketAsyncEventArgs e)
+ {
+ // determine which type of operation just completed and call the associated handler
+
+ switch (e.LastOperation)
+ {
+ case SocketAsyncOperation.Receive:
+ ProcessReceive(e);
+ break;
+ case SocketAsyncOperation.Send:
+ ProcessSend(e);
+ break;
+ default:
+ throw new ArgumentException("The last operation completed on the socket was not a receive or send");
+ }
+
+ }
+ void IO_Completed2(object sender, SocketAsyncEventArgs e)
+ {
+ // determine which type of operation just completed and call the associated handler
+
+ switch (e.LastOperation)
+ {
+ case SocketAsyncOperation.Receive:
+ ProcessReceive(e);
+ break;
+ case SocketAsyncOperation.Send:
+ ProcessSend2(e);
+ break;
+ default:
+ throw new ArgumentException("The last operation completed on the socket was not a receive or send");
+ }
+
+ }
+
+ // This method is invoked when an asynchronous receive operation completes.
+ // If the remote host closed the connection, then the socket is closed.
+ // If data was received then the data is echoed back to the client.
+ //
+ private void ProcessReceive(SocketAsyncEventArgs e)
+ {
+ try
+ {
+ // check if the remote host closed the connection
+ AsyncUserToken token = (AsyncUserToken)e.UserToken;
+ if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
+ {
+ //读取数据
+ byte[] data = new byte[e.BytesTransferred];
+ Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
+ lock (token.Buffer)
+ {
+ token.Buffer.AddRange(data);
+ }
+ //注意:你一定会问,这里为什么要用do-while循环?
+ //如果当客户发送大数据流的时候,e.BytesTransferred的大小就会比客户端发送过来的要小,
+ //需要分多次接收.所以收到包的时候,先判断包头的大小.够一个完整的包再处理.
+ //如果客户短时间内发送多个小数据包时, 服务器可能会一次性把他们全收了.
+ //这样如果没有一个循环来控制,那么只会处理第一个包,
+ //剩下的包全部留在token.Buffer中了,只有等下一个数据包过来后,才会放出一个来.
+ do
+ {
+
+ //如果包头不完整
+ if (token.Buffer.Count < 4)
+ break;
+
+ //判断包的长度
+ byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray();
+ int packageLen = BitConverter.ToInt32(lenBytes, 0) - 4;
+ if (packageLen > token.Buffer.Count - 4)
+ { //长度不够时,退出循环,让程序继续接收
+ break;
+ }
+
+ //包够长时,则提取出来,交给后面的程序去处理
+ byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray();
+ //从数据池中移除这组数据
+ lock (token.Buffer)
+ {
+ token.Buffer.RemoveRange(0, packageLen + 4);
+ }
+ //将数据包交给后台处理,这里你也可以新开个线程来处理.加快速度.
+ if (ReceiveClientData != null)
+ ReceiveClientData(token, rev);
+
+ DataCallBackReady(token, rev);
+
+ //这里API处理完后,并没有返回结果,当然结果是要返回的,却不是在这里, 这里的代码只管接收.
+ //若要返回结果,可在API处理中调用此类对象的SendMessage方法,统一打包发送.不要被微软的示例给迷惑了.
+ } while (token.Buffer.Count > 4);
+
+ //继续接收. 为什么要这么写,请看Socket.ReceiveAsync方法的说明
+ if (!token.Socket.ReceiveAsync(e))
+ this.ProcessReceive(e);
+ }
+ else
+ {
+ CloseClientSocket(e);
+ }
+ }
+ catch (Exception xe)
+ {
+ //RuncomLib.Log.LogUtils.Info(xe.Message + "\r\n" + xe.StackTrace);
+ }
+ }
+
+ // This method is invoked when an asynchronous send operation completes.
+ // The method issues another receive on the socket to read any additional
+ // data sent from the client
+ //
+ //
+ private void ProcessSend(SocketAsyncEventArgs e)
+ {
+ if (e.SocketError == SocketError.Success)
+ {
+ // done echoing data back to the client
+ AsyncUserToken token = (AsyncUserToken)e.UserToken;
+ // read the next block of data send from the client
+ bool willRaiseEvent = token.Socket.ReceiveAsync(e);
+ if (!willRaiseEvent)
+ {
+ ProcessReceive(e);
+ }
+ }
+ else
+ {
+ CloseClientSocket(e);
+ }
+ }
+
+ private void ProcessSend2(SocketAsyncEventArgs e)
+ {
+ if (e.SocketError == SocketError.Success)
+ {
+ // done echoing data back to the client
+ //AsyncUserToken token = (AsyncUserToken)e.UserToken;
+ // read the next block of data send from the client
+ //bool willRaiseEvent = token.Socket.ReceiveAsync(e);
+ //if (!willRaiseEvent)
+ //{
+ // ProcessReceive(e);
+ //}
+ }
+ else
+ {
+ CloseClientSocket(e);
+ }
+ e.SetBuffer(null, 0, 0);
+ m_Sendpool.Push(e);
+ //Console.WriteLine("发送完毕压进回对象池");
+
+ SendForMsgPool();
+ }
+
+ //关闭客户端
+ private void CloseClientSocket(SocketAsyncEventArgs e)
+ {
+ AsyncUserToken token = e.UserToken as AsyncUserToken;
+
+ //调用关闭连接
+ OnClose(token);
+
+ lock (m_clients) { m_clients.Remove(token); }
+
+ //补充处理
+ lock (_DictSocketAsyncUserToken) { _DictSocketAsyncUserToken.Remove(token.Socket); }
+
+ ////尝试1
+ //m_Sendpool.Push(_DictAsyncUserTokenSendSAEA[token]);
+ //lock (_DictAsyncUserTokenSendSAEA) { _DictAsyncUserTokenSendSAEA.Remove(token); }
+ ////尝试1结束
+
+ //如果有事件,则调用事件,发送客户端数量变化通知
+ if (ClientNumberChange != null)
+ ClientNumberChange(-1, token);
+ // close the socket associated with the client
+ try
+ {
+ token.Socket.Shutdown(SocketShutdown.Send);
+ }
+ catch (Exception) { }
+ token.Socket.Close();
+ // decrement the counter keeping track of the total number of clients connected to the server
+ Interlocked.Decrement(ref m_clientCount);
+ m_maxNumberAcceptedClients.Release();
+ // Free the SocketAsyncEventArg so they can be reused by another client
+ e.UserToken = new AsyncUserToken();
+ m_pool.Push(e);
+ }
+
+ ///
+ /// 对数据进行打包,然后再发送
+ ///
+ ///
+ ///
+ ///
+ //public void SendMessage(AsyncUserToken token, byte[] message)
+ //{
+ // if (token == null || token.Socket == null || !token.Socket.Connected)
+ // return;
+ // try
+ // {
+ // //对要发送的消息,制定简单协议,头4字节指定包的大小,方便客户端接收(协议可以自己定)
+ // byte[] buff = new byte[message.Length + 4];
+ // byte[] len = BitConverter.GetBytes(message.Length);
+ // Array.Copy(len, buff, 4);
+ // Array.Copy(message, 0, buff, 4, message.Length);
+ // //token.Socket.Send(buff); //这句也可以发送, 可根据自己的需要来选择
+ // //新建异步发送对象, 发送消息
+ // SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
+ // sendArg.UserToken = token;
+ // sendArg.SetBuffer(buff, 0, buff.Length); //将数据放置进去.
+ // token.Socket.SendAsync(sendArg);
+ // }
+ // catch (Exception e)
+ // {
+ // //RuncomLib.Log.LogUtils.Info("SendMessage - Error:" + e.Message);
+ // }
+ //}
+ //bool flag_SendForMsgPool = false;
+ int sendrun = 0;
+ public void SendForMsgPool()
+ {
+ //if (flag_SendForMsgPool) return;
+ try
+ {
+ if (sendrun < msg_pool.Count || msg_pool.Count < 1)
+ return;
+
+ sendrun++;
+ while (msg_pool.Count > 0)
+ {
+ try
+ {
+ TokenWithMsg msg = msg_pool.Dequeue();
+ //Console.WriteLine("从信息池取出发送");
+ SendMessage(msg.token, msg.message);
+ msg = null;
+ }
+ catch
+ {
+ Console.WriteLine("==============================================>");
+ }
+ }
+ sendrun--;
+ Console.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ }
+ catch(Exception ex)
+ {
+ Console.WriteLine(ex.ToString());
+ }
+
+ }
+
+ public void SendMessage(AsyncUserToken token, byte[] message)
+ {
+ if (token == null || token.Socket == null || !token.Socket.Connected)
+ return;
+ try
+ {
+ message = SendDataWithHead(message);
+
+ //尝试2 (发送的时候从队列取,动态绑定
+ //Console.WriteLine("队列取出 并 发送!!!!");
+ if (m_Sendpool.Count > 0)
+ {
+ SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop();
+ myreadEventArgs.UserToken = token;
+ myreadEventArgs.AcceptSocket = token.Socket;
+ myreadEventArgs.SetBuffer(message, 0, message.Length); //将数据放置进去.
+ token.Socket.SendAsync(myreadEventArgs);
+ return;
+ }
+ else
+ {
+ msg_pool.Enqueue(new TokenWithMsg() { token = token, message = message });
+ //Console.WriteLine("!!!!压入消息发送队列MSG_Pool");
+ return;
+ }
+
+ //尝试结束
+
+ ////尝试1
+ //Console.WriteLine("发送!!!!");
+ //SocketAsyncEventArgs myreadEventArgs;
+ //if (!_DictAsyncUserTokenSendSAEA.ContainsKey(token))
+ //{
+ // myreadEventArgs = m_Sendpool.Pop();
+ // myreadEventArgs.UserToken = token;
+ // _DictAsyncUserTokenSendSAEA.Add(token, myreadEventArgs);
+ // myreadEventArgs.AcceptSocket = token.Socket;
+ // //myreadEventArgs.AcceptSocket.RemoteEndPoint = token.Remote;
+ // token.IPAddress = ((IPEndPoint)(myreadEventArgs.AcceptSocket.RemoteEndPoint)).Address;
+ //}
+ //else
+ //{
+ // myreadEventArgs = _DictAsyncUserTokenSendSAEA[token];
+ //}
+
+ //myreadEventArgs.SetBuffer(message, 0, message.Length); //将数据放置进去.
+ //token.Socket.SendAsync(myreadEventArgs);
+
+ //return;
+ ////尝试1结束
+
+ //新建异步发送对象, 发送消息
+ SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
+ sendArg.UserToken = token;
+ sendArg.SetBuffer(message, 0, message.Length); //将数据放置进去.
+ token.Socket.SendAsync(sendArg);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.ToString());
+ //RuncomLib.Log.LogUtils.Info("SendMessage - Error:" + e.Message);
+ }
+
+ }
+
+ //拼接长度
+ private static byte[] SendDataWithHead(byte[] message)
+ {
+
+ MemoryStream memoryStream = new MemoryStream();//创建一个内存流
+
+ byte[] BagHead = BitConverter.GetBytes(message.Length + 4);//往字节数组中写入包头(包头自身的长度和消息体的长度)的长度
+
+ memoryStream.Write(BagHead, 0, BagHead.Length);//将包头写入内存流
+
+ memoryStream.Write(message, 0, message.Length);//将消息体写入内存流
+
+ byte[] HeadAndBody = memoryStream.ToArray();//将内存流中的数据写入字节数组
+
+ memoryStream.Close();//关闭内存
+ memoryStream.Dispose();//释放资源
+
+ return HeadAndBody;
+ }
+
+ #region
+
+ ///
+ /// 用于调用者回调的虚函数
+ ///
+ ///
+ public virtual void DataCallBack(AsyncUserToken sk, int CMDID, byte[] data)
+ {
+
+ }
+ ///
+ /// 断开连接
+ ///
+ ///
+ public virtual void OnClose(AsyncUserToken sk)
+ {
+
+ }
+
+ public virtual void OnCloseReady(AsyncUserToken token)
+ {
+ OnClose(token);
+
+ lock (m_clients) { m_clients.Remove(token); }
+
+ //补充处理
+ lock (_DictSocketAsyncUserToken) { _DictSocketAsyncUserToken.Remove(token.Socket); }
+
+
+
+ //如果有事件,则调用事件,发送客户端数量变化通知
+ if (ClientNumberChange != null)
+ ClientNumberChange(-1, token);
+ // close the socket associated with the client
+ try
+ {
+ token.Socket.Shutdown(SocketShutdown.Send);
+ }
+ catch (Exception) { }
+ token.Socket.Close();
+ // decrement the counter keeping track of the total number of clients connected to the server
+ Interlocked.Decrement(ref m_clientCount);
+ m_maxNumberAcceptedClients.Release();
+
+
+ //试着加入一个释放
+ //token.Socket.Dispose();
+
+ // Free the SocketAsyncEventArg so they can be reused by another client
+ //e.UserToken = new AsyncUserToken();
+ //m_pool.Push(e);
+ //这里直接注释了进程池,需要验证是否会出问题
+ }
+
+ ///
+ /// 发送数据并计数
+ ///
+ ///
+ private void SendWithIndex(AsyncUserToken token, byte[] data)
+ {
+ try
+ {
+ //发送数据
+ SendMessage(token, data);
+
+ token.SendIndex = MaxSendIndexNum;
+ }
+ catch
+ {
+ OnCloseReady(token);
+ }
+ }
+
+ public AsyncUserToken GetAsyncUserTokenForSocket(Socket sk)
+ {
+ return _DictSocketAsyncUserToken.ContainsKey(sk) ? _DictSocketAsyncUserToken[sk] : null;
+ }
+
+ ///
+ /// 对外暴露的发送消息
+ ///
+ ///
+ /// 序列化之后的数据
+ public void SendToSocket(Socket sk, int CMDID, int ERRCODE, byte[] data)
+ {
+ AsyncUserToken token = GetAsyncUserTokenForSocket(sk);
+ HunterNet_S2C _s2cdata = new HunterNet_S2C();
+ _s2cdata.HunterNetCore_CmdID = CMDID;
+ _s2cdata.HunterNetCore_Data = data;
+ _s2cdata.HunterNetCore_ERRORCode = ERRCODE;
+ byte[] _finaldata = Serizlize(_s2cdata);
+ SendWithIndex(token, _finaldata);
+ }
+
+ ///
+ /// 发送心跳包
+ ///
+ ///
+ ///
+ private void SendHeartbeat(AsyncUserToken token)
+ {
+ if (token == null || token.Socket == null || !token.Socket.Connected)
+ return;
+ try
+ {
+ Console.WriteLine(DateTime.Now.ToString() + "发送心跳包");
+ token.SendIndex = MaxSendIndexNum;
+
+ //新建异步发送对象, 发送消息
+ SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
+ sendArg.UserToken = token;
+ sendArg.SetBuffer(HeartbeatData, 0, HeartbeatData.Length); //将数据放置进去.
+ token.Socket.SendAsync(sendArg);
+ }
+ catch (Exception e)
+ {
+ OnCloseReady(token);
+ }
+ }
+
+ private void DataCallBackReady(AsyncUserToken sk, byte[] data)
+ {
+
+ //增加接收计数
+ sk.RevIndex = MaxRevIndexNum;
+
+ if (data.Length == 1 && data[0] == 0x00)//心跳包
+ {
+ Console.WriteLine("收到心跳包");
+ //无处理
+ }
+ else
+ {
+ try
+ {
+ HunterNet_C2S _s2c = DeSerizlize(data);
+ DataCallBack(sk, (int)_s2c.HunterNetCore_CmdID, _s2c.HunterNetCore_Data);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("数据解析错误");
+ }
+ }
+ }
+
+ ///
+ /// 心跳包时钟事件
+ ///
+ ///
+ ///
+ private void CheckUpdatetimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ for (int i = 0; i < m_clients.Count(); i++)
+ {
+ //Console.WriteLine("RevIndex->{0} SendIndex->{1}", m_clients[i].RevIndex, m_clients[i].SendIndex);
+ //接收服务器数据计数
+ m_clients[i].RevIndex--;
+ if (m_clients[i].RevIndex <= 0)
+ {
+ //判定掉线
+ OnCloseReady(m_clients[i]);
+ return;
+ }
+
+ //发送计数
+ m_clients[i].SendIndex--;
+ if (m_clients[i].SendIndex <= 0)//需要发送心跳包了
+ {
+ //重置倒计时计数
+ m_clients[i].SendIndex = MaxSendIndexNum;
+ SendHeartbeat(m_clients[i]);
+ }
+ }
+ }
+ #endregion
+
+ public static byte[] Serizlize(T MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ Serializer.Serialize(ms, MsgObj);
+ byte[] data1 = ms.ToArray();
+ return data1;
+ }
+ }
+
+ public static T DeSerizlize(byte[] MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream(MsgObj))
+ {
+ var ds_obj = Serializer.Deserialize(ms);
+ //ds_obj = MySet(ds_obj);
+ return ds_obj;
+ }
+
+ }
+ }
+}
diff --git a/NetLib/HaoYueNet.ServerNetwork/NetWork/TokenMsgPool.cs b/NetLib/HaoYueNet.ServerNetwork/NetWork/TokenMsgPool.cs
new file mode 100644
index 0000000..5807533
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/NetWork/TokenMsgPool.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Sockets;
+using System.Text;
+
+namespace HaoYueNet.ServerNetwork
+{
+ public class TokenWithMsg
+ {
+ public AsyncUserToken token;
+ public byte[] message;
+ }
+
+ public class TokenMsgPool
+ {
+ //Stack msg_pool;
+ Queue msg_pool;
+
+ public TokenMsgPool(int capacity)
+ {
+ //msg_pool = new Stack(capacity);
+ msg_pool = new Queue(capacity);
+ }
+
+ //public void Push(TokenWithMsg item)
+ //{
+ // if (item == null) { throw new ArgumentNullException("Items added to a SocketAsyncEventArgsPool cannot be null"); }
+ // lock (msg_pool)
+ // {
+ // msg_pool.Push(item);
+ // }
+ //}
+
+ ///
+ /// 向 Queue 的末尾添加一个对象。
+ ///
+ ///
+ public void Enqueue(TokenWithMsg item)
+ {
+ lock (msg_pool)
+ {
+ msg_pool.Enqueue(item);
+ }
+ }
+
+ //移除并返回在 Queue 的开头的对象。
+ public TokenWithMsg Dequeue()
+ {
+ lock (msg_pool)
+ {
+ return msg_pool.Dequeue();
+ }
+ }
+
+ //// Removes a SocketAsyncEventArgs instance from the pool
+ //// and returns the object removed from the pool
+ //public TokenWithMsg Pop()
+ //{
+ // lock (msg_pool)
+ // {
+ // return msg_pool.Pop();
+ // }
+ //}
+
+ // The number of SocketAsyncEventArgs instances in the pool
+ public int Count
+ {
+ get { return msg_pool.Count; }
+ }
+
+ public void Clear()
+ {
+ msg_pool.Clear();
+ }
+ }
+}
diff --git a/NetLib/HaoYueNet.ServerNetwork/protobuf_HunterNetCore.cs b/NetLib/HaoYueNet.ServerNetwork/protobuf_HunterNetCore.cs
new file mode 100644
index 0000000..740871f
--- /dev/null
+++ b/NetLib/HaoYueNet.ServerNetwork/protobuf_HunterNetCore.cs
@@ -0,0 +1,120 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+// Option: missing-value detection (*Specified/ShouldSerialize*/Reset*) enabled
+
+// Generated from: proto/protobuf_HunterNetCore.proto
+namespace HunterProtobufCore
+{
+ [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"HunterNet_C2S")]
+ public partial class HunterNet_C2S : global::ProtoBuf.IExtensible
+ {
+ public HunterNet_C2S() {}
+
+ private int? _HunterNetCore_CmdID;
+ [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"HunterNetCore_CmdID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_CmdID
+ {
+ get { return _HunterNetCore_CmdID; }
+ set { _HunterNetCore_CmdID = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_CmdIDSpecified
+ {
+ get { return this._HunterNetCore_CmdID != null; }
+ set { if (value == (this._HunterNetCore_CmdID== null)) this._HunterNetCore_CmdID = value ? this.HunterNetCore_CmdID : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_CmdID() { return HunterNetCore_CmdIDSpecified; }
+ private void ResetHunterNetCore_CmdID() { HunterNetCore_CmdIDSpecified = false; }
+
+ private byte[] _HunterNetCore_Data;
+ [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"HunterNetCore_Data", DataFormat = global::ProtoBuf.DataFormat.Default)]
+ public byte[] HunterNetCore_Data
+ {
+ get { return _HunterNetCore_Data; }
+ set { _HunterNetCore_Data = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_DataSpecified
+ {
+ get { return this._HunterNetCore_Data != null; }
+ set { if (value == (this._HunterNetCore_Data== null)) this._HunterNetCore_Data = value ? this.HunterNetCore_Data : (byte[])null; }
+ }
+ private bool ShouldSerializeHunterNetCore_Data() { return HunterNetCore_DataSpecified; }
+ private void ResetHunterNetCore_Data() { HunterNetCore_DataSpecified = false; }
+
+ private global::ProtoBuf.IExtension extensionObject;
+ global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+ { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
+ }
+
+ [global::System.Serializable, global::ProtoBuf.ProtoContract(Name=@"HunterNet_S2C")]
+ public partial class HunterNet_S2C : global::ProtoBuf.IExtensible
+ {
+ public HunterNet_S2C() {}
+
+ private int? _HunterNetCore_CmdID;
+ [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"HunterNetCore_CmdID", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_CmdID
+ {
+ get { return _HunterNetCore_CmdID; }
+ set { _HunterNetCore_CmdID = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_CmdIDSpecified
+ {
+ get { return this._HunterNetCore_CmdID != null; }
+ set { if (value == (this._HunterNetCore_CmdID== null)) this._HunterNetCore_CmdID = value ? this.HunterNetCore_CmdID : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_CmdID() { return HunterNetCore_CmdIDSpecified; }
+ private void ResetHunterNetCore_CmdID() { HunterNetCore_CmdIDSpecified = false; }
+
+ private int? _HunterNetCore_ERRORCode;
+ [global::ProtoBuf.ProtoMember(2, IsRequired = false, Name=@"HunterNetCore_ERRORCode", DataFormat = global::ProtoBuf.DataFormat.TwosComplement)]
+ public int? HunterNetCore_ERRORCode
+ {
+ get { return _HunterNetCore_ERRORCode; }
+ set { _HunterNetCore_ERRORCode = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_ERRORCodeSpecified
+ {
+ get { return this._HunterNetCore_ERRORCode != null; }
+ set { if (value == (this._HunterNetCore_ERRORCode== null)) this._HunterNetCore_ERRORCode = value ? this.HunterNetCore_ERRORCode : (int?)null; }
+ }
+ private bool ShouldSerializeHunterNetCore_ERRORCode() { return HunterNetCore_ERRORCodeSpecified; }
+ private void ResetHunterNetCore_ERRORCode() { HunterNetCore_ERRORCodeSpecified = false; }
+
+ private byte[] _HunterNetCore_Data;
+ [global::ProtoBuf.ProtoMember(3, IsRequired = false, Name=@"HunterNetCore_Data", DataFormat = global::ProtoBuf.DataFormat.Default)]
+ public byte[] HunterNetCore_Data
+ {
+ get { return _HunterNetCore_Data; }
+ set { _HunterNetCore_Data = value; }
+ }
+ [global::System.Xml.Serialization.XmlIgnore]
+ [global::System.ComponentModel.Browsable(false)]
+ public bool HunterNetCore_DataSpecified
+ {
+ get { return this._HunterNetCore_Data != null; }
+ set { if (value == (this._HunterNetCore_Data== null)) this._HunterNetCore_Data = value ? this.HunterNetCore_Data : (byte[])null; }
+ }
+ private bool ShouldSerializeHunterNetCore_Data() { return HunterNetCore_DataSpecified; }
+ private void ResetHunterNetCore_Data() { HunterNetCore_DataSpecified = false; }
+
+ private global::ProtoBuf.IExtension extensionObject;
+ global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
+ { return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing); }
+ }
+
+}
\ No newline at end of file
diff --git a/Simple/SimpleClient/Network/NetBase.cs b/Simple/SimpleClient/Network/NetBase.cs
new file mode 100644
index 0000000..83aedbd
--- /dev/null
+++ b/Simple/SimpleClient/Network/NetBase.cs
@@ -0,0 +1,33 @@
+using ProtoBuf;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SimpleClient
+{
+ public static class NetBase
+ {
+ //序列化
+ public static byte[] Serizlize(T MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream())
+ {
+ Serializer.Serialize(ms, MsgObj);
+ byte[] data1 = ms.ToArray();
+ return data1;
+ }
+ }
+ //反序列化
+ public static T DeSerizlize(byte[] MsgObj)
+ {
+ using (MemoryStream ms = new MemoryStream(MsgObj))
+ {
+ var ds_obj = Serializer.Deserialize(ms);
+ return ds_obj;
+ }
+ }
+ }
+
+}
diff --git a/Simple/SimpleClient/Network/NetworkHelper.cs b/Simple/SimpleClient/Network/NetworkHelper.cs
new file mode 100644
index 0000000..3120917
--- /dev/null
+++ b/Simple/SimpleClient/Network/NetworkHelper.cs
@@ -0,0 +1,79 @@
+using HaoYueNet.ClientNetwork;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SimpleClient
+{
+ ///
+ /// 继承网络库,以支持网络功能
+ ///
+ public class NetworkHelper : NetworkHelperCore
+ {
+ public NetworkHelper()
+ {
+ //指定接收服务器数据事件
+ OnDataCallBack += GetDataCallBack;
+ //断开连接
+ OnClose += Close;
+ //网络库调试信息输出事件,用于打印连接断开,收发事件
+ OnLogOut += NetworkDeBugLog;
+ OnConnected += NetworkConnected;
+ }
+
+
+ public void NetworkConnected(bool IsConnect)
+ {
+ if (IsConnect)
+ NetworkDeBugLog("服务器连接成功");
+ else
+ {
+ NetworkDeBugLog("服务器连接失败");
+ //to do 重连逻辑
+ }
+ }
+
+ public void NetworkDeBugLog(string str)
+ {
+ //用于Unity内的输出
+ //Debug.Log("NetCoreDebug >> "+str);
+
+ Console.WriteLine("NetCoreDebug >> " + str);
+ }
+
+ ///
+ /// 接受包回调
+ ///
+ /// 协议ID
+ /// 错误编号
+ /// 业务数据
+ public void GetDataCallBack(int CMDID, int ERRCODE, byte[] data)
+
+ {
+ Console.WriteLine("收到消息 CMDID =>" + CMDID + " ERRCODE =>" + ERRCODE + " 数据长度=>" + data.Length);
+
+ try
+ {
+ //根据协议ID走不同逻辑
+ switch (CMDID)
+ {
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("逻辑处理错误:" + ex.ToString());
+ }
+
+ }
+
+ ///
+ /// 关闭连接
+ ///
+ public void Close()
+ {
+ Console.WriteLine("断开连接");
+ }
+ }
+}
diff --git a/Simple/SimpleClient/Program.cs b/Simple/SimpleClient/Program.cs
new file mode 100644
index 0000000..a121771
--- /dev/null
+++ b/Simple/SimpleClient/Program.cs
@@ -0,0 +1,22 @@
+// See https://aka.ms/new-console-template for more information
+
+using SimpleClient;
+
+Console.WriteLine("Hello, World!");
+
+StaticComm.networkHelper = new NetworkHelper();
+
+StaticComm.networkHelper.Init("127.0.0.1", 23846);
+
+while (true)
+{
+ string CommandStr = Console.ReadLine();
+ string Command = "";
+ Command = ((CommandStr.IndexOf(" ") <= 0) ? CommandStr : CommandStr.Substring(0, CommandStr.IndexOf(" ")));
+ switch (Command)
+ {
+ default:
+ Console.WriteLine("未知命令" + CommandStr);
+ break;
+ }
+}
\ No newline at end of file
diff --git a/Simple/SimpleClient/SimpleClient.csproj b/Simple/SimpleClient/SimpleClient.csproj
new file mode 100644
index 0000000..7a081c4
--- /dev/null
+++ b/Simple/SimpleClient/SimpleClient.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Simple/SimpleClient/StaticComm.cs b/Simple/SimpleClient/StaticComm.cs
new file mode 100644
index 0000000..2fcb9f5
--- /dev/null
+++ b/Simple/SimpleClient/StaticComm.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SimpleClient
+{
+ public class StaticComm
+ {
+ public static string TokenStr;
+ public static long RID = -1;
+ public static string IP;
+ public static int Port;
+
+
+ public static NetworkHelper networkHelper;
+ }
+}
diff --git a/Simple/SimpleServer/Manager/ClientManager.cs b/Simple/SimpleServer/Manager/ClientManager.cs
new file mode 100644
index 0000000..637c98b
--- /dev/null
+++ b/Simple/SimpleServer/Manager/ClientManager.cs
@@ -0,0 +1,186 @@
+using System.Net.Sockets;
+using System.Timers;
+
+namespace SimpleServer
+{
+ public class ClientInfo
+ {
+ public long UID { get; set; }
+ public long RID { get; set; }
+ public Socket _socket { get; set; }
+ public bool IsOffline { get; set; } = false;
+ public DateTime LogOutDT { get; set; }
+ }
+
+ public class ClientManager
+ {
+ public List ClientList = new List();
+ public Dictionary _DictRIDClient = new Dictionary();
+ public Dictionary _DictSocketClient = new Dictionary();
+ public Dictionary _DictUIDClient = new Dictionary();
+
+ private System.Timers.Timer _ClientCheckTimer;
+ private long _RemoveOfflineCacheMin;
+ public void Init(long ticktime,long RemoveOfflineCacheMin)
+ {
+ //换算成毫秒
+ _RemoveOfflineCacheMin = RemoveOfflineCacheMin * 1000;
+ _ClientCheckTimer = new System.Timers.Timer();
+ _ClientCheckTimer.Interval = ticktime;
+ _ClientCheckTimer.AutoReset = true;
+ _ClientCheckTimer.Elapsed += new ElapsedEventHandler(ClientCheckClearOffline_Elapsed);
+ _ClientCheckTimer.Enabled = true;
+ }
+
+ private void ClientCheckClearOffline_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ DateTime CheckDT = DateTime.Now.AddMinutes(-1 * _RemoveOfflineCacheMin);
+ ClientInfo[] OfflineClientlist = ClientList.Where(w => w.IsOffline == true && w.LogOutDT < CheckDT).ToArray();
+
+ Console.WriteLine("开始清理离线过久的玩家的缓存");
+ for (int i = 0; i < OfflineClientlist.Length; i++)
+ {
+ //to do 掉线处理
+ RemoveClient(OfflineClientlist[i]);
+ }
+ GC.Collect();
+ }
+
+
+ //通用处理
+ #region clientlist 处理
+
+ ///
+ /// 增加用户
+ ///
+ ///
+ public void AddClient(ClientInfo clientInfo)
+ {
+ try
+ {
+ Console.WriteLine("追加连接玩家 RID=>" + clientInfo.RID + " UID=>" + clientInfo.UID);
+ lock (ClientList)
+ {
+ _DictUIDClient.Add(clientInfo.UID, clientInfo);
+ _DictRIDClient.Add(clientInfo.RID, clientInfo);
+ _DictSocketClient.Add(clientInfo._socket, clientInfo);
+ ClientList.Add(clientInfo);
+ }
+ }
+ catch (Exception ex)
+ {
+ ex.ToString();
+ }
+ }
+
+ ///
+ /// 清理连接
+ ///
+ ///
+ public void RemoveClient(ClientInfo client)
+ {
+ lock (ClientList)
+ {
+ if(_DictUIDClient.ContainsKey(client.UID))
+ _DictUIDClient.Remove(client.UID);
+
+ if (_DictRIDClient.ContainsKey(client.RID))
+ _DictRIDClient.Remove(client.RID);
+
+ if (_DictSocketClient.ContainsKey(client._socket))
+ _DictSocketClient.Remove(client._socket);
+
+ ClientList.Remove(client);
+ }
+ }
+
+ public ClientInfo GetClientForRoleID(int RoleID)
+ {
+ return _DictRIDClient.ContainsKey(RoleID) ? _DictRIDClient[RoleID] : null;
+ }
+
+ public ClientInfo GetClientForSocket(Socket sk)
+ {
+ return _DictSocketClient.ContainsKey(sk) ? _DictSocketClient[sk] : null;
+ }
+
+ ///
+ /// 获取在线玩家
+ ///
+ ///
+ public List GetOnlineClientList()
+ {
+ return ClientList.Where(w => w.IsOffline == false).ToList();
+ }
+
+
+ ///
+ /// 设置玩家离线
+ ///
+ ///
+ public void SetClientOfflineForSocket(Socket sk)
+ {
+ if (!_DictSocketClient.ContainsKey(sk))
+ return;
+
+ Console.WriteLine("标记玩家RID"+ _DictSocketClient[sk].RID+ "为离线");
+ _DictSocketClient[sk].IsOffline = true;
+ _DictSocketClient[sk].LogOutDT = DateTime.Now;
+ }
+
+ public void RemoveClientForSocket(Socket sk)
+ {
+ if (!_DictSocketClient.ContainsKey(sk))
+ return;
+
+ RemoveClient(_DictSocketClient[sk]);
+ //ClientList.Remove(GetClientForSocket(sk));
+ }
+
+ #endregion
+
+ //public void AddClient_JoinGame(Socket sk)
+ //{
+ // var c = new ClientInfo();
+ // c = AutoRoleID;
+ // AddClient(c);
+ //}
+
+ ///
+ /// 给一组用户发送数据
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void ClientSend(List _toclientlist, int CMDID, int ERRCODE, byte[] data)
+ {
+ for (int i = 0; i < _toclientlist.Count();i++)
+ {
+ if (_toclientlist[i] == null || _toclientlist[i].IsOffline)
+ continue;
+ ServerManager.g_SocketMgr.SendToSocket(_toclientlist[i]._socket, CMDID,ERRCODE,data);
+ }
+ }
+
+ public void ClientSend(Socket _socket, int CMDID, int ERRCODE, byte[] data)
+ {
+ //Console.WriteLine("发送数据 CMDID->"+ CMDID);
+ ServerManager.g_SocketMgr.SendToSocket(_socket, CMDID, ERRCODE, data);
+ }
+
+ ///
+ /// 给一个连接发送数据
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void ClientSend(ClientInfo _c, int CMDID, int ERRCODE, byte[] data)
+ {
+ if (_c == null || _c.IsOffline)
+ return;
+ ServerManager.g_SocketMgr.SendToSocket(_c._socket, CMDID, ERRCODE, data);
+ }
+ }
+}
diff --git a/Simple/SimpleServer/Manager/ServerManager.cs b/Simple/SimpleServer/Manager/ServerManager.cs
new file mode 100644
index 0000000..354cb4c
--- /dev/null
+++ b/Simple/SimpleServer/Manager/ServerManager.cs
@@ -0,0 +1,8 @@
+namespace SimpleServer
+{
+ public static class ServerManager
+ {
+ public static ClientManager g_ClientMgr;
+ public static IOCPNetWork g_SocketMgr;
+ }
+}
\ No newline at end of file
diff --git a/Simple/SimpleServer/NetWork/IOCPNetWork.cs b/Simple/SimpleServer/NetWork/IOCPNetWork.cs
new file mode 100644
index 0000000..fef483d
--- /dev/null
+++ b/Simple/SimpleServer/NetWork/IOCPNetWork.cs
@@ -0,0 +1,78 @@
+using HaoYueNet.ServerNetwork;
+using System.Net.Sockets;
+
+namespace SimpleServer
+{
+ public class IOCPNetWork : SocketManager
+ {
+ public IOCPNetWork(int numConnections, int receiveBufferSize)
+ : base(numConnections, receiveBufferSize)
+ {
+ m_clientCount = 0;
+ m_maxConnectNum = numConnections;
+ m_revBufferSize = receiveBufferSize;
+ // allocate buffers such that the maximum number of sockets can have one outstanding read and
+ //write posted to the socket simultaneously
+ m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToAlloc, receiveBufferSize);
+
+ m_pool = new SocketEventPool(numConnections);
+ m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
+
+
+ ClientNumberChange += IOCPNetWork_ClientNumberChange;
+
+ }
+
+ private void IOCPNetWork_ClientNumberChange(int num, AsyncUserToken token)
+ {
+ Console.WriteLine("建立新的连接");
+ }
+
+ ///
+ /// 接受包回调
+ ///
+ /// 协议ID
+ /// 错误编号
+ /// 业务数据
+ public override void DataCallBack(AsyncUserToken token, int CMDID, byte[] data)
+ {
+ DataCallBackToOld(token.Socket, CMDID, data);
+ }
+
+ public void DataCallBackToOld(Socket sk, int CMDID, byte[] data)
+ {
+ //ServerManager.g_Log.Debug("收到消息 CMDID =>" + CMDID + " 数据长度=>" + data.Length);
+ try
+ {
+ switch (CMDID)
+ {
+
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("逻辑处理错误:" + ex.ToString());
+ }
+ }
+
+ ///
+ /// 断开连接
+ ///
+ ///
+ public override void OnClose(AsyncUserToken token)
+ {
+ OnCloseToOld(token.Socket);
+ }
+
+ ///
+ /// 断开连接
+ ///
+ ///
+ public void OnCloseToOld(Socket sk)
+ {
+ //ServerManager.g_Log.Debug("清理掉线");
+ ServerManager.g_ClientMgr.SetClientOfflineForSocket(sk);
+ }
+
+ }
+}
diff --git a/Simple/SimpleServer/Program.cs b/Simple/SimpleServer/Program.cs
new file mode 100644
index 0000000..451242a
--- /dev/null
+++ b/Simple/SimpleServer/Program.cs
@@ -0,0 +1,27 @@
+// See https://aka.ms/new-console-template for more information
+using SimpleServer;
+using System.Net;
+
+Console.WriteLine("Hello, World!");
+
+ServerManager.g_ClientMgr = new ClientManager();
+ServerManager.g_SocketMgr = new IOCPNetWork(1024, 1024);
+ServerManager.g_SocketMgr.Init();
+ServerManager.g_SocketMgr.Start(new IPEndPoint(IPAddress.Any.Address, 23846));
+Console.WriteLine("监听:" + 23846);
+Console.WriteLine("Succeed!");
+while (true)
+{
+ string CommandStr = Console.ReadLine();
+ string Command = "";
+ Command = ((CommandStr.IndexOf(" ") <= 0) ? CommandStr : CommandStr.Substring(0, CommandStr.IndexOf(" ")));
+ switch(Command)
+ {
+ case "list":
+ Console.WriteLine("当前在线:" + ServerManager.g_ClientMgr.ClientList.Count());
+ break;
+ default:
+ Console.WriteLine("未知命令"+CommandStr);
+ break;
+ }
+}
\ No newline at end of file
diff --git a/Simple/SimpleServer/SimpleServer.csproj b/Simple/SimpleServer/SimpleServer.csproj
new file mode 100644
index 0000000..10b64d3
--- /dev/null
+++ b/Simple/SimpleServer/SimpleServer.csproj
@@ -0,0 +1,14 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+