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 + + + + + + +