c1_unity/Assets/Scripts/Common/Network/TCPService/TCPChannel.Reconnect.cs
2025-11-03 10:59:33 +08:00

518 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}
}