518 lines
18 KiB
C#
518 lines
18 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Net.Sockets;
|
||
using System.Text;
|
||
using System.Threading;
|
||
using Facebook.MiniJSON;
|
||
using ThinkingAnalytics;
|
||
|
||
namespace BF
|
||
{
|
||
internal partial class TCPChannel
|
||
{
|
||
private const uint Reconnect_Req_Group = 1068769299;
|
||
private const uint Reconnect_Rsp_Group = 1068771132;
|
||
private const byte Reconnect_Req_Cmd = 0;
|
||
private const int Reconnect_Rsp_OK = 0;
|
||
|
||
private enum SmartReconnectStatus
|
||
{
|
||
None,
|
||
Countdown,
|
||
TryReconnect,
|
||
}
|
||
|
||
private readonly object mSmartReconnectLock = new object();
|
||
|
||
/// <summary>
|
||
/// 缓存一定数量已经发送的消息,为重连成功后,重发发送失败的数据包
|
||
/// </summary>
|
||
private NetSafeQueue<NetOutgoingMessage> alreadySendMessagesQueue;
|
||
private Stack<NetOutgoingMessage> alreadySendExchangeStack;
|
||
|
||
/// <summary>
|
||
/// 缓存重连期间,所有的外部发送消息;重连成功后,继续发送
|
||
/// </summary>
|
||
private Queue<NetOutgoingMessage> waitingSendMessagesQueue;
|
||
|
||
/// <summary>
|
||
/// 记录尝试重连的次数
|
||
/// </summary>
|
||
private int tryReconnectCount = 0;
|
||
|
||
/// <summary>
|
||
/// 用于区分是外部调用的重连逻辑,还是内部自动重连逻辑
|
||
/// </summary>
|
||
private bool internalReconnectFlag = true;
|
||
|
||
#region Smart Reconnect
|
||
|
||
private float smartReconnectInterval = 5;
|
||
private double lastSmartStartTime = 0;
|
||
private SmartReconnectStatus smartStatus;
|
||
|
||
#endregion
|
||
|
||
private void InitializeReconnect()
|
||
{
|
||
int cacheSize = ownerConnection.configuration.AlreadySendMessageCacheCount;
|
||
alreadySendMessagesQueue = new NetSafeQueue<NetOutgoingMessage>(cacheSize);
|
||
alreadySendExchangeStack = new Stack<NetOutgoingMessage>();
|
||
waitingSendMessagesQueue = new Queue<NetOutgoingMessage>(cacheSize);
|
||
internalReconnectFlag = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 连接成功后,调用,重置重连信息
|
||
/// </summary>
|
||
private void ResetTryReconnectCount()
|
||
{
|
||
tryReconnectCount = 0;
|
||
internalReconnectFlag = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重连失败后,调用
|
||
/// </summary>
|
||
private void TryReconnectFailed()
|
||
{
|
||
if (connectContext != NetConnectContext.Reconnect)
|
||
{
|
||
return;
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 尝试重连,达到最大重连次数后,通知应用层
|
||
/// </summary>
|
||
private bool TryReconnect()
|
||
{
|
||
if (!ownerConnection.configuration.EnableSilenceReconnect)
|
||
{
|
||
SetConnectContext(NetConnectContext.Invalid);
|
||
Disconnect();
|
||
LogError(NetErrorCode.PeerDisconnect, "TryReconnect, Disable silence reconnect, connect disconnect !!!");
|
||
return false;
|
||
}
|
||
|
||
// 必须在玩家连接成功后,重连才有意义;否则直接失败!!
|
||
if (visibleStatus != NetConnectStatus.Connected)
|
||
{
|
||
SetConnectContext(NetConnectContext.Invalid);
|
||
Disconnect();
|
||
LogError(NetErrorCode.PeerDisconnect, "TryReconnect, visibleStatus is connected, connect disconnect !!!");
|
||
return false;
|
||
}
|
||
|
||
if (connectContext == NetConnectContext.Reconnect)
|
||
{
|
||
var now = NetTime.Now;
|
||
if (tryReconnectCount >= ownerConnection.configuration.AutoReconnectCount)
|
||
{
|
||
SetConnectContext(NetConnectContext.Invalid);
|
||
Disconnect();
|
||
ResetTryReconnectCount();
|
||
LogError(NetErrorCode.PeerDisconnect, "Try reconnect failed, channel disconnect.");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// BFLog.Log($"Begin try reconnect : {tryReconnectCount}");
|
||
|
||
PrepareReconnect();
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置状态,准备重连
|
||
/// </summary>
|
||
private void PrepareReconnect()
|
||
{
|
||
//进入重连上下文
|
||
SetConnectContext(NetConnectContext.Reconnect);
|
||
|
||
//断开现有连接
|
||
Disconnect();
|
||
|
||
//缓存已经在sendMessageQueue中的消息,并清空sendMessageQueue;等待重连成功后,再继续发送
|
||
CacheAlreadyInSendMessageQueueMessage(sendMessageQueue);
|
||
//清除上一个TCP连接的发送接受消息队列,不要继续发送接受数据;等待重连成功后再次进行收发数据
|
||
ClearSendReceiveMessageQueue();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重连,创建新连接
|
||
/// </summary>
|
||
private void TryConnectNewConnect()
|
||
{
|
||
if (connectContext != NetConnectContext.Reconnect || actualStatus != NetConnectStatus.Disconnected)
|
||
{
|
||
return;
|
||
}
|
||
|
||
tryReconnectCount += 1;
|
||
|
||
//开始新的连接
|
||
ConnectInternalAsync();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检测收到的消息是否是重连消息
|
||
/// </summary>
|
||
/// <param name="incomingMessage"></param>
|
||
/// <returns>true: 是重连消息;false: 其他消息</returns>
|
||
private bool FilterReconnectIncomingMessage(NetIncomingMessage incomingMessage)
|
||
{
|
||
if (connectContext != NetConnectContext.Reconnect)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (incomingMessage.Group != Reconnect_Rsp_Group)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
actualStatus = NetConnectStatus.Decoding2;
|
||
visibleStatus = NetConnectStatus.Decoding2;
|
||
incomingMessage.MessageType = NetIncomingMessageType.StatusChange;
|
||
incomingMessage.ConnectStatus = NetConnectStatus.Decoding2;
|
||
ReleaseMessage(incomingMessage);
|
||
decoding = false;
|
||
return true;
|
||
}
|
||
|
||
private bool FilterReconnectIncomingMessage2()
|
||
{
|
||
var isSuccess = DeserializeReconnectRsp(out var status, out var seqCs);
|
||
if (!isSuccess)
|
||
{
|
||
SetActualStatus(NetConnectStatus.Unauthorized);
|
||
return true;
|
||
}
|
||
|
||
if (status != Reconnect_Rsp_OK)
|
||
{
|
||
SetActualStatus(NetConnectStatus.Unauthorized);
|
||
return true;
|
||
}
|
||
|
||
//检查等待重新发送的消息
|
||
ResendMissingOutgoingMessage(seqCs);
|
||
|
||
// BFLog.Log($"End try reconnect : {tryReconnectCount}, success !!!");
|
||
|
||
ResetTryReconnectCount();
|
||
ResetSmartReconnect();
|
||
SetConnectContext(NetConnectContext.NewConnect);
|
||
SetActualStatus(NetConnectStatus.Connected);
|
||
ResetConnectTimeout();
|
||
ResetHeartBeatStatus();
|
||
// ResetOldSessionRc4Key();
|
||
|
||
ForceSendHeartBeatImmediately();
|
||
if(ownerConnection.UniqueIdentifier.CompareTo(BF.NetManager.MAIN_SOCKET_NAME) == 0)
|
||
{
|
||
BF.BFMain.Instance.NetMgr.decodeFinish = false;
|
||
}
|
||
else
|
||
{
|
||
BF.BFMain.Instance.NetMgr.decodeChatFinish = false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重连成功后,重新发送丢失的消息
|
||
/// </summary>
|
||
/// <param name="serverReceiveSeq">服务器接受到的最大 Seq</param>
|
||
private void ResendMissingOutgoingMessage(long serverReceiveSeq)
|
||
{
|
||
BFLog.Log($"ResendMissingOutgoingMessage : serverReceiveSeq = {serverReceiveSeq}, sendMessageQueue = {sendMessageQueue.Count}");
|
||
|
||
List<INetOutgoingMessage> pendingResendMessages = new List<INetOutgoingMessage>();
|
||
|
||
alreadySendExchangeStack.Clear();
|
||
while (alreadySendMessagesQueue.Count > 0)
|
||
{
|
||
alreadySendMessagesQueue.TryDequeue(out var outgoingMessage);
|
||
if (outgoingMessage.Seq <= serverReceiveSeq)
|
||
{
|
||
ownerConnection.Recycle(outgoingMessage);
|
||
continue;
|
||
}
|
||
// seq为0的一定是重连后发送的第一条loginReq,除此以外的消息添加到发送队列
|
||
if (outgoingMessage.Seq != 0 && outgoingMessage.Group != HeartBeat_Req_Group)
|
||
{
|
||
pendingResendMessages.Add(outgoingMessage);
|
||
}
|
||
}
|
||
|
||
while (waitingSendMessagesQueue.Count > 0)
|
||
{
|
||
var outgoingMessage = waitingSendMessagesQueue.Dequeue();
|
||
//添加到发送队列
|
||
pendingResendMessages.Add(outgoingMessage);
|
||
|
||
// BFLog.Log($"ResendMissingOutgoingMessage waitingSendMessagesQueue : {outgoingMessage.Group}, {outgoingMessage.Cmd}, {outgoingMessage.Seq}");
|
||
}
|
||
|
||
List<List<byte>> msgsPack = new List<List<byte>>();
|
||
foreach (NetOutgoingMessage message in pendingResendMessages)
|
||
{
|
||
// byte[] data = messageService.SerializeRaw(message);
|
||
// msgsPack.Add(data.ToList());
|
||
for (int index = 0; index < message.Data.Length; ++ index)
|
||
{
|
||
if (message.Data[index] == 0)
|
||
{
|
||
message.PackDataLength = index;
|
||
break;
|
||
}
|
||
}
|
||
byte[] data = new byte[message.PackDataLength];
|
||
Array.Copy(message.Data, data, message.PackDataLength);
|
||
|
||
BFLog.Log($"ResendMissingOutgoingMessage SerializeRaw : {message.Group}, {message.Cmd}, {message.Seq}");
|
||
var outgoingMsg = ownerConnection.CreateOutgoingMessage(NetOutgoingMessageType.UserData);
|
||
outgoingMsg.Encode(message.Group, message.Cmd, data);
|
||
outgoingMsg.Seq = message.Seq;
|
||
EnqueueRawSendMessage(outgoingMsg);
|
||
// BFLog.Log("==================================== message.PackDataLength = " + message.PackDataLength.ToString());
|
||
// int min = Math.Min(message.PackDataLength, 10);
|
||
// for (int index = 0; index < min; ++ index)
|
||
// {
|
||
// BFLog.Log("message.Data[{0}] = {1}", index, message.Data[index]);
|
||
// }
|
||
// BFLog.Log("==================================== message.Data.Length = " + message.Data.Length.ToString());
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送重连请求
|
||
/// </summary>
|
||
private void SendReconnectMessage()
|
||
{
|
||
if (connectContext != NetConnectContext.Reconnect)
|
||
{
|
||
return;
|
||
}
|
||
|
||
// var reconnectReq = SerializeReconnectReq();
|
||
var outgoingMsg = ownerConnection.CreateOutgoingMessage(NetOutgoingMessageType.UserData);
|
||
var bytes = BF.BFMain.Instance.NetMgr.GetLoginReqData();
|
||
outgoingMsg.Encode(Reconnect_Req_Group, Reconnect_Req_Cmd, bytes);
|
||
// BFLog.Log("=============== SendReconnectMessage");
|
||
EnqueueRawSendMessage(outgoingMsg);
|
||
}
|
||
|
||
/// <summary>
|
||
/// @TODO Temp
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private bool DeserializeReconnectRsp(out long status, out long seqCs)
|
||
{
|
||
// BFLog.Log("==================================== DeserializeReconnectRsp");
|
||
string pbData;
|
||
if(ownerConnection.UniqueIdentifier.CompareTo(BF.NetManager.MAIN_SOCKET_NAME) == 0)
|
||
|
||
{
|
||
pbData = BF.BFMain.Instance.NetMgr.GetDecodePbStr();
|
||
}
|
||
else
|
||
{
|
||
pbData = BF.BFMain.Instance.NetMgr.GetDecodeChatPbStr();
|
||
}
|
||
Dictionary<string, object> dict = Json.Deserialize(pbData) as Dictionary<string, object>;
|
||
// BFLog.Log("==================================== DeserializeReconnectRsp pbData = " + pbData);
|
||
// BFLog.Log("==================================== DeserializeReconnectRsp status = " + (long)dict["status"]);
|
||
// BFLog.Log("==================================== DeserializeReconnectRsp send_id = " + (long)dict["send_id"]);
|
||
|
||
status = (long)dict["status"];
|
||
seqCs = (long)dict["send_id"];
|
||
return status == 0;
|
||
}
|
||
|
||
#region Reconnect message sync
|
||
|
||
private bool FilterMissingDataSyncIncomingMessage(NetIncomingMessage incomingMessage)
|
||
{
|
||
if (incomingMessage.Group != 0 || incomingMessage.CMD != 3)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var isSuccess = DeserializeMissingMessageSync(incomingMessage.Data, out var missingReceiveData);
|
||
if (!isSuccess)
|
||
{
|
||
//重连消息解析失败,判定为重连不成功,不做任何其他处理,继续等待到连接超时;
|
||
return true;
|
||
}
|
||
|
||
//接收到补发消息,解析协议后,直接release
|
||
foreach (var receiveData in missingReceiveData)
|
||
{
|
||
var incomingMsg = ownerConnection.CreateIncomingMessage(NetIncomingMessageType.Data, receiveData);
|
||
messageService.DeserializeRaw(incomingMsg, receiveSeq);
|
||
ReleaseMessage(incomingMsg);
|
||
|
||
// BFLog.Log($"FilterMissingDataSyncIncomingMessage: {incomingMsg.Group}, {incomingMsg.CMD}, {incomingMsg.Seq}");
|
||
}
|
||
|
||
//reconnect success
|
||
ResetTryReconnectCount();
|
||
ResetSmartReconnect();
|
||
SetConnectContext(NetConnectContext.NewConnect);
|
||
SetActualStatus(NetConnectStatus.Connected);
|
||
ResetConnectTimeout();
|
||
ResetHeartBeatStatus();
|
||
ResetOldSessionRc4Key();
|
||
|
||
ForceSendHeartBeatImmediately();
|
||
|
||
return true;
|
||
}
|
||
|
||
private bool DeserializeMissingMessageSync(byte[] data, out List<byte[]> missingReceiveData)
|
||
{
|
||
missingReceiveData = new List<byte[]>();
|
||
|
||
string syncString = Encoding.UTF8.GetString(data);
|
||
Dictionary<string, object> syncObj = Json.Deserialize(syncString) as Dictionary<string, object>;
|
||
if (syncObj == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
if (syncObj.Count <= 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
List<object> packArray = (List<object>)syncObj["pack"];
|
||
foreach (var pack in packArray)
|
||
{
|
||
missingReceiveData.Add(Encoding.UTF8.GetBytes((string)pack));
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
private byte[] SerializeMissingMessageSync( List<List<byte>> missingMessageData)
|
||
{
|
||
Dictionary<string, object> syncMessageDic = new Dictionary<string, object>();
|
||
syncMessageDic["pack"] = missingMessageData;
|
||
|
||
string syncString = Json.Serialize(syncMessageDic);
|
||
byte[] syncData = Encoding.UTF8.GetBytes(syncString);
|
||
|
||
return syncData;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Smart Reconnect
|
||
|
||
private void HandleSocketError(SocketError socketError, string errorMessage)
|
||
{
|
||
switch (socketError)
|
||
{
|
||
case SocketError.Shutdown:
|
||
case SocketError.NetworkDown:
|
||
case SocketError.NetworkReset:
|
||
case SocketError.TimedOut:
|
||
case SocketError.ConnectionReset:
|
||
case SocketError.ConnectionAborted:
|
||
case SocketError.NetworkUnreachable:
|
||
// StartSmartReconnectCountdown();
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
LogError($"socketError = {(int)socketError}, {errorMessage}");
|
||
}
|
||
|
||
private void StartSmartReconnectCountdown()
|
||
{
|
||
// BFLog.Log($"StartSmartReconnectCountdown call...");
|
||
|
||
lock (mSmartReconnectLock)
|
||
{
|
||
if (!ownerConnection.configuration.EnableSilenceReconnect)
|
||
{
|
||
if (socket == null)
|
||
{
|
||
//已经调用Disconnect()断开了连接,不要再次抛出异常消息
|
||
// BFLog.Log($"Disconnect() already call...");
|
||
return;
|
||
}
|
||
|
||
SetConnectContext(NetConnectContext.Invalid);
|
||
Disconnect();
|
||
LogError(NetErrorCode.PeerDisconnect, "Disable silence reconnect, connect disconnect !!!");
|
||
return;
|
||
}
|
||
|
||
if (smartStatus == SmartReconnectStatus.Countdown)
|
||
{
|
||
return;
|
||
}
|
||
|
||
//进入重连状态
|
||
if (TryReconnect())
|
||
{
|
||
smartReconnectInterval =
|
||
ownerConnection.configuration.ReconnectBaseInterval * (tryReconnectCount + 1);
|
||
lastSmartStartTime = NetTime.Now;
|
||
SetSmartReconnectStatus(SmartReconnectStatus.Countdown);
|
||
|
||
// BFLog.Log($"SmartReconnectStatus.Countdown : {smartReconnectInterval}");
|
||
}
|
||
else
|
||
{
|
||
ResetSmartReconnect();
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重连成功后后,调用
|
||
/// </summary>
|
||
private void ResetSmartReconnect()
|
||
{
|
||
SetSmartReconnectStatus(SmartReconnectStatus.None);
|
||
smartReconnectInterval = ownerConnection.configuration.ReconnectBaseInterval;
|
||
}
|
||
|
||
private void SetSmartReconnectStatus(SmartReconnectStatus newStatus)
|
||
{
|
||
smartStatus = newStatus;
|
||
}
|
||
|
||
private void UpdateSmartReconnect()
|
||
{
|
||
if (smartStatus != SmartReconnectStatus.Countdown)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (NetTime.Now - lastSmartStartTime < smartReconnectInterval)
|
||
{
|
||
return;
|
||
}
|
||
|
||
SetSmartReconnectStatus(SmartReconnectStatus.TryReconnect);
|
||
|
||
TryConnectNewConnect();
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
} |