From 2fa5af62c7d0b216e9c80b5ddbeb4ec7a536aa6a Mon Sep 17 00:00:00 2001 From: sin365 <353374337@qq.com> Date: Fri, 10 Dec 2021 23:38:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E6=AC=A1=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 | Bin 0 -> 108844 bytes .vs/HaoYueNet/project-colors.json | 36 + .vs/HaoYueNet/v17/.futdcache.v1 | Bin 0 -> 1070 bytes HaoYueNet.sln | 53 ++ .../HaoYueNet.ClientNetwork.csproj | 13 + .../NetworkHelperCore.cs | 360 ++++++++ .../protobuf_HunterNetCore.cs | 120 +++ .../HaoYueNet.ServerNetwork.csproj | 13 + .../NetWork/AsyncUserToken.cs | 57 ++ .../NetWork/BufferManager.cs | 64 ++ .../NetWork/SocketEventPool.cs | 49 + .../NetWork/SocketManager.cs | 838 ++++++++++++++++++ .../NetWork/TokenMsgPool.cs | 77 ++ .../protobuf_HunterNetCore.cs | 120 +++ Simple/SimpleClient/Network/NetBase.cs | 33 + Simple/SimpleClient/Network/NetworkHelper.cs | 79 ++ Simple/SimpleClient/Program.cs | 22 + Simple/SimpleClient/SimpleClient.csproj | 19 + Simple/SimpleClient/StaticComm.cs | 19 + Simple/SimpleServer/Manager/ClientManager.cs | 186 ++++ Simple/SimpleServer/Manager/ServerManager.cs | 8 + Simple/SimpleServer/NetWork/IOCPNetWork.cs | 78 ++ Simple/SimpleServer/Program.cs | 27 + Simple/SimpleServer/SimpleServer.csproj | 14 + 24 files changed, 2285 insertions(+) create mode 100644 .vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 create mode 100644 .vs/HaoYueNet/project-colors.json create mode 100644 .vs/HaoYueNet/v17/.futdcache.v1 create mode 100644 HaoYueNet.sln create mode 100644 NetLib/HaoYueNet.ClientNetwork/HaoYueNet.ClientNetwork.csproj create mode 100644 NetLib/HaoYueNet.ClientNetwork/NetworkHelperCore.cs create mode 100644 NetLib/HaoYueNet.ClientNetwork/protobuf_HunterNetCore.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/HaoYueNet.ServerNetwork.csproj create mode 100644 NetLib/HaoYueNet.ServerNetwork/NetWork/AsyncUserToken.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/NetWork/BufferManager.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/NetWork/SocketEventPool.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/NetWork/SocketManager.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/NetWork/TokenMsgPool.cs create mode 100644 NetLib/HaoYueNet.ServerNetwork/protobuf_HunterNetCore.cs create mode 100644 Simple/SimpleClient/Network/NetBase.cs create mode 100644 Simple/SimpleClient/Network/NetworkHelper.cs create mode 100644 Simple/SimpleClient/Program.cs create mode 100644 Simple/SimpleClient/SimpleClient.csproj create mode 100644 Simple/SimpleClient/StaticComm.cs create mode 100644 Simple/SimpleServer/Manager/ClientManager.cs create mode 100644 Simple/SimpleServer/Manager/ServerManager.cs create mode 100644 Simple/SimpleServer/NetWork/IOCPNetWork.cs create mode 100644 Simple/SimpleServer/Program.cs create mode 100644 Simple/SimpleServer/SimpleServer.csproj diff --git a/.vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 b/.vs/HaoYueNet/DesignTimeBuild/.dtbcache.v2 new file mode 100644 index 0000000000000000000000000000000000000000..59f948de425addbd3424a5efb090686adbe44d8e GIT binary patch literal 108844 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

x(n%Uq2ahdk)A?&8tGZ2=a8O9 zdI9N0q?eIiL3$PGb)+|t-b8u}>20KUk={dkAL&D+kB~l*!%sey`Dbzv$`@+9$ye&* z8};$6`uJWALUCzygNxA(Ka%(n;wKV6LHt7E7l_|T{08v{i9aCzBJmf*KP3Kv2++gQ z03Mb$A<+aPL?Q%{Lm~$vOd<@?j6^et?MQ3~u|0|HA$B0K1H_Ibc7)i8#7+=xNVI|2 zg~TopyOP)yVs{d|L+nXnPl)y;+Cy|8(E(yV68k~yPhx+F14$eR(TPMSh%O|$Ky)S1 z6(XNRK13mjLWmwDdO-9d(F>vvi9QeqlQbj#4v~vBt}4tA~6bL42dxiV@ZsK7*Aq6#6%JkAtsZU3~?lhBO!`O6hjm1rQgKxDeuE5*I^ULgEsL%Sc=XaRrGhAg&^D6~xsfu7

vITOihvSO;-CiQ6IWBylIiT_o;;*g#?f#N8zB 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6d16ed6da647e2193f3ce320dd30e439fec2d35a GIT binary patch literal 1070 zcmd;NU|_Ixv5N6X%#SQh^-C>5VmaqzrskCZ#me)GvSUzqddbBFMfq7wK;68~`FUxX z>7_-9C7JnotS+fZrRf|2If*4{`9-;Gj(L^N0im2$A$x&J!2p+S!Kp=MsYNi`Pku3+fQB^= t3lSLz)qx~B1QZnQM + + + 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 + + + + + + +