287 lines
9.3 KiB
C#
287 lines
9.3 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
|
||
using System.Text;
|
||
using Thousandto.Core.Base;
|
||
using Thousandto.Code.Center;
|
||
using Thousandto.Core.Framework;
|
||
using UnityEngine;
|
||
|
||
namespace Thousandto.Code.Logic
|
||
{
|
||
/// <summary>
|
||
/// 心跳系统,
|
||
/// 这里主要处理与服务器的定时信息发送
|
||
/// </summary>
|
||
public class HeartSystem
|
||
{
|
||
//10秒 再加500毫秒的误差,避免误杀,这里的单位是毫米
|
||
private const float CN_SEND_HEART_INTERVAL = 10.5f * 1000;
|
||
//20秒,这里等待返回超时的话,再次发送消息的间隔时间,单位毫秒
|
||
private const float CN_SEND_TIMEOUT_HEART_INTERVAL = 20 * 1000;
|
||
//等待第一次接受数据的超时时间,2分钟
|
||
private const float CN_WAIT_FIRST_RECIEVED_TIMEOUT = 2 * 60 * 1000;
|
||
|
||
//做线程锁处理
|
||
private static object _lockObj;
|
||
|
||
//统计连续发送心跳的个数
|
||
private int _sendHeartMsgCounter = 0;
|
||
//消息延迟次数
|
||
private int _msgDelayTimes = 0;
|
||
|
||
//是否允许心跳线程
|
||
private bool _enable = false;
|
||
|
||
//等待服务器应答
|
||
private bool _waitServerRespond = false;
|
||
|
||
//普通心跳消息计时器
|
||
private float _normalHeartTimer = 0;
|
||
|
||
//发送消息时间
|
||
private DateTime _sendTime = DateTime.Now;
|
||
|
||
//接收响应时间
|
||
private DateTime _receiveTime = DateTime.Now;
|
||
|
||
//启动时间
|
||
private DateTime _enableTime = DateTime.Now;
|
||
|
||
//服务器时间
|
||
private double _serverTime = 0f;
|
||
//切换到后台心跳计时器
|
||
private float _inBackgroundTimer = 0f;
|
||
//切换到后台的系统Tick
|
||
int _startBackgroundTick = 0;
|
||
//服务器时区偏移秒数
|
||
private int _serverZoneOffset = 0;
|
||
|
||
//服务器同步时间(UTC时间)
|
||
private double _serverUTCTime = -1;
|
||
//服务器同步时间(UTC时间)
|
||
public double ServerTime
|
||
{
|
||
get
|
||
{
|
||
return _serverUTCTime;
|
||
}
|
||
}
|
||
|
||
|
||
//服务器时区偏移秒数
|
||
public int ServerZoneOffset
|
||
{
|
||
get
|
||
{
|
||
return _serverZoneOffset;
|
||
}
|
||
}
|
||
|
||
//服务器时区时间
|
||
public double ServerZoneTime
|
||
{
|
||
get
|
||
{
|
||
return ServerTime + _serverZoneOffset;
|
||
}
|
||
}
|
||
|
||
//网络延迟ping值
|
||
public int NetPingValue { get; set; }
|
||
|
||
//是否已经接受到心跳数据
|
||
public bool IsReceiveHeart { get; private set; }
|
||
|
||
//测试停止心跳
|
||
public bool TestStopReceiveHeart { get; set; }
|
||
|
||
public HeartSystem()
|
||
{
|
||
_lockObj = new object();
|
||
IsReceiveHeart = false;
|
||
TestStopReceiveHeart = false;
|
||
}
|
||
|
||
public void SetServerZoneOffset(int zoneOffset)
|
||
{
|
||
_serverZoneOffset = zoneOffset;
|
||
}
|
||
|
||
//网络线程的心跳处理
|
||
public void OnNetworkThreadTick(float deltaByms)
|
||
{
|
||
if (_enable == false) return;
|
||
|
||
if (IsReceiveHeart)
|
||
{//当服务器发送过心跳给客户端就正常心跳逻辑
|
||
//根据服务器是否返回,来判断客户端的时间间隔是否正确.
|
||
if (_waitServerRespond)
|
||
{
|
||
NetPingValue += (int)(deltaByms);
|
||
|
||
//这里做超时判断,这里的判断时通过发送时间来判断
|
||
var elapse = (DateTime.Now - _sendTime).TotalMilliseconds;
|
||
if (elapse > CN_SEND_TIMEOUT_HEART_INTERVAL)
|
||
{
|
||
SendReqHeartMsg();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//如果已经接到服务器反馈的消息,那么这里就准备下次的心跳发送
|
||
//这里是通过接受消息的时间来判断
|
||
var elapse = (DateTime.Now - _receiveTime).TotalMilliseconds;
|
||
if (elapse >= CN_SEND_HEART_INTERVAL || elapse < 0)
|
||
{
|
||
SendReqHeartMsg();
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
//当服务器一直没有发送心跳消息给客户端,那么就需要这里做一下驱动才可以.
|
||
if ((DateTime.Now - _enableTime).TotalMilliseconds > CN_WAIT_FIRST_RECIEVED_TIMEOUT)
|
||
{
|
||
_enableTime = DateTime.Now;
|
||
SendReqHeartMsg();
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//通过Unity的心跳来处理
|
||
public void Update(float delta)
|
||
{
|
||
//计算当前帧的服务器时间
|
||
_serverUTCTime = _serverTime + (DateTime.Now - _receiveTime).TotalSeconds;
|
||
|
||
if (_enable)
|
||
{
|
||
//这里判断是否需要重连
|
||
CheckNeedReconnect();
|
||
}
|
||
}
|
||
|
||
//开启心跳处理
|
||
public void EnabelHeartMsg(bool enable)
|
||
{
|
||
Debug.Log("心跳设置:"+enable);
|
||
_enable = enable;
|
||
_sendHeartMsgCounter = 0;
|
||
_msgDelayTimes = 0;
|
||
_enableTime = DateTime.Now;
|
||
if (enable)
|
||
{
|
||
GameCenter.Networker.RecordMsgTagTime = Time.realtimeSinceStartup;
|
||
}
|
||
}
|
||
|
||
public void Uninitialize()
|
||
{
|
||
IsReceiveHeart = false;
|
||
EnabelHeartMsg(false);
|
||
}
|
||
|
||
//打断网络,启动重连
|
||
private bool CheckNeedReconnect()
|
||
{
|
||
//客户端发送了3次心跳都没收到服务器返回,则主动断掉socket
|
||
if (_msgDelayTimes > 3)
|
||
{
|
||
//开启断线重连的话就复位计数
|
||
_msgDelayTimes = 0;
|
||
|
||
if ( !Application.isEditor)
|
||
{//编辑器状态下不进入断线重连状态
|
||
//游戏主场景才做主动断线操作
|
||
if (GameCenter.GameStateSystem != null && GameCenter.GameStateSystem.IsCurState((int)GameStateId.World))
|
||
{
|
||
//停止心跳监听
|
||
_enable = false;
|
||
Debug.LogError("服务器心跳无反馈,启动断线重连机制.");
|
||
GameCenter.Networker.Disconnect();
|
||
GameCenter.ReconnectSystem.Reconnect();
|
||
return true;
|
||
}
|
||
}
|
||
else {
|
||
|
||
Debug.LogError("判断重连,但因为是编辑器下所以不进行重连处理.");
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
#region//网络通讯
|
||
//往服务器发送请求退出的消息
|
||
public void SendReqQuit()
|
||
{
|
||
MSG_Register.ReqQuit req = new MSG_Register.ReqQuit();
|
||
req.Send();
|
||
IsReceiveHeart = false;
|
||
_sendHeartMsgCounter = 0;
|
||
}
|
||
|
||
//发送一个特殊心跳信息,让服务器重新倒计时
|
||
public void SendReqReallyHeartMsg()
|
||
{
|
||
MSG_heart.ReqReallyHeart normalHeartReq = new MSG_heart.ReqReallyHeart();
|
||
normalHeartReq.Send();
|
||
}
|
||
|
||
//发送普通心跳同步信息
|
||
public void SendReqHeartMsg()
|
||
{
|
||
|
||
MSG_heart.ReqHeart req = new MSG_heart.ReqHeart();
|
||
req.time = TimeUtils.GetNow();
|
||
req.Send();
|
||
|
||
|
||
//记录最后一次发送时间
|
||
_sendTime = DateTime.Now;
|
||
//记录发送次数
|
||
_sendHeartMsgCounter++;
|
||
//等待服务器应答
|
||
_waitServerRespond = true;
|
||
//消息延迟的次数
|
||
_msgDelayTimes++;
|
||
//Debug.LogError("SendReqHeartMsg::" + _sendTime.ToFileTimeUtc() + ";;" + _msgDelayTimes);
|
||
}
|
||
|
||
|
||
|
||
//收到服务器下发的用于检测加速器的消息,这个时候开始进行倒计时
|
||
public void OnHearMsgReceive(MSG_heart.ResHeart result)
|
||
{
|
||
if (!TestStopReceiveHeart)
|
||
{
|
||
//当接受到回复消息,那么延迟次数就设置为0
|
||
_msgDelayTimes = 0;
|
||
|
||
//这里设置不用等待了
|
||
_waitServerRespond = false;
|
||
|
||
_serverTime = result.serverTime;
|
||
|
||
_receiveTime = DateTime.Now;
|
||
|
||
if (_sendHeartMsgCounter == 0)
|
||
{
|
||
//第一次获取,这里给一个随机值
|
||
NetPingValue = new System.Random().Next(1, 50);
|
||
}
|
||
else
|
||
{
|
||
//从发送开始到接收为止,计算延迟
|
||
NetPingValue = (int)(_receiveTime - _sendTime).TotalMilliseconds;
|
||
//Debug.LogError("OnHearMsgRecie:::"+ _sendTime.ToFileTimeUtc()+ ";;" + _receiveTime.ToFileTimeUtc()+ ";;;" + NetPingValue);
|
||
}
|
||
_serverUTCTime = _serverTime + (DateTime.Now - _receiveTime).TotalSeconds;
|
||
IsReceiveHeart = true;
|
||
}
|
||
//Debug.LogError("OnHearMsgReceive(MSG_heart.ResHeart result)::" + _receiveTime);
|
||
}
|
||
#endregion
|
||
}
|
||
}
|