自动重连和内存流优化

This commit is contained in:
sin365 2023-12-14 18:15:04 +08:00
parent 902c7859ee
commit 4f9cb4d22d
25 changed files with 310 additions and 141 deletions

View File

@ -1,5 +1,4 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using HunterProtobufCore;
using System.Net;
using System.Net.Sockets;
@ -37,9 +36,11 @@ namespace HaoYueNet.ClientNetwork
private System.Timers.Timer _heartTimer;
public void Init(string IP, int port, bool bBindReuseAddress = false,int bBindport = 0)
{
public static string LastConnectIP;
public static int LastConnectPort;
public bool Init(string IP, int port, bool bBindReuseAddress = false,int bBindport = 0)
{
LogOut("==>初始化网络核心");
RevIndex = MaxRevIndexNum;
@ -52,10 +53,12 @@ namespace HaoYueNet.ClientNetwork
IPEndPoint ipe = new IPEndPoint(IPAddress.Any, Convert.ToInt32(bBindport));
client.Bind(ipe);
}
Connect(IP, port);
LastConnectIP = IP;
LastConnectPort = port;
return Connect(IP, port);
}
public bool Connect(string IP, int port)
bool Connect(string IP, int port)
{
//带回调的
try
@ -246,10 +249,12 @@ namespace HaoYueNet.ClientNetwork
OnReceiveData(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray());
}
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
private void Recive(object o)
{
var client = o as Socket;
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
//MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
while (true)
{
@ -278,8 +283,6 @@ namespace HaoYueNet.ClientNetwork
while (true)
{
if (effective > 0)//如果接受到的消息不为0不为空
{
int HeadLength = 0;//包头长度(包头+包体)
@ -296,16 +299,29 @@ namespace HaoYueNet.ClientNetwork
//↓↓↓↓↓↓↓↓ ↓↓↓
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());
//DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray());
//改为Array.Copy 提升效率
int CoreLenght = HeadLength - 4;
byte[] retData = new byte[CoreLenght];
Array.Copy(getData, StartIndex + 4, retData, 0, CoreLenght);
DataCallBackReady(retData);
StartIndex += HeadLength;//当读取一条完整的数据后,读取数据的起始下标应为当前接受到的消息体的长度(当前数据的尾部或下一条消息的首部)
}
}

View File

@ -1,5 +1,4 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using HunterProtobufCore;
using System.Net;
using System.Net.Sockets;
@ -252,10 +251,11 @@ namespace HaoYueNet.ClientNetwork
OnDataCallBack(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray());
}
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
private void Recive(object o)
{
var client = o as Socket;
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
//MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
while (true)
{
@ -302,21 +302,33 @@ namespace HaoYueNet.ClientNetwork
//↓↓↓↓↓↓↓↓ ↓↓↓
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());
//DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray());
//改为Array.Copy 提升效率
int CoreLenght = HeadLength - 4;
byte[] retData = new byte[CoreLenght];
Array.Copy(getData, StartIndex + 4, retData, 0, CoreLenght);
DataCallBackReady(retData);
StartIndex += HeadLength;//当读取一条完整的数据后,读取数据的起始下标应为当前接受到的消息体的长度(当前数据的尾部或下一条消息的首部)
}
}
}
}
}

View File

