mirror of
https://github.com/Alex-Rachel/TEngine.git
synced 2025-08-14 16:51:28 +00:00
提交网络模块TCP 超时重连机制
提交网络模块TCP 超时重连机制
This commit is contained in:
@@ -1,10 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TEngineCore;
|
||||
|
||||
namespace TEngineHotUpdate
|
||||
{
|
||||
@@ -12,32 +6,32 @@ namespace TEngineHotUpdate
|
||||
{
|
||||
public static void Init()
|
||||
{
|
||||
Debug.Log("Init");
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
|
||||
public static void Start()
|
||||
{
|
||||
Debug.Log("Start");
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
Debug.Log("Update");
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
|
||||
public static void LateUpdate()
|
||||
{
|
||||
Debug.Log("LateUpdate");
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
|
||||
public static void Destroy()
|
||||
{
|
||||
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
|
||||
public static void OnApplicationPause(bool isPause)
|
||||
{
|
||||
|
||||
GameTime.StartFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Project ToolsVersion="15.0"
|
||||
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -56,6 +57,12 @@
|
||||
<Reference Include="UnityEngine.InputLegacyModule">
|
||||
<HintPath>UnityLib\UnityEngine.InputLegacyModule.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net">
|
||||
<HintPath>UnityLib\protobuf-net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="protobuf-net">
|
||||
<HintPath>UnityLib\Google.Protobuf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
@@ -68,6 +75,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\GameLogic\" />
|
||||
<Folder Include="src\Proto\" />
|
||||
<Folder Include="src\TEngineCore\3rd\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
BIN
TEngineHotUpdate/UnityLib/Google.Protobuf.dll
Normal file
BIN
TEngineHotUpdate/UnityLib/Google.Protobuf.dll
Normal file
Binary file not shown.
BIN
TEngineHotUpdate/UnityLib/protobuf-net.dll
Normal file
BIN
TEngineHotUpdate/UnityLib/protobuf-net.dll
Normal file
Binary file not shown.
2104
TEngineHotUpdate/src/Proto/TEngineProto.cs
Normal file
2104
TEngineHotUpdate/src/Proto/TEngineProto.cs
Normal file
File diff suppressed because it is too large
Load Diff
31
TEngineHotUpdate/src/TEngineCore/Core/GameTime.cs
Normal file
31
TEngineHotUpdate/src/TEngineCore/Core/GameTime.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore
|
||||
{
|
||||
public static class GameTime
|
||||
{
|
||||
public static void StartFrame()
|
||||
{
|
||||
time = Time.time;
|
||||
deltaTime = Time.deltaTime;
|
||||
quickRealTime = Time.realtimeSinceStartup;
|
||||
frameCount = Time.frameCount;
|
||||
unscaledTime = Time.unscaledTime;
|
||||
}
|
||||
|
||||
public static float time;
|
||||
public static float deltaTime;
|
||||
public static int frameCount;
|
||||
public static float unscaledTime;
|
||||
|
||||
public static float realtimeSinceStartup
|
||||
{
|
||||
get
|
||||
{
|
||||
return Time.realtimeSinceStartup;
|
||||
}
|
||||
}
|
||||
|
||||
public static float quickRealTime;
|
||||
}
|
||||
}
|
166
TEngineHotUpdate/src/TEngineCore/Net/ClientConnectWatcher.cs
Normal file
166
TEngineHotUpdate/src/TEngineCore/Net/ClientConnectWatcher.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
enum ClientConnectWatcherStatus
|
||||
{
|
||||
StatusInit,
|
||||
StatusReconnectAuto,
|
||||
StatusReconnectConfirm,
|
||||
StatusWaitExit
|
||||
}
|
||||
|
||||
class ClientConnectWatcher
|
||||
{
|
||||
#region Propreties
|
||||
private GameClient m_client;
|
||||
private float m_statusTime;
|
||||
private int m_reconnetCnt = 0;
|
||||
private int m_disconnectReason = 0;
|
||||
private ClientConnectWatcherStatus m_status = ClientConnectWatcherStatus.StatusInit;
|
||||
|
||||
private bool m_enable = false;
|
||||
public bool Enable
|
||||
{
|
||||
get { return m_enable; }
|
||||
set
|
||||
{
|
||||
if (m_enable != value)
|
||||
{
|
||||
m_enable = value;
|
||||
if (m_enable)
|
||||
{
|
||||
OnEnable();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnDisable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClientConnectWatcherStatus Status
|
||||
{
|
||||
get { return m_status; }
|
||||
set
|
||||
{
|
||||
if (m_status != value)
|
||||
{
|
||||
m_status = value;
|
||||
m_statusTime = GameTime.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public ClientConnectWatcher(GameClient client)
|
||||
{
|
||||
m_client = client;
|
||||
m_statusTime = GameTime.time;
|
||||
m_status = ClientConnectWatcherStatus.StatusInit;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!m_enable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_client.IsEntered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_status)
|
||||
{
|
||||
case ClientConnectWatcherStatus.StatusInit:
|
||||
UpdateOnInitStatus();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusReconnectAuto:
|
||||
UpdateOnReconnectAuto();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusReconnectConfirm:
|
||||
UpdateOnReconnectConfirm();
|
||||
break;
|
||||
case ClientConnectWatcherStatus.StatusWaitExit:
|
||||
UpdateOnWaitExit();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReConnect()
|
||||
{
|
||||
if (m_status == ClientConnectWatcherStatus.StatusReconnectConfirm)
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnInitStatus()
|
||||
{
|
||||
if (m_reconnetCnt <= 2)
|
||||
{
|
||||
if (m_reconnetCnt == 0)
|
||||
{
|
||||
m_disconnectReason = m_client.LastNetErrCode;
|
||||
}
|
||||
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectAuto;
|
||||
m_reconnetCnt++;
|
||||
|
||||
//Reconnect
|
||||
m_client.Reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusReconnectConfirm;
|
||||
m_reconnetCnt++;
|
||||
|
||||
//var window = UISys.Mgr.ShowWindow<Tip_NetError>();
|
||||
//window.SetErrCode(m_disconnectReason);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnReconnectAuto()
|
||||
{
|
||||
if (m_client.IsEntered)
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
m_reconnetCnt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
float nowTime = GameTime.time;
|
||||
if (m_statusTime + 5 < nowTime)
|
||||
{
|
||||
//切换到默认的,下一帧继续判断是否需要自动还是手动
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateOnReconnectConfirm()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UpdateOnWaitExit()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
m_reconnetCnt = 0;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Status = ClientConnectWatcherStatus.StatusInit;
|
||||
m_reconnetCnt = 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public enum ClientSocketEventType
|
||||
{
|
||||
EventConnected,
|
||||
EventConnectFail,
|
||||
EventDisconnected,
|
||||
}
|
||||
|
||||
public interface IClientSocket
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否连接了
|
||||
/// </summary>
|
||||
bool IsConnected { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否是流协议
|
||||
/// </summary>
|
||||
bool IsStream { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 心跳间隔
|
||||
/// </summary>
|
||||
int HeartBeatInterval { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 本地绑定地址
|
||||
/// </summary>
|
||||
EndPoint LocalAddr { get; }
|
||||
|
||||
SocketError LastSockError { get; }
|
||||
|
||||
string LastErrDesc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 注册系统事件
|
||||
/// </summary>
|
||||
/// <param name="handler"></param>
|
||||
void RegEventHandle(Action<ClientSocketEventType> handler);
|
||||
|
||||
/// <summary>
|
||||
/// 连接请求
|
||||
/// </summary>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="iTimeout"></param>
|
||||
/// <param name="retryNum"></param>
|
||||
/// <returns></returns>
|
||||
bool Connect(string server, int port, int iTimeout, int retryNum);
|
||||
|
||||
/// <summary>
|
||||
/// 关闭连接
|
||||
/// </summary>
|
||||
void Close();
|
||||
|
||||
/// <summary>
|
||||
/// 关闭连接
|
||||
/// </summary>
|
||||
void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// 发送数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool Send(byte[] data, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 发送快捷数据,不用保证丢包
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <param name="len"></param>
|
||||
/// <returns></returns>
|
||||
bool SendUdpTypeData(byte[] data, int offset, int len);
|
||||
|
||||
/// <summary>
|
||||
/// 是否支持udp的包
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool IsSupportUdpType();
|
||||
|
||||
/// <summary>
|
||||
/// 收包处理
|
||||
/// </summary>
|
||||
/// <param name="buf"></param>
|
||||
/// <param name="iOffset"></param>
|
||||
/// <param name="maxSize"></param>
|
||||
/// <returns></returns>
|
||||
int Recv(byte[] buf, int iOffset, int maxSize);
|
||||
|
||||
/// <summary>
|
||||
/// 循环调用
|
||||
/// </summary>
|
||||
void Update();
|
||||
|
||||
/// <summary>
|
||||
/// 最后一帧,保证肯定要包发出去,减少延迟
|
||||
/// </summary>
|
||||
void LateUpdate();
|
||||
|
||||
/// <summary>
|
||||
/// 获取写队列的个数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
int GetSendQueueCount();
|
||||
|
||||
/// <summary>
|
||||
/// 像底层注册错误打印,当缓冲区满之类的,调用上层的统计来打印
|
||||
/// </summary>
|
||||
/// <param name="debugCmd"></param>
|
||||
void RegDebugCmdHandle(Action debugCmd);
|
||||
}
|
||||
}
|
117
TEngineHotUpdate/src/TEngineCore/Net/ClientSocket/Message.cs
Normal file
117
TEngineHotUpdate/src/TEngineCore/Net/ClientSocket/Message.cs
Normal file
@@ -0,0 +1,117 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Google.Protobuf;
|
||||
using TEngineProto;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public class Message
|
||||
{
|
||||
private const int BufferHead = 4;
|
||||
|
||||
private static byte[] buffer = new byte[1024];
|
||||
|
||||
private int startindex;
|
||||
|
||||
public byte[] Buffer
|
||||
{
|
||||
get { return buffer; }
|
||||
}
|
||||
|
||||
public int StartIndex
|
||||
{
|
||||
get { return startindex; }
|
||||
}
|
||||
|
||||
public int Remsize
|
||||
{
|
||||
get { return buffer.Length - startindex; }
|
||||
}
|
||||
|
||||
public void ReadBuffer(byte[] bufBytes, Action<MainPack> handleResponse = null)
|
||||
{
|
||||
var length = bufBytes.Length;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
Buffer[i] = bufBytes[i];
|
||||
}
|
||||
|
||||
startindex += length;
|
||||
|
||||
if (startindex <= BufferHead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = length - BufferHead;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (startindex >= (count + BufferHead))
|
||||
{
|
||||
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, BufferHead, count);
|
||||
|
||||
if (handleResponse != null)
|
||||
{
|
||||
handleResponse(pack);
|
||||
}
|
||||
|
||||
Array.Copy(buffer, length, buffer, 0, startindex - length);
|
||||
|
||||
startindex -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadBuffer(int length, Action<MainPack> handleResponse = null)
|
||||
{
|
||||
startindex += length;
|
||||
|
||||
if (startindex <= BufferHead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int count = length - BufferHead;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (startindex >= (count + BufferHead))
|
||||
{
|
||||
MainPack pack = (MainPack)MainPack.Descriptor.Parser.ParseFrom(buffer, BufferHead, count);
|
||||
|
||||
if (handleResponse != null)
|
||||
{
|
||||
handleResponse(pack);
|
||||
}
|
||||
|
||||
Array.Copy(buffer, length, buffer, 0, startindex - length);
|
||||
|
||||
startindex -= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] PackData(MainPack pack)
|
||||
{
|
||||
byte[] data = pack.ToByteArray();
|
||||
byte[] head = BitConverter.GetBytes(data.Length);
|
||||
return head.Concat(data).ToArray();
|
||||
}
|
||||
|
||||
public static byte[] PackDataUdp(MainPack pack)
|
||||
{
|
||||
return pack.ToByteArray();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Sockets;
|
||||
using TEngineProto;
|
||||
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public class TcpConnection
|
||||
{
|
||||
private Socket socket;
|
||||
private string m_Host;
|
||||
private int m_Port;
|
||||
private Message message;
|
||||
private GameClient gameClient;
|
||||
|
||||
public TcpConnection(GameClient gameClient)
|
||||
{
|
||||
message = new Message();
|
||||
this.gameClient = gameClient;
|
||||
}
|
||||
|
||||
public bool Connect(string host, int port)
|
||||
{
|
||||
if (socket == null)
|
||||
{
|
||||
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (socket.Connected)
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
}
|
||||
|
||||
TLogger.LogInfo("start connect server[{0}:{1}]...", host, port);
|
||||
|
||||
gameClient.Status = GameClientStatus.StatusInit;
|
||||
try
|
||||
{
|
||||
socket.Connect(host, port);
|
||||
StartReceive();
|
||||
gameClient.Status = GameClientStatus.StatusConnect;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogError(e.Message);
|
||||
TLogger.LogError("socket connect {0}:{1} failed", host, port);
|
||||
//ChangeStateOnEnterFail();
|
||||
return false;
|
||||
}
|
||||
m_Host = host;
|
||||
m_Port = port;
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartReceive()
|
||||
{
|
||||
socket.BeginReceive(message.Buffer, message.StartIndex, message.Remsize, SocketFlags.None, ReceiveCallback, null);
|
||||
}
|
||||
|
||||
void ReceiveCallback(IAsyncResult asyncResult)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (socket == null || socket.Connected == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Length = socket.EndReceive(asyncResult);
|
||||
|
||||
if (Length == 0)
|
||||
{
|
||||
Close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
message.ReadBuffer(Length, gameClient.HandleResponse);
|
||||
|
||||
StartReceive();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogError("TcpConnection DisConnected: " + e);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SendCsMsg(MainPack mainPack)
|
||||
{
|
||||
if (socket == null || socket.Connected == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
socket.Send(Message.PackData(mainPack));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogError("TcpConnection SendCsMsg: " + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (socket != null && socket.Connected)
|
||||
{
|
||||
socket.Close();
|
||||
}
|
||||
gameClient.Status = GameClientStatus.StatusInit;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public class UdpConnection
|
||||
{
|
||||
|
||||
}
|
||||
}
|
42
TEngineHotUpdate/src/TEngineCore/Net/DataCenterSys.cs
Normal file
42
TEngineHotUpdate/src/TEngineCore/Net/DataCenterSys.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据中心系统
|
||||
/// </summary>
|
||||
public class DataCenterSys : BaseLogicSys<DataCenterSys>
|
||||
{
|
||||
private List<IDataCenterModule> m_listModule = new List<IDataCenterModule>();
|
||||
|
||||
public override bool OnInit()
|
||||
{
|
||||
RegCmdHandle();
|
||||
InitModule();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RegCmdHandle()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void InitModule()
|
||||
{
|
||||
//InitModule(LoginDataMgr.Instance);
|
||||
}
|
||||
|
||||
public void InitModule(IDataCenterModule module)
|
||||
{
|
||||
if (!m_listModule.Contains(module))
|
||||
{
|
||||
module.Init();
|
||||
m_listModule.Add(module);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
568
TEngineHotUpdate/src/TEngineCore/Net/GameClient.cs
Normal file
568
TEngineHotUpdate/src/TEngineCore/Net/GameClient.cs
Normal file
@@ -0,0 +1,568 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TEngineProto;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
/// <summary>
|
||||
/// 客户端状态
|
||||
/// </summary>
|
||||
public enum GameClientStatus
|
||||
{
|
||||
StatusInit, //初始化
|
||||
StatusReconnect, //重新连接
|
||||
StatusClose, //断开连接
|
||||
StatusConnect, //连接中
|
||||
StatusEnter, //Login登录成功
|
||||
}
|
||||
|
||||
public delegate void CsMsgDelegate(MainPack mainPack);
|
||||
|
||||
public class GameClient:TSingleton<GameClient>
|
||||
{
|
||||
|
||||
#region Propriety
|
||||
|
||||
#region TimeOutCheck
|
||||
private const int CHECK_TIMEOUT_PERFRAME = 10;
|
||||
const int MAX_MSG_HANDLE = 256;
|
||||
UInt32 m_dwLastCheckIndex = 0;
|
||||
CsMsgDelegate[] m_aMsgHandles = new CsMsgDelegate[MAX_MSG_HANDLE];
|
||||
float[] m_fMsgRegTime = new float[MAX_MSG_HANDLE];
|
||||
private float m_timeout = 15;
|
||||
#endregion
|
||||
|
||||
private string m_lastHost = null;
|
||||
private int m_lastPort = 0;
|
||||
private GameClientStatus m_status = GameClientStatus.StatusInit;
|
||||
/// <summary>
|
||||
/// GameClient状态
|
||||
/// </summary>
|
||||
public GameClientStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
set
|
||||
{
|
||||
m_status = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 最新连接错误的时间
|
||||
/// </summary>
|
||||
private float m_lastLogDisconnectErrTime = 0f;
|
||||
/// <summary>
|
||||
/// 最新的错误码
|
||||
/// </summary>
|
||||
private int m_lastNetErrCode = 0;
|
||||
/// <summary>
|
||||
/// 最近一次心跳的时间
|
||||
/// </summary>
|
||||
private float m_lastHbTime = 0f;
|
||||
/// <summary>
|
||||
/// 心跳间隔
|
||||
/// </summary>
|
||||
private const float m_heartBeatDurTime = 15;
|
||||
/// <summary>
|
||||
/// 连续心跳超时
|
||||
/// </summary>
|
||||
private int m_heatBeatTimeoutNum = 0;
|
||||
private int m_ping = -1;
|
||||
public bool IsEntered
|
||||
{
|
||||
get { return m_status == GameClientStatus.StatusEnter; }
|
||||
}
|
||||
public bool IsNetworkOkAndLogined
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_status == GameClientStatus.StatusEnter;
|
||||
}
|
||||
}
|
||||
|
||||
public int LastNetErrCode
|
||||
{
|
||||
get { return m_lastNetErrCode; }
|
||||
}
|
||||
|
||||
private ClientConnectWatcher m_connectWatcher;
|
||||
|
||||
private void ResetParam()
|
||||
{
|
||||
m_lastLogDisconnectErrTime = 0f;
|
||||
m_heatBeatTimeoutNum = 0;
|
||||
m_lastHbTime = 0f;
|
||||
m_ping = -1;
|
||||
m_lastNetErrCode = 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private TcpConnection m_connect;
|
||||
|
||||
public GameClient()
|
||||
{
|
||||
m_connect = new TcpConnection(this);
|
||||
m_connectWatcher = new ClientConnectWatcher(this);
|
||||
}
|
||||
|
||||
public bool Connect(string host, int port, bool reconnect = false)
|
||||
{
|
||||
ResetParam();
|
||||
if (!reconnect)
|
||||
{
|
||||
SetWatchReconnect(false);
|
||||
}
|
||||
//GameEventMgr.Instance.Send(ShowWaitingUI);
|
||||
m_lastHost = host;
|
||||
m_lastPort = port;
|
||||
Status = reconnect ? GameClientStatus.StatusReconnect : GameClientStatus.StatusInit;
|
||||
TLogger.LogWarning("Start connect server {0}:{1} Reconnect:{2}", host, port, reconnect);
|
||||
return m_connect.Connect(host, port);
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
m_connect.Close();
|
||||
m_status = GameClientStatus.StatusInit;
|
||||
}
|
||||
|
||||
#region 发送网络消息
|
||||
/// <summary>
|
||||
/// 发送消息包
|
||||
/// </summary>
|
||||
/// <param name="reqPkg"></param>
|
||||
/// <returns></returns>
|
||||
public bool SendCsMsg(MainPack reqPkg)
|
||||
{
|
||||
if (!CheckPack(reqPkg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return DoSendData(reqPkg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送消息包并注册回调
|
||||
/// </summary>
|
||||
/// <param name="pack"></param>
|
||||
/// <param name="resHandler"></param>
|
||||
/// <param name="needShowWaitUI"></param>
|
||||
/// <returns></returns>
|
||||
public bool SendCsMsg(MainPack pack, CsMsgDelegate resHandler = null, bool needShowWaitUI = true)
|
||||
{
|
||||
if (!CheckPack(pack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var ret = DoSendData(pack);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
TLogger.LogError("SendCSMsg Error");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (resHandler != null)
|
||||
{
|
||||
RegTimeOutHandle((uint)pack.Actioncode, resHandler);
|
||||
RegActionHandle((int)pack.Actioncode, resHandler);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private bool DoSendData(MainPack reqPkg)
|
||||
{
|
||||
var sendRet = m_connect.SendCsMsg(reqPkg);
|
||||
|
||||
return sendRet;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 网络消息回调,非主线程
|
||||
Dictionary<int, List<CsMsgDelegate>> m_mapCmdHandle = new Dictionary<int, List<CsMsgDelegate>>();
|
||||
/// <summary>
|
||||
/// 委托缓存堆栈
|
||||
/// </summary>
|
||||
private Queue<List<CsMsgDelegate>> cachelistHandle = new Queue<List<CsMsgDelegate>>();
|
||||
|
||||
/// <summary>
|
||||
/// 消息包缓存堆栈
|
||||
/// </summary>
|
||||
private Queue<MainPack> queuepPacks = new Queue<MainPack>();
|
||||
|
||||
/// <summary>
|
||||
/// 网络消息回调,非主线程
|
||||
/// </summary>
|
||||
/// <param name="pack"></param>
|
||||
public void HandleResponse(MainPack pack)
|
||||
{
|
||||
lock (cachelistHandle)
|
||||
{
|
||||
List<CsMsgDelegate> listHandle;
|
||||
|
||||
if (m_mapCmdHandle.TryGetValue((int)pack.Actioncode, out listHandle))
|
||||
{
|
||||
cachelistHandle.Enqueue(listHandle);
|
||||
|
||||
queuepPacks.Enqueue(pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Udp网络消息回调,非主线程
|
||||
/// </summary>
|
||||
/// <param name="pack"></param>
|
||||
private void UdpHandleResponse(MainPack pack)
|
||||
{
|
||||
//Debug.Log(pack);
|
||||
List<CsMsgDelegate> listHandle;
|
||||
|
||||
if (m_mapCmdHandle.TryGetValue((int)pack.Actioncode, out listHandle))
|
||||
{
|
||||
foreach (CsMsgDelegate handle in listHandle)
|
||||
{
|
||||
handle(pack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 注册网络消息回调
|
||||
/// <summary>
|
||||
/// 注册静态消息
|
||||
/// </summary>
|
||||
/// <param name="iCmdID"></param>
|
||||
/// <param name="msgDelegate"></param>
|
||||
public void RegActionHandle(int actionId, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
List<CsMsgDelegate> listHandle;
|
||||
if (!m_mapCmdHandle.TryGetValue(actionId, out listHandle))
|
||||
{
|
||||
listHandle = new List<CsMsgDelegate>();
|
||||
m_mapCmdHandle[actionId] = listHandle;
|
||||
}
|
||||
|
||||
if (listHandle != null)
|
||||
{
|
||||
if (listHandle.Contains(msgDelegate))
|
||||
{
|
||||
Debug.LogFormat("-------------repeat RegCmdHandle ActionCode:{0}-----------", (ActionCode)actionId);
|
||||
}
|
||||
listHandle.Add(msgDelegate);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 注册Udp静态消息
|
||||
/// </summary>
|
||||
/// <param name="iCmdID"></param>
|
||||
/// <param name="msgDelegate"></param>
|
||||
public void UdpRegActionHandle(int actionId, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
List<CsMsgDelegate> listHandle;
|
||||
if (!m_mapCmdHandle.TryGetValue(actionId, out listHandle))
|
||||
{
|
||||
listHandle = new List<CsMsgDelegate>();
|
||||
m_mapCmdHandle[actionId] = listHandle;
|
||||
}
|
||||
|
||||
if (listHandle != null)
|
||||
{
|
||||
if (listHandle.Contains(msgDelegate))
|
||||
{
|
||||
Debug.LogFormat("-------------repeat RegCmdHandle ActionCode:{0}-----------", (ActionCode)actionId);
|
||||
}
|
||||
listHandle.Add(msgDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除消息处理函数
|
||||
/// </summary>
|
||||
/// <param name="cmdId"></param>
|
||||
/// <param name="msgDelegate"></param>
|
||||
public void RmvCmdHandle(int actionId, CsMsgDelegate msgDelegate)
|
||||
{
|
||||
List<CsMsgDelegate> listHandle;
|
||||
if (!m_mapCmdHandle.TryGetValue(actionId, out listHandle))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (listHandle != null)
|
||||
{
|
||||
listHandle.Remove(msgDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckPack(MainPack pack)
|
||||
{
|
||||
if (pack == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pack.Actioncode == ActionCode.ActionNone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pack.Requestcode == RequestCode.RequestNone)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 心跳处理
|
||||
protected bool CheckHeatBeatTimeout()
|
||||
{
|
||||
if (m_heatBeatTimeoutNum >= 2)
|
||||
{
|
||||
Shutdown();
|
||||
m_heatBeatTimeoutNum = 0;
|
||||
Status = GameClientStatus.StatusClose;
|
||||
TLogger.LogError("heat beat detect timeout");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TickHeartBeat()
|
||||
{
|
||||
if (Status != GameClientStatus.StatusEnter)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var nowTime = GameTime.realtimeSinceStartup;
|
||||
if (m_lastHbTime + m_heartBeatDurTime < nowTime)
|
||||
{
|
||||
m_lastHbTime = nowTime;
|
||||
MainPack pack = new MainPack
|
||||
{
|
||||
Actioncode = ActionCode.HeartBeat
|
||||
};
|
||||
GameClient.Instance.SendCsMsg(pack, HandleHeatBeatRes);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleHeatBeatRes(MainPack mainPack)
|
||||
{
|
||||
if (mainPack.Returncode != ReturnCode.Success)
|
||||
{
|
||||
//如果是超时了,则标记最近收到包的次数
|
||||
if (mainPack.Returncode == ReturnCode.MsgTimeOut)
|
||||
{
|
||||
m_heatBeatTimeoutNum++;
|
||||
TLogger.LogError("heat beat timeout: {0}", m_heatBeatTimeoutNum);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float diffTime = GameTime.realtimeSinceStartup - mainPack.HeatEchoTime;
|
||||
m_ping = (int)(diffTime * 1000);
|
||||
m_heatBeatTimeoutNum = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有的网络消息
|
||||
/// </summary>
|
||||
public void CleanAllNetMsg()
|
||||
{
|
||||
m_mapCmdHandle.Clear();
|
||||
}
|
||||
|
||||
public void Reconnect()
|
||||
{
|
||||
m_connectWatcher.OnReConnect();
|
||||
Connect(m_lastHost, m_lastPort, true);
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
HandleCsMsgOnUpdate();
|
||||
CheckCsMsgTimeOut();
|
||||
TickHeartBeat();
|
||||
CheckHeatBeatTimeout();
|
||||
m_connectWatcher.Update();
|
||||
}
|
||||
|
||||
#region 超时检测
|
||||
|
||||
private readonly MainPack _timeOutPack = new MainPack { Returncode = ReturnCode.MsgTimeOut };
|
||||
private void CheckCsMsgTimeOut()
|
||||
{
|
||||
float nowTime = GameTime.time;
|
||||
for (int i = 0; i < CHECK_TIMEOUT_PERFRAME; i++)
|
||||
{
|
||||
m_dwLastCheckIndex = (m_dwLastCheckIndex + 1) % MAX_MSG_HANDLE;
|
||||
if (m_aMsgHandles[m_dwLastCheckIndex] != null)
|
||||
{
|
||||
if (m_fMsgRegTime[m_dwLastCheckIndex] + m_timeout < nowTime)
|
||||
{
|
||||
TLogger.LogError("msg timeout, resCmdID[{0}]", m_aMsgHandles[m_dwLastCheckIndex]);
|
||||
|
||||
NotifyTimeout(m_aMsgHandles[m_dwLastCheckIndex]);
|
||||
|
||||
RmvCheckCsMsg((int)m_dwLastCheckIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RmvCheckCsMsg(int index)
|
||||
{
|
||||
m_aMsgHandles[index] = null;
|
||||
m_fMsgRegTime[index] = 0;
|
||||
}
|
||||
|
||||
private void RegTimeOutHandle(uint actionCode, CsMsgDelegate resHandler)
|
||||
{
|
||||
uint hashIndex = actionCode % MAX_MSG_HANDLE;
|
||||
m_aMsgHandles[hashIndex] = resHandler;
|
||||
m_fMsgRegTime[hashIndex] = GameTime.time;
|
||||
}
|
||||
|
||||
protected void NotifyTimeout(CsMsgDelegate msgHandler)
|
||||
{
|
||||
msgHandler(_timeOutPack);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void HandleCsMsgOnUpdate()
|
||||
{
|
||||
if (cachelistHandle.Count <= 0 || queuepPacks.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
foreach (CsMsgDelegate handle in cachelistHandle.Dequeue())
|
||||
{
|
||||
handle(queuepPacks.Peek());
|
||||
}
|
||||
queuepPacks.Dequeue();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
TLogger.LogError(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
}
|
||||
|
||||
public override void Active()
|
||||
{
|
||||
base.Active();
|
||||
}
|
||||
|
||||
public override void Release()
|
||||
{
|
||||
base.Release();
|
||||
}
|
||||
|
||||
public bool IsStatusCanSendMsg()
|
||||
{
|
||||
if (m_status == GameClientStatus.StatusEnter)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float nowTime = GameTime.time;
|
||||
if (m_lastLogDisconnectErrTime + 5 < nowTime)
|
||||
{
|
||||
TLogger.LogError("GameClient not connected, send msg failed");
|
||||
m_lastLogDisconnectErrTime = nowTime;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置是否需要监控网络重连
|
||||
/// </summary>
|
||||
/// <param name="needWatch"></param>
|
||||
public void SetWatchReconnect(bool needWatch)
|
||||
{
|
||||
m_connectWatcher.Enable = needWatch;
|
||||
}
|
||||
|
||||
#region Ping
|
||||
/// <summary>
|
||||
/// ping值
|
||||
/// </summary>
|
||||
public int Ping
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsPingValid())
|
||||
{
|
||||
return m_ping / 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsPingValid()
|
||||
{
|
||||
if (IsNetworkOkAndLogined)
|
||||
{
|
||||
return m_ping >= 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetNetworkType
|
||||
public static CsNetworkType GetNetworkType()
|
||||
{
|
||||
CsNetworkType csNetType = CsNetworkType.CSNETWORK_UNKNOWN;
|
||||
NetworkReachability reachability = Application.internetReachability;
|
||||
switch (reachability)
|
||||
{
|
||||
case NetworkReachability.NotReachable:
|
||||
break;
|
||||
case NetworkReachability.ReachableViaLocalAreaNetwork:
|
||||
csNetType = CsNetworkType.CSNETWORK_WIFI;
|
||||
break;
|
||||
case NetworkReachability.ReachableViaCarrierDataNetwork:
|
||||
csNetType = CsNetworkType.CSNETWORK_3G;
|
||||
break;
|
||||
}
|
||||
return csNetType;
|
||||
}
|
||||
|
||||
public enum CsNetworkType
|
||||
{
|
||||
CSNETWORK_UNKNOWN = 0, /*未知类型*/
|
||||
CSNETWORK_WIFI = 1, /*Wifi类型*/
|
||||
CSNETWORK_3G = 2, /*3G类型*/
|
||||
CSNETWORK_2G = 3 /*2G类型*/
|
||||
};
|
||||
#endregion
|
||||
}
|
||||
}
|
48
TEngineHotUpdate/src/TEngineCore/Net/IDataCenterModule.cs
Normal file
48
TEngineHotUpdate/src/TEngineCore/Net/IDataCenterModule.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public interface IDataCenterModule
|
||||
{
|
||||
void Init();
|
||||
|
||||
void OnRoleLogout();
|
||||
|
||||
void OnUpdate();
|
||||
|
||||
void OnMainPlayerMapChange();
|
||||
}
|
||||
public class DataCenterModule<T> : IDataCenterModule where T : new()
|
||||
{
|
||||
private static T instance;
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (null == instance)
|
||||
{
|
||||
instance = new T();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnRoleLogout()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnUpdate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void OnMainPlayerMapChange()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
9
TEngineHotUpdate/src/TEngineCore/Net/NetEventId.cs
Normal file
9
TEngineHotUpdate/src/TEngineCore/Net/NetEventId.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
internal class NetEventId
|
||||
{
|
||||
public static int HeartBeat = StringId.StringToHash("NetEventId.HeartBeat");
|
||||
public static int ConnectTcp = StringId.StringToHash("NetEventId.ConnectTcp");
|
||||
public static int ConnectUdp = StringId.StringToHash("NetEventId.ConnectUdp");
|
||||
}
|
||||
}
|
52
TEngineHotUpdate/src/TEngineCore/Net/ProtoUtils.cs
Normal file
52
TEngineHotUpdate/src/TEngineCore/Net/ProtoUtils.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using System.IO;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace TEngineCore.Net
|
||||
{
|
||||
public class ProtoUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化 MainPack -> byte[]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="mainPack"></param>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static byte[] Serialize<T>(T mainPack) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var stream = new System.IO.MemoryStream())
|
||||
{
|
||||
ProtoBuf.Serializer.Serialize(stream, mainPack);
|
||||
return stream.ToArray();
|
||||
}
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
TLogger.LogError($"[Serialize] Error:{ex.Message}, {ex.Data["StackTrace"]}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="buffer"></param>
|
||||
/// <returns></returns>
|
||||
[CanBeNull]
|
||||
public static T DeSerialize<T>(byte[] buffer) where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
return ProtoBuf.Serializer.Deserialize(typeof(T), new System.IO.MemoryStream(buffer)) as T;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
TLogger.LogError(($"[DeSerialize] 错误:{ex.Message}, {ex.Data["StackTrace"]}"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user