using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TEngineProto;
using UnityEngine;
namespace TEngineCore.Net
{
///
/// 客户端状态
///
public enum GameClientStatus
{
StatusInit, //初始化
StatusReconnect, //重新连接
StatusClose, //断开连接
StatusConnect, //连接中
StatusEnter, //Login登录成功
}
public delegate void CsMsgDelegate(MainPack mainPack);
public class GameClient:TSingleton
{
#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;
///
/// GameClient状态
///
public GameClientStatus Status
{
get
{
return m_status;
}
set
{
m_status = value;
}
}
///
/// 最新连接错误的时间
///
private float m_lastLogDisconnectErrTime = 0f;
///
/// 最新的错误码
///
private int m_lastNetErrCode = 0;
///
/// 最近一次心跳的时间
///
private float m_lastHbTime = 0f;
///
/// 心跳间隔
///
private const float m_heartBeatDurTime = 15;
///
/// 连续心跳超时
///
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 发送网络消息
///
/// 发送消息包
///
///
///
public bool SendCsMsg(MainPack reqPkg)
{
if (!CheckPack(reqPkg))
{
return false;
}
return DoSendData(reqPkg);
}
///
/// 发送消息包并注册回调
///
///
///
///
///
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> m_mapCmdHandle = new Dictionary>();
///
/// 委托缓存堆栈
///
private Queue> cachelistHandle = new Queue>();
///
/// 消息包缓存堆栈
///
private Queue queuepPacks = new Queue();
///
/// 网络消息回调,非主线程
///
///
public void HandleResponse(MainPack pack)
{
lock (cachelistHandle)
{
List listHandle;
if (m_mapCmdHandle.TryGetValue((int)pack.Actioncode, out listHandle))
{
cachelistHandle.Enqueue(listHandle);
queuepPacks.Enqueue(pack);
}
}
}
///
/// Udp网络消息回调,非主线程
///
///
private void UdpHandleResponse(MainPack pack)
{
//Debug.Log(pack);
List listHandle;
if (m_mapCmdHandle.TryGetValue((int)pack.Actioncode, out listHandle))
{
foreach (CsMsgDelegate handle in listHandle)
{
handle(pack);
}
}
}
#endregion
#region 注册网络消息回调
///
/// 注册静态消息
///
///
///
public void RegActionHandle(int actionId, CsMsgDelegate msgDelegate)
{
List listHandle;
if (!m_mapCmdHandle.TryGetValue(actionId, out listHandle))
{
listHandle = new List();
m_mapCmdHandle[actionId] = listHandle;
}
if (listHandle != null)
{
if (listHandle.Contains(msgDelegate))
{
Debug.LogFormat("-------------repeat RegCmdHandle ActionCode:{0}-----------", (ActionCode)actionId);
}
listHandle.Add(msgDelegate);
}
}
///
/// 注册Udp静态消息
///
///
///
public void UdpRegActionHandle(int actionId, CsMsgDelegate msgDelegate)
{
List listHandle;
if (!m_mapCmdHandle.TryGetValue(actionId, out listHandle))
{
listHandle = new List();
m_mapCmdHandle[actionId] = listHandle;
}
if (listHandle != null)
{
if (listHandle.Contains(msgDelegate))
{
Debug.LogFormat("-------------repeat RegCmdHandle ActionCode:{0}-----------", (ActionCode)actionId);
}
listHandle.Add(msgDelegate);
}
}
///
/// 移除消息处理函数
///
///
///
public void RmvCmdHandle(int actionId, CsMsgDelegate msgDelegate)
{
List 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
///
/// 清理所有的网络消息
///
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;
if (m_aMsgHandles[hashIndex] != null)
{
NotifyTimeout(m_aMsgHandles[hashIndex]);
RmvCheckCsMsg((int)hashIndex);
}
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())
{
var pack = queuepPacks.Peek();
handle(pack);
UInt32 hashIndex = (uint)pack.Actioncode % MAX_MSG_HANDLE;
m_aMsgHandles[hashIndex](null);
RmvCheckCsMsg((int)hashIndex);
}
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;
}
///
/// 设置是否需要监控网络重连
///
///
public void SetWatchReconnect(bool needWatch)
{
m_connectWatcher.Enable = needWatch;
}
#region Ping
///
/// ping值
///
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
}
}