@ -6,10 +6,9 @@
#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 {
namespace HunterProtobufCore
{
/// <summary>Holder for reflection information generated from protobuf_HunterNetCore.proto</summary>
public static partial class ProtobufHunterNetCoreReflection {

View File

@ -249,10 +249,12 @@ namespace HaoYueNet.ClientNetworkNet4x
OnReceiveData(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray());
}
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
private void Recive(object o)
{
var client = o as Socket;
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
//MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
while (true)
{
@ -281,8 +283,6 @@ namespace HaoYueNet.ClientNetworkNet4x
while (true)
{
if (effective > 0)//如果接受到的消息不为0不为空
{
int HeadLength = 0;//包头长度(包头+包体)
@ -299,16 +299,29 @@ namespace HaoYueNet.ClientNetworkNet4x
//↓↓↓↓↓↓↓↓ ↓↓↓
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());
//DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray());
//改为Array.Copy 提升效率
int CoreLenght = HeadLength - 4;
byte[] retData = new byte[CoreLenght];
Array.Copy(getData, StartIndex + 4, retData, 0, CoreLenght);
DataCallBackReady(retData);
StartIndex += HeadLength;//当读取一条完整的数据后,读取数据的起始下标应为当前接受到的消息体的长度(当前数据的尾部或下一条消息的首部)
}
}

View File

@ -255,10 +255,11 @@ namespace HaoYueNet.ClientNetworkNet4x
OnDataCallBack(_c2s.HunterNetCoreCmdID, _c2s.HunterNetCoreERRORCode, _c2s.HunterNetCoreData.ToArray());
}
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
private void Recive(object o)
{
var client = o as Socket;
MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
//MemoryStream memoryStream = new MemoryStream();//开辟一个内存流
while (true)
{
@ -305,21 +306,33 @@ namespace HaoYueNet.ClientNetworkNet4x
//↓↓↓↓↓↓↓↓ ↓↓↓
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());
//DataCallBackReady(getData.Skip(StartIndex+4).Take(HeadLength-4).ToArray());
//改为Array.Copy 提升效率
int CoreLenght = HeadLength - 4;
byte[] retData = new byte[CoreLenght];
Array.Copy(getData, StartIndex + 4, retData, 0, CoreLenght);
DataCallBackReady(retData);
StartIndex += HeadLength;//当读取一条完整的数据后,读取数据的起始下标应为当前接受到的消息体的长度(当前数据的尾部或下一条消息的首部)
}
}
}
}
}

View File

