sin365
1 month ago
20 changed files with 3901 additions and 0 deletions
@ -0,0 +1,167 @@
|
||||
using System; |
||||
using System.Net.Sockets; |
||||
|
||||
namespace HaoYueNet.ClientNetworkNet.Standard2 |
||||
{ |
||||
public static class BaseData |
||||
{ |
||||
/// <summary> |
||||
/// 心跳包数据 |
||||
/// </summary> |
||||
public static byte[] HeartbeatData = new byte[5] { 0x05, 0x00, 0x00, 0x00, 0x00 }; |
||||
|
||||
public static void writeInt(byte[] buf, int offset, int value) |
||||
{ |
||||
buf[offset++] = (byte)(255 & value); |
||||
buf[offset++] = (byte)(255 & value >> 8); |
||||
buf[offset++] = (byte)(255 & value >> 16); |
||||
//buf[offset++] = (byte)(255 & value >>> 24); |
||||
buf[offset++] = (byte)(255 & value >> 24); |
||||
} |
||||
|
||||
public static void writeUInt16(byte[] buf, int offset, int value) |
||||
{ |
||||
buf[offset++] = (byte)(255 & value); |
||||
buf[offset++] = (byte)(255 & value >> 8); |
||||
} |
||||
|
||||
public static class HunterNet_Heartbeat |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs) |
||||
{ |
||||
myreadEventArgs.SetBuffer(HeartbeatData, 0, HeartbeatData.Length); |
||||
} |
||||
} |
||||
|
||||
public static class HunterNet_S2C |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs, UInt16 CmdID, UInt16 Error, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] data = CreatePkgData(CmdID, Error, AddonBytes_Data); |
||||
myreadEventArgs.SetBuffer(data,0, data.Length); |
||||
} |
||||
|
||||
public static byte[] CreatePkgData(UInt16 CmdID, UInt16 Error, byte[] AddonBytes_Data) |
||||
{ |
||||
//byte[] AddonBytes_CmdID = BitConverter.GetBytes(CmdID); |
||||
//byte[] AddonBytes_Error = BitConverter.GetBytes(Error); |
||||
//int AllLenght = AddonBytes_CmdID.Length + AddonBytes_Error.Length + AddonBytes_Data.Length + 4; |
||||
//int LastIndex = 0; |
||||
////包长度 |
||||
//byte[] AddonBytes_Lenght = BitConverter.GetBytes(AllLenght); |
||||
|
||||
//byte[] BufferData = new byte[AllLenght]; |
||||
////包长度 |
||||
//AddonBytes_Lenght.CopyTo(BufferData, LastIndex); |
||||
//LastIndex += AddonBytes_Lenght.Length; |
||||
|
||||
////CMDID |
||||
//AddonBytes_CmdID.CopyTo(BufferData, LastIndex); |
||||
//LastIndex += AddonBytes_CmdID.Length; |
||||
|
||||
////Error |
||||
//AddonBytes_Error.CopyTo(BufferData, LastIndex); |
||||
//LastIndex += AddonBytes_Error.Length; |
||||
|
||||
////DATA |
||||
//AddonBytes_Data.CopyTo(BufferData, LastIndex); |
||||
//LastIndex += AddonBytes_Data.Length; |
||||
//return BufferData; |
||||
|
||||
////用Buffer.BlockCopy拷贝 |
||||
//byte[] AddonBytes_CmdID = BitConverter.GetBytes(CmdID); |
||||
//byte[] AddonBytes_Error = BitConverter.GetBytes(Error); |
||||
//int AllLenght = AddonBytes_CmdID.Length + AddonBytes_Error.Length + AddonBytes_Data.Length + 4; |
||||
//int LastIndex = 0; |
||||
////包长度 |
||||
//byte[] AddonBytes_Lenght = BitConverter.GetBytes(AllLenght); |
||||
|
||||
//byte[] BufferData = new byte[AllLenght]; |
||||
////包长度 |
||||
//Buffer.BlockCopy(AddonBytes_Lenght, 0, BufferData, LastIndex, AddonBytes_Lenght.Length); |
||||
//LastIndex += AddonBytes_Lenght.Length; |
||||
|
||||
////CMDID |
||||
//Buffer.BlockCopy(AddonBytes_CmdID, 0, BufferData, LastIndex, AddonBytes_CmdID.Length); |
||||
//LastIndex += AddonBytes_CmdID.Length; |
||||
|
||||
////Error |
||||
//Buffer.BlockCopy(AddonBytes_Error, 0, BufferData, LastIndex, AddonBytes_Error.Length); |
||||
//LastIndex += AddonBytes_Error.Length; |
||||
|
||||
////DATA |
||||
//Buffer.BlockCopy(AddonBytes_Data, 0, BufferData, LastIndex, AddonBytes_Data.Length); |
||||
//LastIndex += AddonBytes_Data.Length; |
||||
|
||||
|
||||
|
||||
//用Buffer.BlockCopy拷贝 |
||||
//包长度 |
||||
int AllLenght = 4 + 2 + 2 + AddonBytes_Data.Length; |
||||
byte[] BufferData = new byte[AllLenght]; |
||||
|
||||
//包长度 |
||||
writeInt(BufferData, 0, AllLenght); |
||||
|
||||
//CMDID |
||||
writeUInt16(BufferData, 4, CmdID); |
||||
|
||||
//Error |
||||
writeUInt16(BufferData, 4 + 2, CmdID); |
||||
|
||||
//DATA |
||||
Buffer.BlockCopy(AddonBytes_Data, 0, BufferData, 4 + 2 + 2, AddonBytes_Data.Length); |
||||
|
||||
return BufferData; |
||||
} |
||||
|
||||
public static void AnalysisPkgData(byte[] srcdata, out UInt16 CmdID, out UInt16 Error, out byte[] data) |
||||
{ |
||||
CmdID = BitConverter.ToUInt16(srcdata, 0); |
||||
Error = BitConverter.ToUInt16(srcdata, 2); |
||||
data = new byte[srcdata.Length - 2 - 2]; |
||||
Array.Copy(srcdata, 4, data, 0, data.Length); |
||||
|
||||
} |
||||
} |
||||
|
||||
public static class HunterNet_C2S |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs, UInt16 CmdID, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] data = CreatePkgData(CmdID, AddonBytes_Data); |
||||
myreadEventArgs.SetBuffer(data, 0, data.Length); |
||||
} |
||||
|
||||
public static byte[] CreatePkgData(UInt16 CmdID, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] AddonBytes_CmdID = BitConverter.GetBytes(CmdID); |
||||
int AllLenght = AddonBytes_CmdID.Length + AddonBytes_Data.Length + 4; |
||||
int LastIndex = 0; |
||||
//包长度 |
||||
byte[] AddonBytes_Lenght = BitConverter.GetBytes(AllLenght); |
||||
|
||||
byte[] BufferData = new byte[AllLenght]; |
||||
//包长度 |
||||
Buffer.BlockCopy(AddonBytes_Lenght, 0, BufferData, LastIndex, AddonBytes_Lenght.Length); |
||||
LastIndex += AddonBytes_Lenght.Length; |
||||
|
||||
//CMDID |
||||
Buffer.BlockCopy(AddonBytes_CmdID, 0, BufferData, LastIndex, AddonBytes_CmdID.Length); |
||||
LastIndex += AddonBytes_CmdID.Length; |
||||
|
||||
//DATA |
||||
Buffer.BlockCopy(AddonBytes_Data, 0, BufferData, LastIndex, AddonBytes_Data.Length); |
||||
LastIndex += AddonBytes_Data.Length; |
||||
return BufferData; |
||||
} |
||||
|
||||
public static void AnalysisPkgData(byte[] srcdata, out UInt16 CmdID, out byte[] data) |
||||
{ |
||||
data = new byte[srcdata.Length - 2]; |
||||
CmdID = BitConverter.ToUInt16(srcdata, 0); |
||||
Array.Copy(srcdata, 2, data, 0, data.Length); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
</PropertyGroup> |
||||
<PropertyGroup> |
||||
<!--避免 错误 CS8630 无效的 nullable 值: C# 7.3 的“Enable”。请使用语言版本 8.0 或更高版本 --> |
||||
<LangVersion>8.0</LangVersion> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,388 @@
|
||||
//using HunterProtobufCore; |
||||
using System; |
||||
using System.IO; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using static HaoYueNet.ClientNetworkNet.Standard2.BaseData; |
||||
|
||||
namespace HaoYueNet.ClientNetworkNet.Standard2 |
||||
{ |
||||
public class NetworkHelperCore |
||||
{ |
||||
private Socket client; |
||||
|
||||
|
||||
////响应倒计时计数最大值 |
||||
//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 static string LastConnectIP; |
||||
public static int LastConnectPort; |
||||
public bool bDetailedLog = false; |
||||
|
||||
public bool Init(string IP, int port,bool isHadDetailedLog = true, bool bBindReuseAddress = false,int bBindport = 0) |
||||
{ |
||||
LogOut("==>初始化网络核心"); |
||||
|
||||
bDetailedLog = isHadDetailedLog; |
||||
RevIndex = MaxRevIndexNum; |
||||
SendIndex = MaxSendIndexNum; |
||||
|
||||
client = new Socket(SocketType.Stream, ProtocolType.Tcp); |
||||
if (bBindReuseAddress) |
||||
{ |
||||
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); |
||||
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, Convert.ToInt32(bBindport)); |
||||
client.Bind(ipe); |
||||
} |
||||
LastConnectIP = IP; |
||||
LastConnectPort = port; |
||||
return Connect(IP, port); |
||||
} |
||||
|
||||
bool Connect(string IP, int port) |
||||
{ |
||||
//带回调的 |
||||
try |
||||
{ |
||||
if(bDetailedLog) |
||||
LogOut("连接到远程IP " + IP + ":" + port); |
||||
else |
||||
LogOut("连接到远程服务"); |
||||
|
||||
client.Connect(IP, port); |
||||
Thread thread = new Thread(Recive); |
||||
thread.IsBackground = true; |
||||
thread.Start(client); |
||||
int localport = ((IPEndPoint)client.LocalEndPoint).Port; |
||||
|
||||
if (bDetailedLog) |
||||
LogOut($"连接成功!连接到远程IP->{IP}:{port} | 本地端口->{localport}"); |
||||
else |
||||
LogOut("连接成功!"); |
||||
|
||||
if (_heartTimer == null) |
||||
{ |
||||
_heartTimer = new System.Timers.Timer(); |
||||
} |
||||
_heartTimer.Interval = TimerInterval; |
||||
_heartTimer.Elapsed += CheckUpdatetimer_Elapsed; |
||||
_heartTimer.AutoReset = true; |
||||
_heartTimer.Enabled = true; |
||||
|
||||
if (bDetailedLog) |
||||
LogOut("开启心跳包检测"); |
||||
|
||||
OnConnected?.Invoke(true); |
||||
return true; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
if (bDetailedLog) |
||||
LogOut("连接失败:" + ex.ToString()); |
||||
else |
||||
LogOut("连接失败"); |
||||
|
||||
OnConnected?.Invoke(false); |
||||
return 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("发送心跳包"); |
||||
} |
||||
|
||||
object sendLock = new object(); |
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
private void SendWithIndex(byte[] data) |
||||
{ |
||||
lock (sendLock) |
||||
{ |
||||
//增加发送计数 |
||||
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; |
||||
//} |
||||
|
||||
/// <summary> |
||||
/// 供外部调用 发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="data">序列化之后的数据</param> |
||||
public void SendToServer(int CMDID,byte[] data) |
||||
{ |
||||
//LogOut("准备数据 CMDID=> "+CMDID); |
||||
/* |
||||
HunterNet_C2S _c2sdata = new HunterNet_C2S(); |
||||
_c2sdata.HunterNetCoreCmdID = CMDID; |
||||
_c2sdata.HunterNetCoreData = ByteString.CopyFrom(data); |
||||
byte[] _finaldata = Serizlize(_c2sdata); |
||||
*/ |
||||
byte[] _finaldata = HunterNet_C2S.CreatePkgData((ushort)CMDID, data); |
||||
SendToSocket(_finaldata); |
||||
} |
||||
|
||||
#region 事件定义 |
||||
public delegate void OnReceiveDataHandler(int CMDID, int ERRCODE, byte[] data); |
||||
public delegate void OnConnectedHandler(bool IsConnected); |
||||
public delegate void OnCloseHandler(); |
||||
public delegate void OnLogOutHandler(string Msg); |
||||
#endregion |
||||
|
||||
public event OnConnectedHandler OnConnected; |
||||
public event OnReceiveDataHandler OnReceiveData; |
||||
public event OnCloseHandler OnClose; |
||||
/// <summary> |
||||
/// 网络库调试日志输出 |
||||
/// </summary> |
||||
public event OnLogOutHandler OnLogOut; |
||||
|
||||
/// <summary> |
||||
/// 做好处理的连接管理 |
||||
/// </summary> |
||||
private void OnCloseReady() |
||||
{ |
||||
|
||||
if (bDetailedLog) |
||||
LogOut("关闭心跳包计数"); |
||||
_heartTimer.Enabled = false; |
||||
_heartTimer.Elapsed -= CheckUpdatetimer_Elapsed; |
||||
LogOut("关闭连接"); |
||||
//关闭Socket连接 |
||||
client.Close(); |
||||
OnClose?.Invoke(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 主动关闭连接 |
||||
/// </summary> |
||||
public void CloseConntect() |
||||
{ |
||||
OnCloseReady(); |
||||
} |
||||
|
||||
private void DataCallBackReady(byte[] data) |
||||
{ |
||||
|
||||
//增加接收计数 |
||||
RevIndex = MaxRevIndexNum; |
||||
|
||||
//不处理心跳包 |
||||
if (data.Length == 1 && data[0] == 0x00) |
||||
{ |
||||
//LogOut("收到心跳包"); |
||||
return; |
||||
} |
||||
|
||||
/* |
||||
HunterNet_S2C _c2s = DeSerizlize<HunterNet_S2C>(data); |
||||
|
||||
OnReceiveData(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray()); |
||||
*/ |
||||
|
||||
HunterNet_S2C.AnalysisPkgData(data, out ushort CmdID, out ushort Error, out byte[] resultdata); |
||||
OnReceiveData(CmdID, Error, resultdata); |
||||
} |
||||
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流 |
||||
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();//创建新的内存流 |
||||
*/ |
||||
|
||||
//流复用的方式 不用重新new申请 |
||||
memoryStream.Position = 0; |
||||
memoryStream.SetLength(0); |
||||
|
||||
memoryStream.Write(getData, StartIndex, getData.Length - StartIndex);//从新将接受的消息写入内存流 |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
//把头去掉,就可以吃了,蛋白质是牛肉的六倍 |
||||
//DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray()); |
||||
|
||||
int CoreLenght = HeadLength - 4; |
||||
|
||||
//改为Array.Copy 提升效率 |
||||
//byte[] retData = new byte[CoreLenght]; |
||||
//Array.Copy(getData, StartIndex + 4, retData, 0, CoreLenght); |
||||
//DataCallBackReady(retData); |
||||
|
||||
//用Span |
||||
//Span<byte> getData_span = getData; |
||||
//getData_span = getData_span.Slice(StartIndex + 4,CoreLenght); |
||||
byte[] getData_span = new byte[CoreLenght]; |
||||
//DATA |
||||
Buffer.BlockCopy(getData, StartIndex + 4, getData_span, 0, CoreLenght); |
||||
DataCallBackReady(getData_span); |
||||
|
||||
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 void LogOut(string Msg) |
||||
{ |
||||
//Console.WriteLine(Msg); |
||||
OnLogOut?.Invoke(Msg); |
||||
} |
||||
|
||||
public Socket GetClientSocket() |
||||
{ |
||||
return client; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,364 @@
|
||||
using System; |
||||
using System.IO; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using static HaoYueNet.ClientNetworkNet.Standard2.BaseData; |
||||
|
||||
namespace HaoYueNet.ClientNetworkNet.Standard2 |
||||
{ |
||||
public class NetworkHelperP2PCore |
||||
{ |
||||
private Socket client; |
||||
|
||||
//响应倒计时计数最大值 |
||||
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, bool bBindReuseAddress = false,int bBindport = 0) |
||||
{ |
||||
|
||||
LogOut("==>初始化网络核心"); |
||||
|
||||
RevIndex = MaxRevIndexNum; |
||||
SendIndex = MaxSendIndexNum; |
||||
|
||||
client = new Socket(SocketType.Stream, ProtocolType.Tcp); |
||||
if (bBindReuseAddress) |
||||
{ |
||||
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); |
||||
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, Convert.ToInt32(bBindport)); |
||||
client.Bind(ipe); |
||||
} |
||||
Connect(IP, port); |
||||
} |
||||
|
||||
public bool Connect(string IP, int port) |
||||
{ |
||||
//带回调的 |
||||
try |
||||
{ |
||||
LogOut("连接到远程IP " + IP + ":" + port); |
||||
client.Connect(IP, port); |
||||
Thread thread = new Thread(Recive); |
||||
thread.IsBackground = true; |
||||
thread.Start(client); |
||||
int localport = ((IPEndPoint)client.LocalEndPoint).Port; |
||||
LogOut($"连接成功!连接到远程IP->{IP}:{port} | 本地端口->{localport}"); |
||||
|
||||
if (_heartTimer == null) |
||||
{ |
||||
_heartTimer = new System.Timers.Timer(); |
||||
} |
||||
_heartTimer.Interval = TimerInterval; |
||||
_heartTimer.Elapsed += CheckUpdatetimer_Elapsed; |
||||
_heartTimer.AutoReset = true; |
||||
_heartTimer.Enabled = true; |
||||
LogOut("开启心跳包检测"); |
||||
|
||||
OnConnected(true); |
||||
return true; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
LogOut("连接失败:" + ex.ToString()); |
||||
OnConnected(false); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
~NetworkHelperP2PCore() |
||||
{ |
||||
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(BaseData.HeartbeatData); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
//连接断开 |
||||
OnCloseReady(); |
||||
return; |
||||
} |
||||
//LogOut("发送心跳包"); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
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; |
||||
//} |
||||
|
||||
/// <summary> |
||||
/// 供外部调用 发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="ERRCODE"></param> |
||||
/// <param name="data"></param> |
||||
public void SendToSocket(int CMDID, int ERRCODE, byte[] data) |
||||
{ |
||||
//LogOut("准备数据 CMDID=> "+CMDID); |
||||
/*HunterNet_S2C _s2sdata = new HunterNet_S2C(); |
||||
_s2sdata.HunterNetCoreCmdID = CMDID; |
||||
_s2sdata.HunterNetCoreERRORCode = ERRCODE; |
||||
_s2sdata.HunterNetCoreData = ByteString.CopyFrom(data); |
||||
byte[] _finaldata = Serizlize(_s2sdata);*/ |
||||
|
||||
byte[] _finaldata = HunterNet_S2C.CreatePkgData((ushort)CMDID, (ushort)ERRCODE, data); |
||||
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); |
||||
|
||||
/// <summary> |
||||
/// 网络库调试日志输出 |
||||
/// </summary> |
||||
public event delegate_str OnLogOut; |
||||
|
||||
///// <summary> |
||||
///// 用于调用者回调的虚函数 |
||||
///// </summary> |
||||
///// <param name="data"></param> |
||||
//public virtual void DataCallBack(int CMDID,int ERRCODE,byte[] data) |
||||
//{ |
||||
|
||||
//} |
||||
|
||||
///// <summary> |
||||
///// 断开连接 |
||||
///// </summary> |
||||
///// <param name="sk"></param> |
||||
//public virtual void OnClose() |
||||
//{ |
||||
|
||||
//} |
||||
|
||||
/// <summary> |
||||
/// 做好处理的连接管理 |
||||
/// </summary> |
||||
private void OnCloseReady() |
||||
{ |
||||
LogOut("关闭心跳包计数"); |
||||
_heartTimer.Enabled = false; |
||||
_heartTimer.Elapsed -= CheckUpdatetimer_Elapsed; |
||||
LogOut("关闭连接"); |
||||
//关闭Socket连接 |
||||
client.Close(); |
||||
OnClose(); |
||||
} |
||||
|
||||
|
||||
/// <summary> |
||||
/// 主动关闭连接 |
||||
/// </summary> |
||||
public void CloseConntect() |
||||
{ |
||||
OnCloseReady(); |
||||
} |
||||
|
||||
private void DataCallBackReady(byte[] data) |
||||
{ |
||||
|
||||
//增加接收计数 |
||||
RevIndex = MaxRevIndexNum; |
||||
|
||||
//不处理心跳包 |
||||
if (data.Length == 1 && data[0] == 0x00) |
||||
{ |
||||
//LogOut("收到心跳包"); |
||||
return; |
||||
} |
||||
|
||||
/* |
||||
HunterNet_S2C _c2s = DeSerizlize<HunterNet_S2C>(data); |
||||
OnDataCallBack(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray()); |
||||
*/ |
||||
HunterNet_S2C.AnalysisPkgData(data, out ushort CmdID, out ushort Error, out byte[] resultdata); |
||||
OnDataCallBack(CmdID, Error, resultdata); |
||||
} |
||||
|
||||
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流 |
||||
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();//创建新的内存流 |
||||
*/ |
||||
|
||||
//流复用的方式 不用重新new申请 |
||||
memoryStream.Position = 0; |
||||
memoryStream.SetLength(0); |
||||
|
||||
memoryStream.Write(getData, StartIndex, getData.Length - StartIndex);//从新将接受的消息写入内存流 |
||||
break; |
||||
} |
||||
else |
||||
{ |
||||
//把头去掉,就可以吃了,蛋白质是牛肉的六倍 |
||||
int CoreLenght = HeadLength - 4; |
||||
byte[] getData_span = new byte[CoreLenght]; |
||||
//DATA |
||||
Buffer.BlockCopy(getData, StartIndex + 4, getData_span, 0, CoreLenght); |
||||
DataCallBackReady(getData_span); |
||||
|
||||
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 void LogOut(string Msg) |
||||
{ |
||||
//Console.WriteLine(Msg); |
||||
OnLogOut(Msg); |
||||
} |
||||
|
||||
public Socket GetClientSocket() |
||||
{ |
||||
return client; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,240 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Linq; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using System.Threading.Tasks; |
||||
|
||||
namespace HaoYueNet.ClientNetworkNet.Standard2.OtherMode |
||||
{ |
||||
public class NetworkHelperCore_ListenerMode |
||||
{ |
||||
private Socket serversocket; |
||||
private Dictionary<IntPtr, Socket> mDictHandleClient; |
||||
|
||||
//响应倒计时计数最大值 |
||||
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; |
||||
|
||||
public static string LastConnectIP; |
||||
public static int LastConnectPort; |
||||
public bool bDetailedLog = false; |
||||
|
||||
public void Init(int port) |
||||
{ |
||||
mDictHandleClient = new Dictionary<IntPtr, Socket>(); |
||||
|
||||
LogOut("==>初始化NetworkHelperCore_ListenerMode"); |
||||
serversocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); |
||||
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, port); |
||||
serversocket.Bind(endPoint); // 绑定 |
||||
serversocket.Listen(1); |
||||
//client = serversocket.Accept(); // 接收客户端连接 |
||||
//OnConnected?.Invoke(true); |
||||
//Console.WriteLine("客户端连接成功 信息: " + client.AddressFamily.ToString()); |
||||
//Thread revThread = new Thread(Recive); |
||||
//revThread.Start(client); |
||||
|
||||
Task task = new Task(() => |
||||
{ |
||||
while (true) |
||||
{ |
||||
Socket newclient; |
||||
try |
||||
{ |
||||
newclient = serversocket.Accept(); // 接收客户端连接 |
||||
} |
||||
catch |
||||
{ |
||||
break; |
||||
} |
||||
AddDictSocket(newclient); |
||||
OnConnected?.Invoke(newclient); |
||||
Console.WriteLine("客户端连接成功 信息: " + newclient.AddressFamily.ToString()); |
||||
Thread revThread = new Thread(Recive); |
||||
revThread.Start(newclient); |
||||
} |
||||
}); |
||||
task.Start(); |
||||
} |
||||
|
||||
#region |
||||
|
||||
/// <summary> |
||||
/// 追加Socket返回下标 |
||||
/// </summary> |
||||
/// <param name="socket"></param> |
||||
/// <returns></returns> |
||||
public void AddDictSocket(Socket socket) |
||||
{ |
||||
if (socket == null) |
||||
return; |
||||
lock (mDictHandleClient) |
||||
{ |
||||
mDictHandleClient[socket.Handle] = socket; |
||||
} |
||||
} |
||||
|
||||
public void RemoveDictSocket(Socket socket) |
||||
{ |
||||
if (socket == null) |
||||
return; |
||||
lock (mDictHandleClient) |
||||
{ |
||||
if (!mDictHandleClient.ContainsKey(socket.Handle)) |
||||
return; |
||||
mDictHandleClient.Remove(socket.Handle); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
~NetworkHelperCore_ListenerMode() |
||||
{ |
||||
IntPtr[] keys = mDictHandleClient.Keys.ToArray(); |
||||
for (uint i = 0; i < keys.Length; i++) |
||||
{ |
||||
mDictHandleClient[keys[i]].Close(); |
||||
} |
||||
mDictHandleClient.Clear(); |
||||
} |
||||
|
||||
private void SendToSocket(Socket socket, byte[] data) |
||||
{ |
||||
//已拼接包长度,这里不再需要拼接长度 |
||||
//data = SendDataWithHead(data); |
||||
try |
||||
{ |
||||
SendWithIndex(socket,data); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
//连接断开 |
||||
OnCloseReady(socket); |
||||
return; |
||||
} |
||||
//LogOut("发送消息,消息长度=> "+data.Length); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
private void SendWithIndex(Socket socket,byte[] data) |
||||
{ |
||||
//增加发送计数 |
||||
SendIndex = MaxSendIndexNum; |
||||
//发送数据 |
||||
socket.Send(data); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 供外部调用 发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="data">序列化之后的数据</param> |
||||
public void SendToClient(Socket socket, byte[] data) |
||||
{ |
||||
//LogOut("准备数据 data=> "+data); |
||||
SendToSocket(socket, data); |
||||
} |
||||
|
||||
#region 事件定义 |
||||
public delegate void OnConnectedHandler(Socket socket); |
||||
|
||||
public delegate void OnReceiveDataHandler(Socket sk, byte[] data); |
||||
|
||||
public delegate void OnDisconnectHandler(Socket sk); |
||||
|
||||
public delegate void OnNetLogHandler(string msg); |
||||
#endregion |
||||
|
||||
public event OnConnectedHandler OnConnected; |
||||
|
||||
public event OnReceiveDataHandler OnReceive; |
||||
|
||||
public event OnDisconnectHandler OnDisconnected; |
||||
|
||||
public event OnNetLogHandler OnNetLog; |
||||
|
||||
/// <summary> |
||||
/// 做好处理的连接管理 |
||||
/// </summary> |
||||
private void OnCloseReady(Socket socket) |
||||
{ |
||||
LogOut("关闭连接"); |
||||
//关闭Socket连接 |
||||
socket.Close(); |
||||
RemoveDictSocket(socket); |
||||
OnDisconnected?.Invoke(socket); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 主动关闭连接 |
||||
/// </summary> |
||||
public void CloseConntect(Socket socket) |
||||
{ |
||||
OnCloseReady(socket); |
||||
} |
||||
|
||||
private void DataCallBackReady(Socket socket,byte[] data) |
||||
{ |
||||
//增加接收计数 |
||||
RevIndex = MaxRevIndexNum; |
||||
OnReceive(socket,data); |
||||
} |
||||
|
||||
private void Recive(object o) |
||||
{ |
||||
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流 |
||||
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(client); |
||||
return; |
||||
//断开连接 |
||||
} |
||||
if (effective > 0)//如果接受到的消息不为0(不为空) |
||||
{ |
||||
memoryStream.Write(buffer, 0, effective);//将接受到的数据写入内存流中 |
||||
DataCallBackReady(client, memoryStream.ToArray()); |
||||
//流复用的方式 不用重新new申请 |
||||
memoryStream.Position = 0; |
||||
memoryStream.SetLength(0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public void LogOut(string Msg) |
||||
{ |
||||
//Console.WriteLine(Msg); |
||||
OnNetLog?.Invoke(Msg); |
||||
} |
||||
|
||||
} |
||||
} |
@ -0,0 +1,246 @@
|
||||
using System; |
||||
using System.IO; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using static HaoYueNet.ClientNetworkNet.Standard2.BaseData; |
||||
|
||||
namespace HaoYueNet.ClientNetworkNet.Standard2.OtherMode |
||||
{ |
||||
public class NetworkHelperCore_SourceMode |
||||
{ |
||||
private Socket client; |
||||
|
||||
////响应倒计时计数最大值 |
||||
//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; |
||||
|
||||
public static string LastConnectIP; |
||||
public static int LastConnectPort; |
||||
public bool bDetailedLog = false; |
||||
|
||||
public bool Init(string IP, int port, bool isHadDetailedLog = true, bool bBindReuseAddress = false, int bBindport = 0) |
||||
{ |
||||
LogOut("==>初始化网络核心"); |
||||
|
||||
bDetailedLog = isHadDetailedLog; |
||||
RevIndex = MaxRevIndexNum; |
||||
SendIndex = MaxSendIndexNum; |
||||
|
||||
client = new Socket(SocketType.Stream, ProtocolType.Tcp); |
||||
if (bBindReuseAddress) |
||||
{ |
||||
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); |
||||
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, Convert.ToInt32(bBindport)); |
||||
client.Bind(ipe); |
||||
} |
||||
LastConnectIP = IP; |
||||
LastConnectPort = port; |
||||
return Connect(IP, port); |
||||
} |
||||
|
||||
bool Connect(string IP, int port) |
||||
{ |
||||
//带回调的 |
||||
try |
||||
{ |
||||
if (bDetailedLog) |
||||
LogOut("连接到远程IP " + IP + ":" + port); |
||||
else |
||||
LogOut("连接到远程服务"); |
||||
|
||||
client.Connect(IP, port); |
||||
Thread thread = new Thread(Recive); |
||||
thread.IsBackground = true; |
||||
thread.Start(client); |
||||
int localport = ((IPEndPoint)client.LocalEndPoint).Port; |
||||
|
||||
if (bDetailedLog) |
||||
LogOut($"连接成功!连接到远程IP->{IP}:{port} | 本地端口->{localport}"); |
||||
else |
||||
LogOut("连接成功!"); |
||||
|
||||
if (bDetailedLog) |
||||
LogOut("开启心跳包检测"); |
||||
|
||||
OnConnected?.Invoke(true); |
||||
return true; |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
if (bDetailedLog) |
||||
LogOut("连接失败:" + ex.ToString()); |
||||
else |
||||
LogOut("连接失败"); |
||||
|
||||
OnConnected?.Invoke(false); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
~NetworkHelperCore_SourceMode() |
||||
{ |
||||
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("发送心跳包"); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
private void SendWithIndex(byte[] data) |
||||
{ |
||||
//增加发送计数 |
||||
SendIndex = MaxSendIndexNum; |
||||
//发送数据 |
||||
client.Send(data); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 供外部调用 发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="data">序列化之后的数据</param> |
||||
public void SendToServer(byte[] data) |
||||
{ |
||||
//LogOut("准备数据 data=> "+data); |
||||
SendToSocket(data); |
||||
} |
||||
|
||||
#region 事件定义 |
||||
public delegate void OnReceiveDataHandler(byte[] data); |
||||
public delegate void OnConnectedHandler(bool IsConnected); |
||||
public delegate void OnCloseHandler(); |
||||
public delegate void OnLogOutHandler(string Msg); |
||||
#endregion |
||||
|
||||
public event OnConnectedHandler OnConnected; |
||||
public event OnReceiveDataHandler OnReceiveData; |
||||
public event OnCloseHandler OnClose; |
||||
/// <summary> |
||||
/// 网络库调试日志输出 |
||||
/// </summary> |
||||
public event OnLogOutHandler OnLogOut; |
||||
|
||||
/// <summary> |
||||
/// 做好处理的连接管理 |
||||
/// </summary> |
||||
private void OnCloseReady() |
||||
{ |
||||
LogOut("关闭连接"); |
||||
//关闭Socket连接 |
||||
client.Close(); |
||||
OnClose?.Invoke(); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 主动关闭连接 |
||||
/// </summary> |
||||
public void CloseConntect() |
||||
{ |
||||
OnCloseReady(); |
||||
} |
||||
|
||||
private void DataCallBackReady(byte[] data) |
||||
{ |
||||
//增加接收计数 |
||||
RevIndex = MaxRevIndexNum; |
||||
OnReceiveData(data); |
||||
} |
||||
|
||||
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流 |
||||
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; |
||||
//断开连接 |
||||
} |
||||
if (effective > 0)//如果接受到的消息不为0(不为空) |
||||
{ |
||||
memoryStream.Write(buffer, 0, effective);//将接受到的数据写入内存流中 |
||||
DataCallBackReady(memoryStream.ToArray()); |
||||
//流复用的方式 不用重新new申请 |
||||
memoryStream.Position = 0; |
||||
memoryStream.SetLength(0); |
||||
} |
||||
} |
||||
} |
||||
|
||||
|
||||
public void LogOut(string Msg) |
||||
{ |
||||
//Console.WriteLine(Msg); |
||||
OnLogOut?.Invoke(Msg); |
||||
} |
||||
|
||||
public Socket GetClientSocket() |
||||
{ |
||||
return client; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<!-- |
||||
https://go.microsoft.com/fwlink/?LinkID=208121. |
||||
--> |
||||
<Project> |
||||
<PropertyGroup> |
||||
<Configuration>Release</Configuration> |
||||
<Platform>Any CPU</Platform> |
||||
<PublishDir>bin\Release\netstandard2.0\publish\</PublishDir> |
||||
<PublishProtocol>FileSystem</PublishProtocol> |
||||
<_TargetId>Folder</_TargetId> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,115 @@
|
||||
using System; |
||||
using System.Net.Sockets; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public static class BaseData |
||||
{ |
||||
/// <summary> |
||||
/// 心跳包数据 |
||||
/// </summary> |
||||
public static byte[] HeartbeatData = new byte[5] { 0x05, 0x00, 0x00, 0x00, 0x00 }; |
||||
|
||||
public static void writeInt(byte[] buf, int offset, int value) |
||||
{ |
||||
buf[offset++] = (byte)(255 & value); |
||||
buf[offset++] = (byte)(255 & value >> 8); |
||||
buf[offset++] = (byte)(255 & value >> 16); |
||||
//buf[offset++] = (byte)(255 & value >>> 24); |
||||
buf[offset++] = (byte)(255 & value >> 24); |
||||
} |
||||
|
||||
public static void writeUInt16(byte[] buf, int offset, int value) |
||||
{ |
||||
buf[offset++] = (byte)(255 & value); |
||||
buf[offset++] = (byte)(255 & value >> 8); |
||||
} |
||||
|
||||
public static class HunterNet_Heartbeat |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs) |
||||
{ |
||||
myreadEventArgs.SetBuffer(HeartbeatData, 0, HeartbeatData.Length); |
||||
} |
||||
} |
||||
|
||||
public static class HunterNet_S2C |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs, UInt16 CmdID, UInt16 Error, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] data = CreatePkgData(CmdID, Error, AddonBytes_Data); |
||||
myreadEventArgs.SetBuffer(data, 0, data.Length); |
||||
} |
||||
|
||||
public static byte[] CreatePkgData(UInt16 CmdID, UInt16 Error, byte[] AddonBytes_Data) |
||||
{ |
||||
//用Buffer.BlockCopy拷贝 |
||||
//包长度 |
||||
int AllLenght = 4 + 2 + 2 + AddonBytes_Data.Length; |
||||
byte[] BufferData = new byte[AllLenght]; |
||||
|
||||
//包长度 |
||||
writeInt(BufferData, 0, AllLenght); |
||||
|
||||
//CMDID |
||||
writeUInt16(BufferData, 4, CmdID); |
||||
|
||||
//Error |
||||
writeUInt16(BufferData, 4 + 2, CmdID); |
||||
|
||||
//DATA |
||||
Buffer.BlockCopy(AddonBytes_Data, 0, BufferData, 4 + 2 + 2, AddonBytes_Data.Length); |
||||
|
||||
return BufferData; |
||||
} |
||||
|
||||
public static void AnalysisPkgData(byte[] srcdata, out UInt16 CmdID, out UInt16 Error, out byte[] data) |
||||
{ |
||||
CmdID = BitConverter.ToUInt16(srcdata, 0); |
||||
Error = BitConverter.ToUInt16(srcdata, 2); |
||||
data = new byte[srcdata.Length - 2 - 2]; |
||||
Array.Copy(srcdata, 4, data, 0, data.Length); |
||||
|
||||
} |
||||
} |
||||
|
||||
public static class HunterNet_C2S |
||||
{ |
||||
public static void SetDataToSocketAsyncEventArgs(SocketAsyncEventArgs myreadEventArgs, UInt16 CmdID, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] data = CreatePkgData(CmdID, AddonBytes_Data); |
||||
myreadEventArgs.SetBuffer(data, 0, data.Length); |
||||
} |
||||
|
||||
public static byte[] CreatePkgData(UInt16 CmdID, byte[] AddonBytes_Data) |
||||
{ |
||||
byte[] AddonBytes_CmdID = BitConverter.GetBytes(CmdID); |
||||
int AllLenght = AddonBytes_CmdID.Length + AddonBytes_Data.Length + 4; |
||||
int LastIndex = 0; |
||||
//包长度 |
||||
byte[] AddonBytes_Lenght = BitConverter.GetBytes(AllLenght); |
||||
|
||||
byte[] BufferData = new byte[AllLenght]; |
||||
//包长度 |
||||
Buffer.BlockCopy(AddonBytes_Lenght, 0, BufferData, LastIndex, AddonBytes_Lenght.Length); |
||||
LastIndex += AddonBytes_Lenght.Length; |
||||
|
||||
//CMDID |
||||
Buffer.BlockCopy(AddonBytes_CmdID, 0, BufferData, LastIndex, AddonBytes_CmdID.Length); |
||||
LastIndex += AddonBytes_CmdID.Length; |
||||
|
||||
//DATA |
||||
Buffer.BlockCopy(AddonBytes_Data, 0, BufferData, LastIndex, AddonBytes_Data.Length); |
||||
LastIndex += AddonBytes_Data.Length; |
||||
return BufferData; |
||||
} |
||||
|
||||
public static void AnalysisPkgData(byte[] srcdata, out UInt16 CmdID, out byte[] data) |
||||
{ |
||||
data = new byte[srcdata.Length - 2]; |
||||
CmdID = BitConverter.ToUInt16(srcdata, 0); |
||||
Array.Copy(srcdata, 2, data, 0, data.Length); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk"> |
||||
<PropertyGroup> |
||||
<TargetFramework>netstandard2.0</TargetFramework> |
||||
</PropertyGroup> |
||||
<PropertyGroup> |
||||
<!--避免 错误 CS8630 无效的 nullable 值: C# 7.3 的“Enable”。请使用语言版本 8.0 或更高版本 --> |
||||
<LangVersion>8.0</LangVersion> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,56 @@
|
||||
using System; |
||||
using System.IO; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public class AsyncUserToken |
||||
{ |
||||
/// <summary> |
||||
/// 客户端IP地址 |
||||
/// </summary> |
||||
public IPAddress IPAddress { get; set; } |
||||
|
||||
/// <summary> |
||||
/// 远程地址 |
||||
/// </summary> |
||||
public EndPoint Remote { get; set; } |
||||
|
||||
/// <summary> |
||||
/// 通信SOKET |
||||
/// </summary> |
||||
public Socket Socket { get; set; } |
||||
|
||||
/// <summary> |
||||
/// 连接时间 |
||||
/// </summary> |
||||
public DateTime ConnectTime { get; set; } |
||||
|
||||
/// <summary> |
||||
/// 所属用户信息 |
||||
/// </summary> |
||||
public object UserInfo { get; set; } |
||||
|
||||
/// <summary> |
||||
/// 数据缓存区 |
||||
/// </summary> |
||||
//public List<byte> Buffer { get; set; } |
||||
|
||||
public MemoryStream memoryStream { get; set; } |
||||
|
||||
public AsyncUserToken() |
||||
{ |
||||
//this.Buffer = new List<byte>(); |
||||
this.memoryStream = new MemoryStream(); |
||||
} |
||||
/// <summary> |
||||
/// 响应倒计时计数 |
||||
/// </summary> |
||||
public int RevIndex { get; set; } = 0; |
||||
/// <summary> |
||||
/// 发送倒计时计数 |
||||
/// </summary> |
||||
public int SendIndex { get; set; } = 0; |
||||
} |
||||
} |
@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic; |
||||
using System.Net.Sockets; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
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<int> 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<int>(); |
||||
} |
||||
|
||||
// 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 |
||||
// |
||||
// <returns>true if the buffer was successfully set, else false</returns> |
||||
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); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,43 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
|
||||
public class MemoryStreamPool |
||||
{ |
||||
Stack<MemoryStream> m_pool; |
||||
|
||||
public MemoryStreamPool(int capacity) |
||||
{ |
||||
m_pool = new Stack<MemoryStream>(capacity); |
||||
} |
||||
|
||||
public void Push(MemoryStream item) |
||||
{ |
||||
if (item == null) { throw new ArgumentNullException("Items added to a MemoryStream cannot be null"); } |
||||
lock (m_pool) |
||||
{ |
||||
m_pool.Push(item); |
||||
} |
||||
} |
||||
public MemoryStream Pop() |
||||
{ |
||||
lock (m_pool) |
||||
{ |
||||
return m_pool.Pop(); |
||||
} |
||||
} |
||||
|
||||
public int Count |
||||
{ |
||||
get { return m_pool.Count; } |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
m_pool.Clear(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,47 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Net.Sockets; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
|
||||
public class SocketEventPool |
||||
{ |
||||
Stack<SocketAsyncEventArgs> m_pool; |
||||
|
||||
public SocketEventPool(int capacity) |
||||
{ |
||||
m_pool = new Stack<SocketAsyncEventArgs>(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(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,679 @@
|
||||
//using HunterProtobufCore; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using static HaoYueNet.ServerNetwork.Standard2.BaseData; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public class TcpSaeaServer_SourceMode |
||||
{ |
||||
#region 定义属性 |
||||
protected int MaxRevIndexNum = 50;//响应倒计时计数最大值 |
||||
protected int MaxSendIndexNum = 3;//发送倒计时计数最大值 |
||||
protected static int TimerInterval = 3000;//计时器间隔 |
||||
//protected System.Timers.Timer _heartTimer;//心跳包计数器 |
||||
public int m_maxConnectNum; //最大连接数 |
||||
public int m_revBufferSize; //最大接收字节数 |
||||
protected BufferManager m_bufferManager; |
||||
protected const int opsToAlloc = 2; |
||||
Socket listenSocket; //监听Socket |
||||
protected SocketEventPool m_Receivepool; |
||||
protected SocketEventPool m_Sendpool; |
||||
protected TokenMsgPool_SourceMode msg_pool; |
||||
protected int m_clientCount; //连接的客户端数量 |
||||
protected Semaphore m_maxNumberAcceptedClients;//信号量 |
||||
protected Dictionary<Socket, AsyncUserToken> _DictSocketAsyncUserToken = new Dictionary<Socket, AsyncUserToken>(); |
||||
List<AsyncUserToken> m_clients; //客户端列表 |
||||
public List<AsyncUserToken> ClientList { private set { m_clients = value; } get { return m_clients; } } //获取客户端列表 |
||||
#endregion |
||||
|
||||
#region 定义委托 |
||||
/// <summary> |
||||
/// 客户端连接数量变化时触发 |
||||
/// </summary> |
||||
/// <param name="num">当前增加客户的个数(用户退出时为负数,增加时为正数,一般为1)</param> |
||||
/// <param name="token">增加用户的信息</param> |
||||
public delegate void OnClientNumberChangeHandler(int num, AsyncUserToken token); |
||||
/// <summary> |
||||
/// 接收到客户端的数据 |
||||
/// </summary> |
||||
/// <param name="token">客户端</param> |
||||
/// <param name="buff">客户端数据</param> |
||||
public delegate void OnReceiveDataHandler(AsyncUserToken sk, byte[] data); |
||||
/// <summary> |
||||
/// 断开连接 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
public delegate void OnDisconnectHandler(AsyncUserToken sk); |
||||
/// <summary> |
||||
/// 日志 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
public delegate void OnNetLogHandler(string msg); |
||||
#endregion |
||||
|
||||
#region 定义事件 |
||||
/// <summary> |
||||
/// 客户端连接数量变化事件 |
||||
/// </summary> |
||||
public event OnClientNumberChangeHandler OnClientNumberChange; |
||||
/// <summary> |
||||
/// 接收到客户端的数据事件 |
||||
/// </summary> |
||||
public event OnReceiveDataHandler OnReceive; |
||||
/// <summary> |
||||
/// 接收到客户端的断开连接 |
||||
/// </summary> |
||||
public event OnDisconnectHandler OnDisconnected; |
||||
/// <summary> |
||||
/// 网络库内部输出 |
||||
/// </summary> |
||||
public event OnNetLogHandler OnNetLog; |
||||
#endregion |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="numConnections">最大连接数</param> |
||||
/// <param name="receiveBufferSize">缓存区大小</param> |
||||
public TcpSaeaServer_SourceMode(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_Receivepool = new SocketEventPool(numConnections); |
||||
m_Sendpool = new SocketEventPool(numConnections); |
||||
|
||||
msg_pool = new TokenMsgPool_SourceMode(numConnections); |
||||
|
||||
m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); |
||||
} |
||||
|
||||
#region Client操作 |
||||
/// <summary> |
||||
/// 初始化 |
||||
/// </summary> |
||||
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<AsyncUserToken>(); |
||||
// preallocate pool of SocketAsyncEventArgs objects |
||||
SocketAsyncEventArgs readWriteEventArg; |
||||
|
||||
for (int i = 0; i < m_maxConnectNum; i++) |
||||
{ |
||||
readWriteEventArg = new SocketAsyncEventArgs(); |
||||
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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_Receivepool.Push(readWriteEventArg); |
||||
} |
||||
|
||||
for (int i = 0; i < m_maxConnectNum; i++) |
||||
{ |
||||
readWriteEventArg = new SocketAsyncEventArgs(); |
||||
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); |
||||
readWriteEventArg.UserToken = new AsyncUserToken(); |
||||
|
||||
//发送是否需要如此设置 TODO |
||||
m_bufferManager.SetBuffer(readWriteEventArg); |
||||
|
||||
m_Sendpool.Push(readWriteEventArg); |
||||
} |
||||
OutNetLog("初始化完毕"); |
||||
} |
||||
/// <summary> |
||||
/// 启动服务 |
||||
/// </summary> |
||||
/// <param name="localEndPoint"></param> |
||||
/// <param name="bReuseAddress">是否端口重用</param> |
||||
/// <returns></returns> |
||||
public bool Start(IPEndPoint localEndPoint, bool bReuseAddress = false) |
||||
{ |
||||
try |
||||
{ |
||||
ClearUserToken(); |
||||
listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
||||
|
||||
if (bReuseAddress) |
||||
{ |
||||
listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); |
||||
} |
||||
|
||||
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); |
||||
|
||||
OutNetLog("监听:" + listenSocket.LocalEndPoint.ToString()); |
||||
|
||||
//_heartTimer = new System.Timers.Timer(); |
||||
//_heartTimer.Interval = TimerInterval; |
||||
//_heartTimer.Elapsed += CheckUpdatetimer_Elapsed; |
||||
//_heartTimer.AutoReset = true; |
||||
//_heartTimer.Enabled = true; |
||||
//OutNetLog("开启定时心跳包"); |
||||
|
||||
return true; |
||||
} |
||||
catch (Exception) |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 停止服务 |
||||
/// </summary> |
||||
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; |
||||
ClearUserToken(); |
||||
|
||||
if (OnClientNumberChange != null) |
||||
OnClientNumberChange(-c_count, null); |
||||
} |
||||
public void CloseClient(AsyncUserToken token) |
||||
{ |
||||
try {token.Socket.Shutdown(SocketShutdown.Both);} |
||||
catch (Exception) { } |
||||
} |
||||
/// <summary> |
||||
/// 关闭客户端连接 |
||||
/// </summary> |
||||
/// <param name="e"></param> |
||||
void CloseClientSocket(SocketAsyncEventArgs e) |
||||
{ |
||||
AsyncUserToken token = e.UserToken as AsyncUserToken; |
||||
CloseReady(token); |
||||
// 释放SocketAsyncEventArg,以便其他客户端可以重用它们 |
||||
ReleaseSocketAsyncEventArgs(e); |
||||
} |
||||
void CloseReady(AsyncUserToken token) |
||||
{ |
||||
OnDisconnected?.Invoke(token); |
||||
RemoveUserToken(token); |
||||
//如果有事件,则调用事件,发送客户端数量变化通知 |
||||
OnClientNumberChange?.Invoke(-1, token); |
||||
// 关闭与客户端关联的套接字 |
||||
try { token.Socket.Shutdown(SocketShutdown.Send); } catch (Exception) { } |
||||
token.Socket.Close(); |
||||
// 递减计数器以跟踪连接到服务器的客户端总数 |
||||
Interlocked.Decrement(ref m_clientCount); |
||||
m_maxNumberAcceptedClients.Release(); |
||||
} |
||||
#endregion |
||||
|
||||
#region Token管理 |
||||
public AsyncUserToken GetAsyncUserTokenForSocket(Socket sk) |
||||
{ |
||||
return _DictSocketAsyncUserToken.ContainsKey(sk) ? _DictSocketAsyncUserToken[sk] : null; |
||||
} |
||||
void AddUserToken(AsyncUserToken userToken) |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Add(userToken); |
||||
_DictSocketAsyncUserToken.Add(userToken.Socket, userToken); |
||||
} |
||||
} |
||||
void RemoveUserToken(AsyncUserToken userToken) |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Remove(userToken); |
||||
_DictSocketAsyncUserToken.Remove(userToken.Socket); |
||||
} |
||||
} |
||||
void ClearUserToken() |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Clear(); |
||||
_DictSocketAsyncUserToken.Clear(); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 回收SocketAsyncEventArgs |
||||
/// </summary> |
||||
/// <param name="saea"></param> |
||||
/// <exception cref="ArgumentException"></exception> |
||||
void ReleaseSocketAsyncEventArgs(SocketAsyncEventArgs saea) |
||||
{ |
||||
//saea.UserToken = null;//TODO |
||||
//saea.SetBuffer(null, 0, 0); |
||||
//saea.Dispose(); |
||||
//↑ 这里不要自作主张去清东西,否则回收回去不可用 |
||||
|
||||
switch (saea.LastOperation) |
||||
{ |
||||
case SocketAsyncOperation.Receive: |
||||
m_Receivepool.Push(saea); |
||||
break; |
||||
case SocketAsyncOperation.Send: |
||||
m_Sendpool.Push(saea); |
||||
break; |
||||
default: |
||||
throw new ArgumentException("ReleaseSocketAsyncEventArgs > The last operation completed on the socket was not a receive or send"); |
||||
} |
||||
|
||||
} |
||||
#endregion |
||||
|
||||
#region 监听IOCP循环 |
||||
/// <summary> |
||||
/// 开始接受客户端的连接请求的操作 |
||||
/// </summary> |
||||
/// <param name="acceptEventArg">在服务器的侦听套接字上发出接受操作时要使用的上下文对象</param> |
||||
public void StartAccept(SocketAsyncEventArgs acceptEventArg) |
||||
{ |
||||
if (acceptEventArg == null) |
||||
{ |
||||
acceptEventArg = new SocketAsyncEventArgs(); |
||||
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 此方法是与Socket关联的回调方法。AcceptAsync操作,并在接受操作完成时调用 |
||||
/// </summary> |
||||
/// <param name="sender"></param> |
||||
/// <param name="e"></param> |
||||
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) |
||||
{ |
||||
ProcessAccept(e); |
||||
} |
||||
private void ProcessAccept(SocketAsyncEventArgs e) |
||||
{ |
||||
try |
||||
{ |
||||
Interlocked.Increment(ref m_clientCount); |
||||
//确保监听结束时,有连接才抛给数据接收 |
||||
if (e.AcceptSocket.RemoteEndPoint != null) |
||||
{ |
||||
// Get the socket for the accepted client connection and put it into the |
||||
//ReadEventArg object user token |
||||
SocketAsyncEventArgs readEventArgs = m_Receivepool.Pop(); |
||||
//TODO readEventArgs.UserToken这里的 UserToken 有可能是空 |
||||
AsyncUserToken userToken; |
||||
if (readEventArgs.UserToken == null) |
||||
readEventArgs.UserToken = new 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; |
||||
|
||||
AddUserToken(userToken); |
||||
|
||||
OnClientNumberChange?.Invoke(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); |
||||
} |
||||
#endregion |
||||
|
||||
#region 收发IOCP循环 |
||||
/// <summary>当异步接收操作完成时,会调用此方法。 |
||||
/// 如果远程主机关闭了连接,则套接字关闭。 |
||||
/// 如果接收到数据,则将数据回显到客户端。 |
||||
/// </summary> |
||||
/// <param name="e"></param> |
||||
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) |
||||
lock(token.memoryStream) |
||||
{ |
||||
//token.Buffer.AddRange(data); |
||||
token.memoryStream.Write(e.Buffer, e.Offset, e.BytesTransferred); |
||||
do |
||||
{ |
||||
|
||||
//DataCallBackReady(token, data); |
||||
////从数据池中移除这组数据 |
||||
//lock (token.Buffer) |
||||
//{ |
||||
// token.Buffer.Clear(); |
||||
//} |
||||
|
||||
DataCallBackReady(token, token.memoryStream.ToArray()); |
||||
//流复用的方式 不用重新new申请 |
||||
token.memoryStream.Position = 0; |
||||
token.memoryStream.SetLength(0); |
||||
|
||||
//这里API处理完后,并没有返回结果,当然结果是要返回的,却不是在这里, 这里的代码只管接收. |
||||
//若要返回结果,可在API处理中调用此类对象的SendMessage方法,统一打包发送.不要被微软的示例给迷惑了. |
||||
} while (token.memoryStream.Length > 0); |
||||
//} 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); |
||||
} |
||||
} |
||||
private void ProcessSend(SocketAsyncEventArgs e) |
||||
{ |
||||
if (e.SocketError == SocketError.Success) |
||||
{ |
||||
//TODO |
||||
} |
||||
else |
||||
{ |
||||
CloseClientSocket(e); |
||||
return; |
||||
} |
||||
ReleaseSocketAsyncEventArgs(e); |
||||
SendForMsgPool(); |
||||
} |
||||
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"); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
#region 发送 |
||||
int sendrun = 0; |
||||
/// <summary> |
||||
/// 对外暴露的发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="data">序列化之后的数据</param> |
||||
public void SendToSocket(Socket sk, byte[] data) |
||||
{ |
||||
AsyncUserToken token = GetAsyncUserTokenForSocket(sk); |
||||
SendWithIndex(token, data); |
||||
} |
||||
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_SourceMode msg = msg_pool.Dequeue(); |
||||
//OutNetLog("从信息池取出发送"); |
||||
//是心跳包 |
||||
if (msg.bHeartbeat) |
||||
{ |
||||
SendHeartbeatMessage(msg.token); |
||||
} |
||||
else |
||||
{ |
||||
SendMessage(msg.token,msg.data); |
||||
} |
||||
msg = null; |
||||
} |
||||
catch |
||||
{ |
||||
OutNetLog("==============================================>"); |
||||
} |
||||
} |
||||
sendrun--; |
||||
OutNetLog("!!!!!!!!!!!!!!!!!!!!!!!!!!"); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
OutNetLog(ex.ToString()); |
||||
} |
||||
|
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送心跳包 |
||||
/// </summary> |
||||
/// <param name="token"></param> |
||||
void SendHeartbeatMessage(AsyncUserToken token) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
if (m_Sendpool.Count > 0) |
||||
{ |
||||
SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop(); |
||||
myreadEventArgs.UserToken = token; |
||||
myreadEventArgs.AcceptSocket = token.Socket; |
||||
//直接写入SocketAsyncEventArgs的Buff |
||||
HunterNet_Heartbeat.SetDataToSocketAsyncEventArgs(myreadEventArgs); |
||||
|
||||
//若不需要等待 |
||||
if (!token.Socket.SendAsync(myreadEventArgs)) |
||||
{ |
||||
m_Sendpool.Push(myreadEventArgs); |
||||
} |
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
//先压入队列,等待m_Sendpool回收 |
||||
msg_pool.Enqueue(new TokenWithMsg_SourceMode() { token = token, bHeartbeat = true }); |
||||
//OutNetLog("!!!!压入消息发送队列MSG_Pool"); |
||||
return; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
OutNetLog(e.ToString()); |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
void SendWithIndex(AsyncUserToken token,byte[] data) |
||||
{ |
||||
try |
||||
{ |
||||
//发送数据 |
||||
SendMessage(token, data); |
||||
token.SendIndex = MaxSendIndexNum; |
||||
} |
||||
catch |
||||
{ |
||||
CloseReady(token); |
||||
} |
||||
} |
||||
|
||||
void SendMessage(AsyncUserToken token, byte[] data) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
if (m_Sendpool.Count > 0) |
||||
{ |
||||
SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop(); |
||||
myreadEventArgs.UserToken = token; |
||||
myreadEventArgs.AcceptSocket = token.Socket; |
||||
myreadEventArgs.SetBuffer(data, 0, data.Length); //将数据放置进去. |
||||
|
||||
//若不需要等待 |
||||
if (!token.Socket.SendAsync(myreadEventArgs)) |
||||
{ |
||||
m_Sendpool.Push(myreadEventArgs); |
||||
} |
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
//先压入队列,等待m_Sendpool回收 |
||||
msg_pool.Enqueue(new TokenWithMsg_SourceMode() { token = token, data = data ,bHeartbeat = false}); |
||||
//OutNetLog("!!!!压入消息发送队列MSG_Pool"); |
||||
return; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
OutNetLog(e.ToString()); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
#region 处理前预备 |
||||
private void DataCallBackReady(AsyncUserToken sk, byte[] data) |
||||
{ |
||||
//增加接收计数 |
||||
sk.RevIndex = MaxRevIndexNum; |
||||
|
||||
try |
||||
{ |
||||
//将数据包交给后台处理,这里你也可以新开个线程来处理.加快速度. |
||||
OnReceive?.Invoke(sk, data); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
OutNetLog("数据解析错误"); |
||||
} |
||||
} |
||||
private void OutNetLog(string msg) |
||||
{ |
||||
OnNetLog?.Invoke(msg); |
||||
} |
||||
#endregion |
||||
|
||||
#region 心跳包 |
||||
/* |
||||
/// <summary> |
||||
/// 发送心跳包 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
/// |
||||
private void SendHeartbeatWithIndex(AsyncUserToken token) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
//OutNetLog(DateTime.Now.ToString() + "发送心跳包"); |
||||
token.SendIndex = MaxSendIndexNum; |
||||
SendHeartbeatMessage(token); |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
CloseReady(token); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 心跳包时钟事件 |
||||
/// </summary> |
||||
/// <param name="sender"></param> |
||||
/// <param name="e"></param> |
||||
private void CheckUpdatetimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) |
||||
{ |
||||
for (int i = 0; i < m_clients.Count(); i++) |
||||
{ |
||||
//接收服务器数据计数 |
||||
m_clients[i].RevIndex--; |
||||
if (m_clients[i].RevIndex <= 0) |
||||
{ |
||||
//判定掉线 |
||||
CloseReady(m_clients[i]); |
||||
return; |
||||
} |
||||
|
||||
//发送计数 |
||||
m_clients[i].SendIndex--; |
||||
if (m_clients[i].SendIndex <= 0)//需要发送心跳包了 |
||||
{ |
||||
//重置倒计时计数 |
||||
m_clients[i].SendIndex = MaxSendIndexNum; |
||||
SendHeartbeatWithIndex(m_clients[i]); |
||||
} |
||||
} |
||||
} |
||||
*/ |
||||
#endregion |
||||
} |
||||
} |
@ -0,0 +1,57 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Net.Sockets; |
||||
using System.Text; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public class TokenWithMsg_SourceMode |
||||
{ |
||||
public AsyncUserToken token; |
||||
public byte[] data; |
||||
public bool bHeartbeat; |
||||
} |
||||
|
||||
public class TokenMsgPool_SourceMode |
||||
{ |
||||
//Stack<TokenWithMsg> msg_pool; |
||||
Queue<TokenWithMsg_SourceMode> msg_pool; |
||||
|
||||
public TokenMsgPool_SourceMode(int capacity) |
||||
{ |
||||
msg_pool = new Queue<TokenWithMsg_SourceMode>(capacity); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 向 Queue 的末尾添加一个对象。 |
||||
/// </summary> |
||||
/// <param name="item"></param> |
||||
public void Enqueue(TokenWithMsg_SourceMode item) |
||||
{ |
||||
lock (msg_pool) |
||||
{ |
||||
msg_pool.Enqueue(item); |
||||
} |
||||
} |
||||
|
||||
//移除并返回在 Queue 的开头的对象。 |
||||
public TokenWithMsg_SourceMode Dequeue() |
||||
{ |
||||
lock (msg_pool) |
||||
{ |
||||
return msg_pool.Dequeue(); |
||||
} |
||||
} |
||||
|
||||
public int Count |
||||
{ |
||||
get { return msg_pool.Count; } |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
msg_pool.Clear(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,742 @@
|
||||
//using HunterProtobufCore; |
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.IO; |
||||
using System.Net; |
||||
using System.Net.Sockets; |
||||
using System.Threading; |
||||
using static HaoYueNet.ServerNetwork.Standard2.BaseData; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public class TcpSaeaServer |
||||
{ |
||||
#region 定义属性 |
||||
protected int MaxRevIndexNum = 50;//响应倒计时计数最大值 |
||||
protected int MaxSendIndexNum = 3;//发送倒计时计数最大值 |
||||
protected static int TimerInterval = 3000;//计时器间隔 |
||||
protected System.Timers.Timer _heartTimer;//心跳包计数器 |
||||
public int m_maxConnectNum; //最大连接数 |
||||
public int m_revBufferSize; //最大接收字节数 |
||||
protected BufferManager m_bufferManager; |
||||
protected const int opsToAlloc = 2; |
||||
Socket listenSocket; //监听Socket |
||||
protected SocketEventPool m_Receivepool; |
||||
protected SocketEventPool m_Sendpool; |
||||
protected TokenMsgPool msg_pool; |
||||
protected int m_clientCount; //连接的客户端数量 |
||||
protected Semaphore m_maxNumberAcceptedClients;//信号量 |
||||
protected Dictionary<Socket, AsyncUserToken> _DictSocketAsyncUserToken = new Dictionary<Socket, AsyncUserToken>(); |
||||
List<AsyncUserToken> m_clients; //客户端列表 |
||||
public List<AsyncUserToken> ClientList { private set { m_clients = value; } get { return m_clients; } } //获取客户端列表 |
||||
#endregion |
||||
|
||||
#region 定义委托 |
||||
/// <summary> |
||||
/// 客户端连接数量变化时触发 |
||||
/// </summary> |
||||
/// <param name="num">当前增加客户的个数(用户退出时为负数,增加时为正数,一般为1)</param> |
||||
/// <param name="token">增加用户的信息</param> |
||||
public delegate void OnClientNumberChangeHandler(int num, AsyncUserToken token); |
||||
/// <summary> |
||||
/// 接收到客户端的数据 |
||||
/// </summary> |
||||
/// <param name="token">客户端</param> |
||||
/// <param name="buff">客户端数据</param> |
||||
public delegate void OnReceiveDataHandler(AsyncUserToken sk, int CMDID, byte[] data); |
||||
/// <summary> |
||||
/// 断开连接 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
public delegate void OnDisconnectHandler(AsyncUserToken sk); |
||||
/// <summary> |
||||
/// 日志 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
public delegate void OnNetLogHandler(string msg); |
||||
#endregion |
||||
|
||||
#region 定义事件 |
||||
/// <summary> |
||||
/// 客户端连接数量变化事件 |
||||
/// </summary> |
||||
public event OnClientNumberChangeHandler OnClientNumberChange; |
||||
/// <summary> |
||||
/// 接收到客户端的数据事件 |
||||
/// </summary> |
||||
public event OnReceiveDataHandler OnReceive; |
||||
/// <summary> |
||||
/// 接收到客户端的断开连接 |
||||
/// </summary> |
||||
public event OnDisconnectHandler OnDisconnected; |
||||
/// <summary> |
||||
/// 网络库内部输出 |
||||
/// </summary> |
||||
public event OnNetLogHandler OnNetLog; |
||||
#endregion |
||||
|
||||
/// <summary> |
||||
/// 构造函数 |
||||
/// </summary> |
||||
/// <param name="numConnections">最大连接数</param> |
||||
/// <param name="receiveBufferSize">缓存区大小</param> |
||||
public TcpSaeaServer(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_Receivepool = new SocketEventPool(numConnections); |
||||
m_Sendpool = new SocketEventPool(numConnections); |
||||
|
||||
msg_pool = new TokenMsgPool(numConnections); |
||||
|
||||
m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections); |
||||
} |
||||
|
||||
#region Client操作 |
||||
/// <summary> |
||||
/// 初始化 |
||||
/// </summary> |
||||
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<AsyncUserToken>(); |
||||
// preallocate pool of SocketAsyncEventArgs objects |
||||
SocketAsyncEventArgs readWriteEventArg; |
||||
|
||||
for (int i = 0; i < m_maxConnectNum; i++) |
||||
{ |
||||
readWriteEventArg = new SocketAsyncEventArgs(); |
||||
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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_Receivepool.Push(readWriteEventArg); |
||||
} |
||||
|
||||
for (int i = 0; i < m_maxConnectNum; i++) |
||||
{ |
||||
readWriteEventArg = new SocketAsyncEventArgs(); |
||||
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed); |
||||
readWriteEventArg.UserToken = new AsyncUserToken(); |
||||
|
||||
//发送是否需要如此设置 TODO |
||||
m_bufferManager.SetBuffer(readWriteEventArg); |
||||
|
||||
m_Sendpool.Push(readWriteEventArg); |
||||
} |
||||
OutNetLog("初始化完毕"); |
||||
} |
||||
/// <summary> |
||||
/// 启动服务 |
||||
/// </summary> |
||||
/// <param name="localEndPoint"></param> |
||||
/// <param name="bReuseAddress">是否端口重用</param> |
||||
/// <returns></returns> |
||||
public bool Start(IPEndPoint localEndPoint, bool bReuseAddress = false) |
||||
{ |
||||
try |
||||
{ |
||||
ClearUserToken(); |
||||
listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); |
||||
|
||||
if (bReuseAddress) |
||||
{ |
||||
listenSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); |
||||
} |
||||
|
||||
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); |
||||
|
||||
OutNetLog("监听:" + listenSocket.LocalEndPoint.ToString()); |
||||
|
||||
_heartTimer = new System.Timers.Timer(); |
||||
_heartTimer.Interval = TimerInterval; |
||||
_heartTimer.Elapsed += CheckUpdatetimer_Elapsed; |
||||
_heartTimer.AutoReset = true; |
||||
_heartTimer.Enabled = true; |
||||
OutNetLog("开启定时心跳包"); |
||||
|
||||
return true; |
||||
} |
||||
catch (Exception) |
||||
{ |
||||
return false; |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 停止服务 |
||||
/// </summary> |
||||
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; |
||||
ClearUserToken(); |
||||
|
||||
if (OnClientNumberChange != null) |
||||
OnClientNumberChange(-c_count, null); |
||||
} |
||||
public void CloseClient(AsyncUserToken token) |
||||
{ |
||||
try {token.Socket.Shutdown(SocketShutdown.Both);} |
||||
catch (Exception) { } |
||||
} |
||||
/// <summary> |
||||
/// 关闭客户端连接 |
||||
/// </summary> |
||||
/// <param name="e"></param> |
||||
void CloseClientSocket(SocketAsyncEventArgs e) |
||||
{ |
||||
AsyncUserToken token = e.UserToken as AsyncUserToken; |
||||
CloseReady(token); |
||||
// 释放SocketAsyncEventArg,以便其他客户端可以重用它们 |
||||
ReleaseSocketAsyncEventArgs(e); |
||||
} |
||||
void CloseReady(AsyncUserToken token) |
||||
{ |
||||
OnDisconnected?.Invoke(token); |
||||
RemoveUserToken(token); |
||||
//如果有事件,则调用事件,发送客户端数量变化通知 |
||||
OnClientNumberChange?.Invoke(-1, token); |
||||
// 关闭与客户端关联的套接字 |
||||
try { token.Socket.Shutdown(SocketShutdown.Send); } catch (Exception) { } |
||||
token.Socket.Close(); |
||||
// 递减计数器以跟踪连接到服务器的客户端总数 |
||||
Interlocked.Decrement(ref m_clientCount); |
||||
m_maxNumberAcceptedClients.Release(); |
||||
} |
||||
#endregion |
||||
|
||||
#region Token管理 |
||||
public AsyncUserToken GetAsyncUserTokenForSocket(Socket sk) |
||||
{ |
||||
return _DictSocketAsyncUserToken.ContainsKey(sk) ? _DictSocketAsyncUserToken[sk] : null; |
||||
} |
||||
void AddUserToken(AsyncUserToken userToken) |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Add(userToken); |
||||
_DictSocketAsyncUserToken.Add(userToken.Socket, userToken); |
||||
} |
||||
} |
||||
void RemoveUserToken(AsyncUserToken userToken) |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Remove(userToken); |
||||
_DictSocketAsyncUserToken.Remove(userToken.Socket); |
||||
} |
||||
} |
||||
void ClearUserToken() |
||||
{ |
||||
lock (_DictSocketAsyncUserToken) |
||||
{ |
||||
m_clients.Clear(); |
||||
_DictSocketAsyncUserToken.Clear(); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 回收SocketAsyncEventArgs |
||||
/// </summary> |
||||
/// <param name="saea"></param> |
||||
/// <exception cref="ArgumentException"></exception> |
||||
void ReleaseSocketAsyncEventArgs(SocketAsyncEventArgs saea) |
||||
{ |
||||
//saea.UserToken = null;//TODO |
||||
//saea.SetBuffer(null, 0, 0); |
||||
//saea.Dispose(); |
||||
//↑ 这里不要自作主张去清东西,否则回收回去不可用 |
||||
|
||||
switch (saea.LastOperation) |
||||
{ |
||||
case SocketAsyncOperation.Receive: |
||||
m_Receivepool.Push(saea); |
||||
break; |
||||
case SocketAsyncOperation.Send: |
||||
m_Sendpool.Push(saea); |
||||
break; |
||||
default: |
||||
throw new ArgumentException("ReleaseSocketAsyncEventArgs > The last operation completed on the socket was not a receive or send"); |
||||
} |
||||
|
||||
} |
||||
#endregion |
||||
|
||||
#region 监听IOCP循环 |
||||
/// <summary> |
||||
/// 开始接受客户端的连接请求的操作 |
||||
/// </summary> |
||||
/// <param name="acceptEventArg">在服务器的侦听套接字上发出接受操作时要使用的上下文对象</param> |
||||
public void StartAccept(SocketAsyncEventArgs acceptEventArg) |
||||
{ |
||||
if (acceptEventArg == null) |
||||
{ |
||||
acceptEventArg = new SocketAsyncEventArgs(); |
||||
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 此方法是与Socket关联的回调方法。AcceptAsync操作,并在接受操作完成时调用 |
||||
/// </summary> |
||||
/// <param name="sender"></param> |
||||
/// <param name="e"></param> |
||||
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e) |
||||
{ |
||||
ProcessAccept(e); |
||||
} |
||||
private void ProcessAccept(SocketAsyncEventArgs e) |
||||
{ |
||||
try |
||||
{ |
||||
Interlocked.Increment(ref m_clientCount); |
||||
|
||||
//确保监听结束时,有连接才抛给数据接收 |
||||
if (e.AcceptSocket.RemoteEndPoint != null) |
||||
{ |
||||
// Get the socket for the accepted client connection and put it into the |
||||
//ReadEventArg object user token |
||||
SocketAsyncEventArgs readEventArgs = m_Receivepool.Pop(); |
||||
//TODO readEventArgs.UserToken这里的 UserToken 有可能是空 |
||||
AsyncUserToken userToken; |
||||
if (readEventArgs.UserToken == null) |
||||
readEventArgs.UserToken = new 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; |
||||
|
||||
AddUserToken(userToken); |
||||
|
||||
OnClientNumberChange?.Invoke(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); |
||||
} |
||||
#endregion |
||||
|
||||
#region 收发IOCP循环 |
||||
/// <summary>当异步接收操作完成时,会调用此方法。 |
||||
/// 如果远程主机关闭了连接,则套接字关闭。 |
||||
/// 如果接收到数据,则将数据回显到客户端。 |
||||
/// </summary> |
||||
/// <param name="e"></param> |
||||
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) |
||||
lock(token.memoryStream) |
||||
{ |
||||
//token.Buffer.AddRange(data); |
||||
token.memoryStream.Write(e.Buffer, e.Offset, e.BytesTransferred); |
||||
do |
||||
{ |
||||
//如果包头不完整 |
||||
//if (token.Buffer.Count < 4) |
||||
if (token.memoryStream.Length < 4) |
||||
break; |
||||
|
||||
////判断包的长度 |
||||
//byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray(); |
||||
//int packageLen = BitConverter.ToInt32(lenBytes, 0) - 4; |
||||
//if (packageLen > token.Buffer.Count - 4) |
||||
//{ //长度不够时,退出循环,让程序继续接收 |
||||
// break; |
||||
//} |
||||
|
||||
long FristBeginPos = token.memoryStream.Position; |
||||
byte[] lenBytes = new byte[4]; |
||||
token.memoryStream.Seek(0, SeekOrigin.Begin); |
||||
token.memoryStream.Read(lenBytes, 0, 4); |
||||
int packageLen = BitConverter.ToInt32(lenBytes, 0) - 4; |
||||
if (packageLen > token.memoryStream.Length - 4) |
||||
{ |
||||
token.memoryStream.Seek(FristBeginPos, SeekOrigin.Begin); |
||||
//长度不够时,退出循环,让程序继续接收 |
||||
break; |
||||
} |
||||
|
||||
////包够长时,则提取出来,交给后面的程序去处理 |
||||
//byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray(); |
||||
|
||||
byte[] rev = new byte[packageLen]; |
||||
token.memoryStream.Seek(4, SeekOrigin.Begin); |
||||
token.memoryStream.Read(rev, 0, packageLen); |
||||
|
||||
//从数据池中移除这组数据 |
||||
//lock (token.Buffer) |
||||
//{ |
||||
// token.Buffer.RemoveRange(0, packageLen + 4); |
||||
//} |
||||
|
||||
token.memoryStream.Seek(FristBeginPos, SeekOrigin.Begin); |
||||
//从数据池中移除这组数据 |
||||
lock (token.memoryStream) |
||||
{ |
||||
//token.memoryStream.Position = 0; |
||||
//token.memoryStream.SetLength(0); |
||||
int numberOfBytesToRemove = packageLen + 4; |
||||
byte[] buf = token.memoryStream.GetBuffer(); |
||||
Buffer.BlockCopy(buf, numberOfBytesToRemove, buf, 0, (int)token.memoryStream.Length - numberOfBytesToRemove); |
||||
token.memoryStream.SetLength(token.memoryStream.Length - numberOfBytesToRemove); |
||||
} |
||||
|
||||
DataCallBackReady(token, rev); |
||||
|
||||
//这里API处理完后,并没有返回结果,当然结果是要返回的,却不是在这里, 这里的代码只管接收. |
||||
//若要返回结果,可在API处理中调用此类对象的SendMessage方法,统一打包发送.不要被微软的示例给迷惑了. |
||||
//} while (token.Buffer.Count > 4); |
||||
} while (token.memoryStream.Length > 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); |
||||
} |
||||
} |
||||
private void ProcessSend(SocketAsyncEventArgs e) |
||||
{ |
||||
if (e.SocketError == SocketError.Success) |
||||
{ |
||||
//TODO |
||||
} |
||||
else |
||||
{ |
||||
CloseClientSocket(e); |
||||
return; |
||||
} |
||||
ReleaseSocketAsyncEventArgs(e); |
||||
SendForMsgPool(); |
||||
} |
||||
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"); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
#region 发送 |
||||
int sendrun = 0; |
||||
/// <summary> |
||||
/// 对外暴露的发送消息 |
||||
/// </summary> |
||||
/// <param name="CMDID"></param> |
||||
/// <param name="data">序列化之后的数据</param> |
||||
public void SendToSocket(Socket sk, int CMDID, int ERRCODE, byte[] data) |
||||
{ |
||||
AsyncUserToken token = GetAsyncUserTokenForSocket(sk); |
||||
/*HunterNet_S2C _s2cdata = new HunterNet_S2C(); |
||||
_s2cdata.HunterNetCoreCmdID = CMDID; |
||||
_s2cdata.HunterNetCoreData = ByteString.CopyFrom(data); |
||||
_s2cdata.HunterNetCoreERRORCode = ERRCODE; |
||||
byte[] _finaldata = Serizlize(_s2cdata);*/ |
||||
|
||||
//byte[] _finaldata = HunterNet_S2C.CreatePkgData((ushort)CMDID, (ushort)ERRCODE, data); |
||||
|
||||
SendWithIndex(token, (ushort)CMDID, (ushort)ERRCODE, data); |
||||
} |
||||
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(); |
||||
//OutNetLog("从信息池取出发送"); |
||||
//是心跳包 |
||||
if (msg.bHeartbeat) |
||||
{ |
||||
SendHeartbeatMessage(msg.token); |
||||
} |
||||
else |
||||
{ |
||||
SendMessage(msg.token, msg.CMDID, msg.Error, msg.data); |
||||
} |
||||
msg = null; |
||||
} |
||||
catch |
||||
{ |
||||
OutNetLog("==============================================>"); |
||||
} |
||||
} |
||||
sendrun--; |
||||
OutNetLog("!!!!!!!!!!!!!!!!!!!!!!!!!!"); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
OutNetLog(ex.ToString()); |
||||
} |
||||
|
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送心跳包 |
||||
/// </summary> |
||||
/// <param name="token"></param> |
||||
void SendHeartbeatMessage(AsyncUserToken token) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
if (m_Sendpool.Count > 0) |
||||
{ |
||||
SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop(); |
||||
myreadEventArgs.UserToken = token; |
||||
myreadEventArgs.AcceptSocket = token.Socket; |
||||
//直接写入SocketAsyncEventArgs的Buff |
||||
HunterNet_Heartbeat.SetDataToSocketAsyncEventArgs(myreadEventArgs); |
||||
|
||||
//若不需要等待 |
||||
if (!token.Socket.SendAsync(myreadEventArgs)) |
||||
{ |
||||
m_Sendpool.Push(myreadEventArgs); |
||||
} |
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
//先压入队列,等待m_Sendpool回收 |
||||
msg_pool.Enqueue(new TokenWithMsg() { token = token, bHeartbeat = true }); |
||||
//OutNetLog("!!!!压入消息发送队列MSG_Pool"); |
||||
return; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
OutNetLog(e.ToString()); |
||||
} |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 发送数据并计数 |
||||
/// </summary> |
||||
/// <param name="data"></param> |
||||
void SendWithIndex(AsyncUserToken token, UInt16 CmdID, UInt16 ERRCODE, byte[] data) |
||||
{ |
||||
try |
||||
{ |
||||
//发送数据 |
||||
SendMessage(token, CmdID, ERRCODE, data); |
||||
token.SendIndex = MaxSendIndexNum; |
||||
} |
||||
catch |
||||
{ |
||||
CloseReady(token); |
||||
} |
||||
} |
||||
|
||||
void SendMessage(AsyncUserToken token, UInt16 CmdID, UInt16 Error, byte[] data) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
if (m_Sendpool.Count > 0) |
||||
{ |
||||
SocketAsyncEventArgs myreadEventArgs = m_Sendpool.Pop(); |
||||
myreadEventArgs.UserToken = token; |
||||
myreadEventArgs.AcceptSocket = token.Socket; |
||||
//myreadEventArgs.SetBuffer(message, 0, message.Length); //将数据放置进去. |
||||
//更换为CMDID和Data直接写入SocketAsyncEventArgs的Buff |
||||
HunterNet_S2C.SetDataToSocketAsyncEventArgs(myreadEventArgs, CmdID, Error, data); |
||||
|
||||
//若不需要等待 |
||||
if (!token.Socket.SendAsync(myreadEventArgs)) |
||||
{ |
||||
m_Sendpool.Push(myreadEventArgs); |
||||
} |
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
//先压入队列,等待m_Sendpool回收 |
||||
msg_pool.Enqueue(new TokenWithMsg() { token = token, CMDID = CmdID, Error = Error, data = data }); |
||||
//OutNetLog("!!!!压入消息发送队列MSG_Pool"); |
||||
return; |
||||
} |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
OutNetLog(e.ToString()); |
||||
} |
||||
} |
||||
#endregion |
||||
|
||||
#region 处理前预备 |
||||
private void DataCallBackReady(AsyncUserToken sk, byte[] data) |
||||
{ |
||||
//增加接收计数 |
||||
sk.RevIndex = MaxRevIndexNum; |
||||
|
||||
if (data.Length == 1 && data[0] == 0x00)//心跳包 |
||||
{ |
||||
//OutNetLog("收到心跳包"); |
||||
//无处理 |
||||
} |
||||
else |
||||
{ |
||||
try |
||||
{ |
||||
//将数据包交给后台处理,这里你也可以新开个线程来处理.加快速度. |
||||
/* |
||||
HunterNet_C2S _s2c = DeSerizlize<HunterNet_C2S>(data); |
||||
OnReceive?.Invoke(sk, (int)_s2c.HunterNetCoreCmdID, _s2c.HunterNetCoreData.ToArray()); |
||||
//DataCallBack(sk, (int)_s2c.HunterNetCoreCmdID, _s2c.HunterNetCoreData.ToArray()); |
||||
*/ |
||||
HunterNet_C2S.AnalysisPkgData(data, out ushort CmdID, out byte[] resultdata); |
||||
OnReceive?.Invoke(sk, CmdID, resultdata); |
||||
} |
||||
catch (Exception ex) |
||||
{ |
||||
OutNetLog("数据解析错误"); |
||||
} |
||||
} |
||||
} |
||||
private void OutNetLog(string msg) |
||||
{ |
||||
OnNetLog?.Invoke(msg); |
||||
} |
||||
#endregion |
||||
|
||||
#region 心跳包 |
||||
/// <summary> |
||||
/// 发送心跳包 |
||||
/// </summary> |
||||
/// <param name="sk"></param> |
||||
/// |
||||
private void SendHeartbeatWithIndex(AsyncUserToken token) |
||||
{ |
||||
if (token == null || token.Socket == null || !token.Socket.Connected) |
||||
return; |
||||
try |
||||
{ |
||||
//OutNetLog(DateTime.Now.ToString() + "发送心跳包"); |
||||
token.SendIndex = MaxSendIndexNum; |
||||
SendHeartbeatMessage(token); |
||||
} |
||||
catch (Exception e) |
||||
{ |
||||
CloseReady(token); |
||||
} |
||||
} |
||||
/// <summary> |
||||
/// 心跳包时钟事件 |
||||
/// </summary> |
||||
/// <param name="sender"></param> |
||||
/// <param name="e"></param> |
||||
private void CheckUpdatetimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) |
||||
{ |
||||
for (int i = 0; i < m_clients.Count; i++) |
||||
{ |
||||
//接收服务器数据计数 |
||||
m_clients[i].RevIndex--; |
||||
if (m_clients[i].RevIndex <= 0) |
||||
{ |
||||
//判定掉线 |
||||
CloseReady(m_clients[i]); |
||||
return; |
||||
} |
||||
|
||||
//发送计数 |
||||
m_clients[i].SendIndex--; |
||||
if (m_clients[i].SendIndex <= 0)//需要发送心跳包了 |
||||
{ |
||||
//重置倒计时计数 |
||||
m_clients[i].SendIndex = MaxSendIndexNum; |
||||
SendHeartbeatWithIndex(m_clients[i]); |
||||
} |
||||
} |
||||
} |
||||
#endregion |
||||
} |
||||
} |
@ -0,0 +1,130 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Net.Sockets; |
||||
using System.Text; |
||||
|
||||
namespace HaoYueNet.ServerNetwork.Standard2 |
||||
{ |
||||
public class TokenWithMsg |
||||
{ |
||||
public AsyncUserToken token; |
||||
public UInt16 CMDID; |
||||
public UInt16 Error; |
||||
public byte[] data; |
||||
public bool bHeartbeat; |
||||
} |
||||
|
||||
public class TokenMsgPool |
||||
{ |
||||
//Stack<TokenWithMsg> msg_pool; |
||||
Queue<TokenWithMsg> msg_pool; |
||||
|
||||
public TokenMsgPool(int capacity) |
||||
{ |
||||
msg_pool = new Queue<TokenWithMsg>(capacity); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// 向 Queue 的末尾添加一个对象。 |
||||
/// </summary> |
||||
/// <param name="item"></param> |
||||
public void Enqueue(TokenWithMsg item) |
||||
{ |
||||
lock (msg_pool) |
||||
{ |
||||
msg_pool.Enqueue(item); |
||||
} |
||||
} |
||||
|
||||
//移除并返回在 Queue 的开头的对象。 |
||||
public TokenWithMsg Dequeue() |
||||
{ |
||||
lock (msg_pool) |
||||
{ |
||||
return msg_pool.Dequeue(); |
||||
} |
||||
} |
||||
|
||||
public int Count |
||||
{ |
||||
get { return msg_pool.Count; } |
||||
} |
||||
|
||||
public void Clear() |
||||
{ |
||||
msg_pool.Clear(); |
||||
} |
||||
} |
||||
|
||||
/* |
||||
public class TokenWithMsg |
||||
{ |
||||
public AsyncUserToken token; |
||||
public byte[] message; |
||||
} |
||||
|
||||
public class TokenMsgPool |
||||
{ |
||||
//Stack<TokenWithMsg> msg_pool; |
||||
Queue<TokenWithMsg> msg_pool; |
||||
|
||||
public TokenMsgPool(int capacity) |
||||
{ |
||||
//msg_pool = new Stack<TokenWithMsg>(capacity); |
||||
msg_pool = new Queue<TokenWithMsg>(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); |
||||
// } |
||||
//} |
||||
|
||||
/// <summary> |
||||
/// 向 Queue 的末尾添加一个对象。 |
||||
/// </summary> |
||||
/// <param name="item"></param> |
||||
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(); |
||||
} |
||||
} |
||||
*/ |
||||
} |
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<!-- |
||||
https://go.microsoft.com/fwlink/?LinkID=208121. |
||||
--> |
||||
<Project> |
||||
<PropertyGroup> |
||||
<Configuration>Release</Configuration> |
||||
<Platform>Any CPU</Platform> |
||||
<PublishDir>bin\Release\netstandard2.0\publish\</PublishDir> |
||||
<PublishProtocol>FileSystem</PublishProtocol> |
||||
<_TargetId>Folder</_TargetId> |
||||
</PropertyGroup> |
||||
</Project> |
@ -0,0 +1,506 @@
|
||||
//// <auto-generated> |
||||
//// Generated by the protocol buffer compiler. DO NOT EDIT! |
||||
//// source: protobuf_HunterNetCore.proto |
||||
//// </auto-generated> |
||||
//#pragma warning disable 1591, 0612, 3021 |
||||
//#region Designer generated code |
||||
|
||||
//using pb = global::Google.Protobuf; |
||||
//using pbc = global::Google.Protobuf.Collections; |
||||
//using pbr = global::Google.Protobuf.Reflection; |
||||
//using scg = global::System.Collections.Generic; |
||||
//namespace HunterProtobufCore { |
||||
|
||||
// /// <summary>Holder for reflection information generated from protobuf_HunterNetCore.proto</summary> |
||||
// public static partial class ProtobufHunterNetCoreReflection { |
||||
|
||||
// #region Descriptor |
||||
// /// <summary>File descriptor for protobuf_HunterNetCore.proto</summary> |
||||
// public static pbr::FileDescriptor Descriptor { |
||||
// get { return descriptor; } |
||||
// } |
||||
// private static pbr::FileDescriptor descriptor; |
||||
|
||||
// static ProtobufHunterNetCoreReflection() { |
||||
// byte[] descriptorData = global::System.Convert.FromBase64String( |
||||
// string.Concat( |
||||
// "Chxwcm90b2J1Zl9IdW50ZXJOZXRDb3JlLnByb3RvEhJIdW50ZXJQcm90b2J1", |
||||
// "ZkNvcmUiSAoNSHVudGVyTmV0X0MyUxIbChNIdW50ZXJOZXRDb3JlX0NtZElE", |
||||
// "GAEgASgFEhoKEkh1bnRlck5ldENvcmVfRGF0YRgCIAEoDCJpCg1IdW50ZXJO", |
||||
// "ZXRfUzJDEhsKE0h1bnRlck5ldENvcmVfQ21kSUQYASABKAUSHwoXSHVudGVy", |
||||
// "TmV0Q29yZV9FUlJPUkNvZGUYAiABKAUSGgoSSHVudGVyTmV0Q29yZV9EYXRh", |
||||
// "GAMgASgMQgJIAWIGcHJvdG8z")); |
||||
// descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, |
||||
// new pbr::FileDescriptor[] { }, |
||||
// new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { |
||||
// new pbr::GeneratedClrTypeInfo(typeof(global::HunterProtobufCore.HunterNet_C2S), global::HunterProtobufCore.HunterNet_C2S.Parser, new[]{ "HunterNetCoreCmdID", "HunterNetCoreData" }, null, null, null, null), |
||||
// new pbr::GeneratedClrTypeInfo(typeof(global::HunterProtobufCore.HunterNet_S2C), global::HunterProtobufCore.HunterNet_S2C.Parser, new[]{ "HunterNetCoreCmdID", "HunterNetCoreERRORCode", "HunterNetCoreData" }, null, null, null, null) |
||||
// })); |
||||
// } |
||||
// #endregion |
||||
|
||||
// } |
||||
// #region Messages |
||||
// /// <summary> |
||||
// ///上行 |
||||
// /// </summary> |
||||
// public sealed partial class HunterNet_C2S : pb::IMessage<HunterNet_C2S> |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// , pb::IBufferMessage |
||||
// #endif |
||||
// { |
||||
// private static readonly pb::MessageParser<HunterNet_C2S> _parser = new pb::MessageParser<HunterNet_C2S>(() => new HunterNet_C2S()); |
||||
// private pb::UnknownFieldSet _unknownFields; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public static pb::MessageParser<HunterNet_C2S> Parser { get { return _parser; } } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public static pbr::MessageDescriptor Descriptor { |
||||
// get { return global::HunterProtobufCore.ProtobufHunterNetCoreReflection.Descriptor.MessageTypes[0]; } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// pbr::MessageDescriptor pb::IMessage.Descriptor { |
||||
// get { return Descriptor; } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_C2S() { |
||||
// OnConstruction(); |
||||
// } |
||||
|
||||
// partial void OnConstruction(); |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_C2S(HunterNet_C2S other) : this() { |
||||
// hunterNetCoreCmdID_ = other.hunterNetCoreCmdID_; |
||||
// hunterNetCoreData_ = other.hunterNetCoreData_; |
||||
// _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_C2S Clone() { |
||||
// return new HunterNet_C2S(this); |
||||
// } |
||||
|
||||
// /// <summary>Field number for the "HunterNetCore_CmdID" field.</summary> |
||||
// public const int HunterNetCoreCmdIDFieldNumber = 1; |
||||
// private int hunterNetCoreCmdID_; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public int HunterNetCoreCmdID { |
||||
// get { return hunterNetCoreCmdID_; } |
||||
// set { |
||||
// hunterNetCoreCmdID_ = value; |
||||
// } |
||||
// } |
||||
|
||||
// /// <summary>Field number for the "HunterNetCore_Data" field.</summary> |
||||
// public const int HunterNetCoreDataFieldNumber = 2; |
||||
// private pb::ByteString hunterNetCoreData_ = pb::ByteString.Empty; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public pb::ByteString HunterNetCoreData { |
||||
// get { return hunterNetCoreData_; } |
||||
// set { |
||||
// hunterNetCoreData_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); |
||||
// } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override bool Equals(object other) { |
||||
// return Equals(other as HunterNet_C2S); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public bool Equals(HunterNet_C2S other) { |
||||
// if (ReferenceEquals(other, null)) { |
||||
// return false; |
||||
// } |
||||
// if (ReferenceEquals(other, this)) { |
||||
// return true; |
||||
// } |
||||
// if (HunterNetCoreCmdID != other.HunterNetCoreCmdID) return false; |
||||
// if (HunterNetCoreData != other.HunterNetCoreData) return false; |
||||
// return Equals(_unknownFields, other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override int GetHashCode() { |
||||
// int hash = 1; |
||||
// if (HunterNetCoreCmdID != 0) hash ^= HunterNetCoreCmdID.GetHashCode(); |
||||
// if (HunterNetCoreData.Length != 0) hash ^= HunterNetCoreData.GetHashCode(); |
||||
// if (_unknownFields != null) { |
||||
// hash ^= _unknownFields.GetHashCode(); |
||||
// } |
||||
// return hash; |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override string ToString() { |
||||
// return pb::JsonFormatter.ToDiagnosticString(this); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void WriteTo(pb::CodedOutputStream output) { |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// output.WriteRawMessage(this); |
||||
// #else |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// output.WriteRawTag(8); |
||||
// output.WriteInt32(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// output.WriteRawTag(18); |
||||
// output.WriteBytes(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// _unknownFields.WriteTo(output); |
||||
// } |
||||
// #endif |
||||
// } |
||||
|
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// output.WriteRawTag(8); |
||||
// output.WriteInt32(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// output.WriteRawTag(18); |
||||
// output.WriteBytes(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// _unknownFields.WriteTo(ref output); |
||||
// } |
||||
// } |
||||
// #endif |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public int CalculateSize() { |
||||
// int size = 0; |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// size += 1 + pb::CodedOutputStream.ComputeInt32Size(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// size += 1 + pb::CodedOutputStream.ComputeBytesSize(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// size += _unknownFields.CalculateSize(); |
||||
// } |
||||
// return size; |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void MergeFrom(HunterNet_C2S other) { |
||||
// if (other == null) { |
||||
// return; |
||||
// } |
||||
// if (other.HunterNetCoreCmdID != 0) { |
||||
// HunterNetCoreCmdID = other.HunterNetCoreCmdID; |
||||
// } |
||||
// if (other.HunterNetCoreData.Length != 0) { |
||||
// HunterNetCoreData = other.HunterNetCoreData; |
||||
// } |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void MergeFrom(pb::CodedInputStream input) { |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// input.ReadRawMessage(this); |
||||
// #else |
||||
// uint tag; |
||||
// while ((tag = input.ReadTag()) != 0) { |
||||
// switch(tag) { |
||||
// default: |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); |
||||
// break; |
||||
// case 8: { |
||||
// HunterNetCoreCmdID = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 18: { |
||||
// HunterNetCoreData = input.ReadBytes(); |
||||
// break; |
||||
// } |
||||
// } |
||||
// } |
||||
// #endif |
||||
// } |
||||
|
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { |
||||
// uint tag; |
||||
// while ((tag = input.ReadTag()) != 0) { |
||||
// switch(tag) { |
||||
// default: |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); |
||||
// break; |
||||
// case 8: { |
||||
// HunterNetCoreCmdID = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 18: { |
||||
// HunterNetCoreData = input.ReadBytes(); |
||||
// break; |
||||
// } |
||||
// } |
||||
// } |
||||
// } |
||||
// #endif |
||||
|
||||
// } |
||||
|
||||
// /// <summary> |
||||
// ///下行 |
||||
// /// </summary> |
||||
// public sealed partial class HunterNet_S2C : pb::IMessage<HunterNet_S2C> |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// , pb::IBufferMessage |
||||
// #endif |
||||
// { |
||||
// private static readonly pb::MessageParser<HunterNet_S2C> _parser = new pb::MessageParser<HunterNet_S2C>(() => new HunterNet_S2C()); |
||||
// private pb::UnknownFieldSet _unknownFields; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public static pb::MessageParser<HunterNet_S2C> Parser { get { return _parser; } } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public static pbr::MessageDescriptor Descriptor { |
||||
// get { return global::HunterProtobufCore.ProtobufHunterNetCoreReflection.Descriptor.MessageTypes[1]; } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// pbr::MessageDescriptor pb::IMessage.Descriptor { |
||||
// get { return Descriptor; } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_S2C() { |
||||
// OnConstruction(); |
||||
// } |
||||
|
||||
// partial void OnConstruction(); |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_S2C(HunterNet_S2C other) : this() { |
||||
// hunterNetCoreCmdID_ = other.hunterNetCoreCmdID_; |
||||
// hunterNetCoreERRORCode_ = other.hunterNetCoreERRORCode_; |
||||
// hunterNetCoreData_ = other.hunterNetCoreData_; |
||||
// _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public HunterNet_S2C Clone() { |
||||
// return new HunterNet_S2C(this); |
||||
// } |
||||
|
||||
// /// <summary>Field number for the "HunterNetCore_CmdID" field.</summary> |
||||
// public const int HunterNetCoreCmdIDFieldNumber = 1; |
||||
// private int hunterNetCoreCmdID_; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public int HunterNetCoreCmdID { |
||||
// get { return hunterNetCoreCmdID_; } |
||||
// set { |
||||
// hunterNetCoreCmdID_ = value; |
||||
// } |
||||
// } |
||||
|
||||
// /// <summary>Field number for the "HunterNetCore_ERRORCode" field.</summary> |
||||
// public const int HunterNetCoreERRORCodeFieldNumber = 2; |
||||
// private int hunterNetCoreERRORCode_; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public int HunterNetCoreERRORCode { |
||||
// get { return hunterNetCoreERRORCode_; } |
||||
// set { |
||||
// hunterNetCoreERRORCode_ = value; |
||||
// } |
||||
// } |
||||
|
||||
// /// <summary>Field number for the "HunterNetCore_Data" field.</summary> |
||||
// public const int HunterNetCoreDataFieldNumber = 3; |
||||
// private pb::ByteString hunterNetCoreData_ = pb::ByteString.Empty; |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public pb::ByteString HunterNetCoreData { |
||||
// get { return hunterNetCoreData_; } |
||||
// set { |
||||
// hunterNetCoreData_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); |
||||
// } |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override bool Equals(object other) { |
||||
// return Equals(other as HunterNet_S2C); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public bool Equals(HunterNet_S2C other) { |
||||
// if (ReferenceEquals(other, null)) { |
||||
// return false; |
||||
// } |
||||
// if (ReferenceEquals(other, this)) { |
||||
// return true; |
||||
// } |
||||
// if (HunterNetCoreCmdID != other.HunterNetCoreCmdID) return false; |
||||
// if (HunterNetCoreERRORCode != other.HunterNetCoreERRORCode) return false; |
||||
// if (HunterNetCoreData != other.HunterNetCoreData) return false; |
||||
// return Equals(_unknownFields, other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override int GetHashCode() { |
||||
// int hash = 1; |
||||
// if (HunterNetCoreCmdID != 0) hash ^= HunterNetCoreCmdID.GetHashCode(); |
||||
// if (HunterNetCoreERRORCode != 0) hash ^= HunterNetCoreERRORCode.GetHashCode(); |
||||
// if (HunterNetCoreData.Length != 0) hash ^= HunterNetCoreData.GetHashCode(); |
||||
// if (_unknownFields != null) { |
||||
// hash ^= _unknownFields.GetHashCode(); |
||||
// } |
||||
// return hash; |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public override string ToString() { |
||||
// return pb::JsonFormatter.ToDiagnosticString(this); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void WriteTo(pb::CodedOutputStream output) { |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// output.WriteRawMessage(this); |
||||
// #else |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// output.WriteRawTag(8); |
||||
// output.WriteInt32(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreERRORCode != 0) { |
||||
// output.WriteRawTag(16); |
||||
// output.WriteInt32(HunterNetCoreERRORCode); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// output.WriteRawTag(26); |
||||
// output.WriteBytes(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// _unknownFields.WriteTo(output); |
||||
// } |
||||
// #endif |
||||
// } |
||||
|
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// output.WriteRawTag(8); |
||||
// output.WriteInt32(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreERRORCode != 0) { |
||||
// output.WriteRawTag(16); |
||||
// output.WriteInt32(HunterNetCoreERRORCode); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// output.WriteRawTag(26); |
||||
// output.WriteBytes(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// _unknownFields.WriteTo(ref output); |
||||
// } |
||||
// } |
||||
// #endif |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public int CalculateSize() { |
||||
// int size = 0; |
||||
// if (HunterNetCoreCmdID != 0) { |
||||
// size += 1 + pb::CodedOutputStream.ComputeInt32Size(HunterNetCoreCmdID); |
||||
// } |
||||
// if (HunterNetCoreERRORCode != 0) { |
||||
// size += 1 + pb::CodedOutputStream.ComputeInt32Size(HunterNetCoreERRORCode); |
||||
// } |
||||
// if (HunterNetCoreData.Length != 0) { |
||||
// size += 1 + pb::CodedOutputStream.ComputeBytesSize(HunterNetCoreData); |
||||
// } |
||||
// if (_unknownFields != null) { |
||||
// size += _unknownFields.CalculateSize(); |
||||
// } |
||||
// return size; |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void MergeFrom(HunterNet_S2C other) { |
||||
// if (other == null) { |
||||
// return; |
||||
// } |
||||
// if (other.HunterNetCoreCmdID != 0) { |
||||
// HunterNetCoreCmdID = other.HunterNetCoreCmdID; |
||||
// } |
||||
// if (other.HunterNetCoreERRORCode != 0) { |
||||
// HunterNetCoreERRORCode = other.HunterNetCoreERRORCode; |
||||
// } |
||||
// if (other.HunterNetCoreData.Length != 0) { |
||||
// HunterNetCoreData = other.HunterNetCoreData; |
||||
// } |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); |
||||
// } |
||||
|
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// public void MergeFrom(pb::CodedInputStream input) { |
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// input.ReadRawMessage(this); |
||||
// #else |
||||
// uint tag; |
||||
// while ((tag = input.ReadTag()) != 0) { |
||||
// switch(tag) { |
||||
// default: |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); |
||||
// break; |
||||
// case 8: { |
||||
// HunterNetCoreCmdID = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 16: { |
||||
// HunterNetCoreERRORCode = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 26: { |
||||
// HunterNetCoreData = input.ReadBytes(); |
||||
// break; |
||||
// } |
||||
// } |
||||
// } |
||||
// #endif |
||||
// } |
||||
|
||||
// #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE |
||||
// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] |
||||
// void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { |
||||
// uint tag; |
||||
// while ((tag = input.ReadTag()) != 0) { |
||||
// switch(tag) { |
||||
// default: |
||||
// _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); |
||||
// break; |
||||
// case 8: { |
||||
// HunterNetCoreCmdID = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 16: { |
||||
// HunterNetCoreERRORCode = input.ReadInt32(); |
||||
// break; |
||||
// } |
||||
// case 26: { |
||||
// HunterNetCoreData = input.ReadBytes(); |
||||
// break; |
||||
// } |
||||
// } |
||||
// } |
||||
// } |
||||
// #endif |
||||
|
||||
// } |
||||
|
||||
// #endregion |
||||
|
||||
//} |
||||
|
||||
//#endregion Designer generated code |
Loading…
Reference in new issue