@ -6,10 +6,9 @@
#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 {
namespace HunterProtobufCore
{
/// <summary>Holder for reflection information generated from protobuf_HunterNetCore.proto</summary>
public static partial class ProtobufHunterNetCoreReflection {

View File

@ -1,10 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace HaoYueNet.ServerNetwork
{
@ -38,13 +33,15 @@ namespace HaoYueNet.ServerNetwork
/// <summary>
/// 数据缓存区
/// </summary>
public List<byte> Buffer { get; set; }
//public List<byte> Buffer { get; set; }
public MemoryStream memoryStream { get; set; }
public AsyncUserToken()
{
this.Buffer = new List<byte>();
//this.Buffer = new List<byte>();
this.memoryStream = new MemoryStream();
}
/// <summary>
/// 响应倒计时计数
/// </summary>

View File

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Net.Sockets;
namespace HaoYueNet.ServerNetwork
{

View File

@ -1,15 +1,7 @@
using Google.Protobuf;
using HunterProtobufCore;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using static Google.Protobuf.Reflection.FieldOptions.Types;
namespace HaoYueNet.ServerNetwork
@ -185,7 +177,7 @@ namespace HaoYueNet.ServerNetwork
// post accepts on the listening socket
StartAccept(null);
OutNetLog("监听:" + listenSocket.AddressFamily.ToString());
OutNetLog("监听:" + listenSocket.LocalEndPoint.ToString());
_heartTimer = new System.Timers.Timer();
_heartTimer.Interval = TimerInterval;
@ -338,7 +330,12 @@ namespace HaoYueNet.ServerNetwork
// Get the socket for the accepted client connection and put it into the
//ReadEventArg object user token
SocketAsyncEventArgs readEventArgs = m_Receivepool.Pop();
AsyncUserToken userToken = (AsyncUserToken)readEventArgs.UserToken;
//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;
@ -381,39 +378,68 @@ namespace HaoYueNet.ServerNetwork
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)
//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.Buffer.AddRange(data);
token.memoryStream.Write(e.Buffer, e.Offset, e.BytesTransferred);
}
do
{
//如果包头不完整
if (token.Buffer.Count < 4)
//if (token.Buffer.Count < 4)
if (token.memoryStream.Length < 4)
break;
//判断包的长度
byte[] lenBytes = token.Buffer.GetRange(0, 4).ToArray();
//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.Buffer.Count - 4)
if (packageLen > token.memoryStream.Length - 4)
{ //长度不够时,退出循环,让程序继续接收
break;
}
//包够长时,则提取出来,交给后面的程序去处理
byte[] rev = token.Buffer.GetRange(4, packageLen).ToArray();
//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.Buffer)
lock (token.memoryStream)
{
token.Buffer.RemoveRange(0, packageLen + 4);
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.Buffer.Count > 4);
} while (token.memoryStream.Length > 4);
//继续接收. 为什么要这么写,请看Socket.ReceiveAsync方法的说明
if (!token.Socket.ReceiveAsync(e))

View File

@ -33,6 +33,20 @@ while (true)
}
App.chat.SendChatMsg(CmdArr[1]);
break;
case "at":
//test
string guid = Guid.NewGuid().ToString();
App.login.Login(guid);
while (true)
{
if (!App.networkHelper.GetClientSocket().Connected)
return;
Thread.Sleep(10);
App.chat.SendChatMsg(guid);
}
//test end
break;
case "socket":
{
Socket socket = App.networkHelper.GetClientSocket();

View File

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ClientCore.Manager;
using ClientCore.Manager;
using ClientCore.Network;
namespace ClientCore
@ -18,6 +13,7 @@ namespace ClientCore
public static NetworkHelper networkHelper;
public static AppLogin login;
public static AppChat chat;
public static UserDataManager user;
public static void Init(string IP, int port)
{
@ -25,6 +21,7 @@ namespace ClientCore
networkHelper = new NetworkHelper();
login = new AppLogin();
chat = new AppChat();
user = new UserDataManager();
networkHelper.Init(IP, port);
}
}

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClientCore.Common
namespace ClientCore.Common
{
public static class Helper
{

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClientCore.Event
namespace ClientCore.Event
{
public enum EEvent
{

View File

@ -1,11 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ClientCore.Event
namespace ClientCore.Event
{
public class EventData
{

View File

@ -1,10 +1,15 @@
using AxibugProtobuf;
using ClientCore.Common;
using ClientCore.Network;
namespace ClientCore.Manager
{
public class AppLogin
{
public AppLogin()
{
NetMsg.Instance.RegNetMsgEvent((int)CommandID.CmdLogin, RecvLoginMsg);
}
public void Login(string Account)
{
Protobuf_Login msg = new Protobuf_Login()
@ -14,5 +19,20 @@ namespace ClientCore.Manager
};
App.networkHelper.SendToServer((int)CommandID.CmdLogin, NetworkHelper.Serizlize(msg));
}
public void RecvLoginMsg(byte[] reqData)
{
Protobuf_Login_RESP msg = ProtoBufHelper.DeSerizlize<Protobuf_Login_RESP>(reqData);
if (msg.Status == LoginResultStatus.Ok)
{
App.log.Debug("登录成功");
App.user.InitMainUserData(msg);
}
else
{
App.log.Debug("登录失败");
}
}
}
}

View File

@ -0,0 +1,57 @@
using AxibugProtobuf;
namespace ClientCore.Manager
{
public class UserDataBase
{
public long UID { get; set; }
public string Account { get; set; }
}
public class MainUserDataBase : UserDataBase
{
public bool IsLoggedIn { get; set; } = false;
}
public class UserDataManager
{
public UserDataManager()
{
//注册重连成功事件,以便后续自动登录
App.networkHelper.OnReConnected += OnReConnected;
}
MainUserDataBase user = new MainUserDataBase();
public bool IsLoggedIn => user.IsLoggedIn;
public void InitMainUserData(string UName)
{
user.Account = UName;
user.IsLoggedIn = true;
//以及其他数据初始化
//...
}
/// <summary>
/// 登出
/// </summary>
public void LoginOutData()
{
user.IsLoggedIn = false;
//以及其他数据清理
//...
}
/// <summary>
/// 当重连成功
/// </summary>
public void OnReConnected()
{
//如果之前已登录,则重新登录
if (user.IsLoggedIn)
{
App.login.Login(user.Account);
}
}
}
}

View File

@ -13,6 +13,10 @@ namespace ClientCore.Network
/// </summary>
public class NetworkHelper : NetworkHelperCore
{
/// <summary>
/// 网络库调试日志输出
/// </summary>
public event OnLogOutHandler OnLogOut;
public NetworkHelper()
{
//指定接收服务器数据事件
@ -24,14 +28,34 @@ namespace ClientCore.Network
OnLogOut += NetworkDeBugLog;
}
public delegate void OnReConnectedHandler();
/// <summary>
/// 重连成功事件
/// </summary>
public event OnReConnectedHandler OnReConnected;
/// <summary>
/// 是否自动重连
/// </summary>
public bool bAutoReConnect = true;
/// <summary>
/// 重连尝试时间
/// </summary>
const int ReConnectTryTime = 1000;
public void NetworkConnected(bool IsConnect)
{
NetworkDeBugLog($"NetworkConnected:{IsConnect}");
if (IsConnect)
NetworkDeBugLog("服务器连接成功");
{
}
else
{
NetworkDeBugLog("服务器连接失败");
//to do 重连逻辑
//连接失败
NetworkDeBugLog("连接失败!");
//自动重连开关
if (bAutoReConnect)
ReConnect();
}
}
@ -39,7 +63,6 @@ namespace ClientCore.Network
{
//用于Unity内的输出
//Debug.Log("NetCoreDebug >> "+str);
Console.WriteLine("NetCoreDebug >> " + str);
}
@ -70,6 +93,39 @@ namespace ClientCore.Network
public void OnConnectClose()
{
NetworkDeBugLog("OnConnectClose");
//自动重连开关
if (bAutoReConnect)
ReConnect();
}
bool bInReConnecting = false;
/// <summary>
/// 自动重连
/// </summary>
void ReConnect()
{
if (bInReConnecting)
return;
bInReConnecting = true;
bool bflagDone = false;
do
{
//等待时间
Thread.Sleep(ReConnectTryTime);
App.log.Debug($"尝试自动重连{LastConnectIP}:{LastConnectPort}……");
//第一步
if (Init(LastConnectIP, LastConnectPort))
{
App.log.Debug($"自动重连成功!");
bflagDone = true;
App.log.Debug($"触发重连后的自动逻辑!");
OnReConnected?.Invoke();
}
} while (!bflagDone);
bInReConnecting = false;
}
}
}

View File

@ -1,5 +1,4 @@
using ServerCore;
using ServerCore.Manager;
using ServerCore.Manager;
ServerManager.InitServer(23846);

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCore.Common
namespace ServerCore.Common
{
public static class Helper
{

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCore.Event
namespace ServerCore.Event
{
public enum EEvent
{

View File

@ -1,10 +1,4 @@
using ServerCore.Manager;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ServerCore.Event
{

View File

@ -15,7 +15,7 @@ namespace ServerCore.Manager
public void RecvPlayerChatMsg(Socket sk, byte[] reqData)
{
ClientInfo _c = ServerManager.g_ClientMgr.GetClientForSocket(sk);
ServerManager.g_Log.Debug("收到新的登录请求");
ServerManager.g_Log.Debug("收到聊天消息请求");
Protobuf_ChatMsg msg = ProtoBufHelper.DeSerizlize<Protobuf_ChatMsg>(reqData);
byte[] respData = ProtoBufHelper.Serizlize(new Protobuf_ChatMsg_RESP()
{

View File

@ -1,5 +1,5 @@
using System.Net;
using ServerCore.NetWork;
using ServerCore.NetWork;
using System.Net;
namespace ServerCore.Manager
{

View File

@ -1,7 +1,5 @@
using AxibugProtobuf;
using HaoYueNet.ServerNetwork;
using HaoYueNet.ServerNetwork;
using ServerCore.Manager;
using System.Net;
using System.Net.Sockets;
namespace ServerCore.NetWork

View File

@ -1,11 +1,5 @@
using ServerCore.Manager;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ServerCore.NetWork
{