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(); /// /// 缓存一定数量已经发送的消息,为重连成功后,重发发送失败的数据包 /// private NetSafeQueue alreadySendMessagesQueue; private Stack alreadySendExchangeStack; /// /// 缓存重连期间,所有的外部发送消息;重连成功后,继续发送 /// private Queue waitingSendMessagesQueue; /// /// 记录尝试重连的次数 /// private int tryReconnectCount = 0; /// /// 用于区分是外部调用的重连逻辑,还是内部自动重连逻辑 /// 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(cacheSize); alreadySendExchangeStack = new Stack(); waitingSendMessagesQueue = new Queue(cacheSize); internalReconnectFlag = true; } /// /// 连接成功后,调用,重置重连信息 /// private void ResetTryReconnectCount() { tryReconnectCount = 0; internalReconnectFlag = true; } /// /// 重连失败后,调用 /// private void TryReconnectFailed() { if (connectContext != NetConnectContext.Reconnect) { return; } } /// /// 尝试重连,达到最大重连次数后,通知应用层 /// 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; } /// /// 重置状态,准备重连 /// private void PrepareReconnect() { //进入重连上下文 SetConnectContext(NetConnectContext.Reconnect); //断开现有连接 Disconnect(); //缓存已经在sendMessageQueue中的消息,并清空sendMessageQueue;等待重连成功后,再继续发送 CacheAlreadyInSendMessageQueueMessage(sendMessageQueue); //清除上一个TCP连接的发送接受消息队列,不要继续发送接受数据;等待重连成功后再次进行收发数据 ClearSendReceiveMessageQueue(); } /// /// 重连,创建新连接 /// private void TryConnectNewConnect() { if (connectContext != NetConnectContext.Reconnect || actualStatus != NetConnectStatus.Disconnected) { return; } tryReconnectCount += 1; //开始新的连接 ConnectInternalAsync(); } /// /// 检测收到的消息是否是重连消息 /// /// /// true: 是重连消息;false: 其他消息 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(); BF.BFMain.Instance.NetMgr.decodeFinish = false; return true; } /// /// 重连成功后,重新发送丢失的消息 /// /// 服务器接受到的最大 Seq private void ResendMissingOutgoingMessage(long serverReceiveSeq) { BFLog.Log($"ResendMissingOutgoingMessage : serverReceiveSeq = {serverReceiveSeq}, sendMessageQueue = {sendMessageQueue.Count}"); List pendingResendMessages = new List(); 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> msgsPack = new List>(); 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()); } } /// /// 发送重连请求 /// 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); } /// /// @TODO Temp /// /// private bool DeserializeReconnectRsp(out long status, out long seqCs) { // BFLog.Log("==================================== DeserializeReconnectRsp"); string pbData = BF.BFMain.Instance.NetMgr.GetDecodePbStr(); Dictionary dict = Json.Deserialize(pbData) as Dictionary; // 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 missingReceiveData) { missingReceiveData = new List(); string syncString = Encoding.UTF8.GetString(data); Dictionary syncObj = Json.Deserialize(syncString) as Dictionary; if (syncObj == null) { return false; } if (syncObj.Count <= 0) { return true; } List packArray = (List)syncObj["pack"]; foreach (var pack in packArray) { missingReceiveData.Add(Encoding.UTF8.GetBytes((string)pack)); } return true; } private byte[] SerializeMissingMessageSync( List> missingMessageData) { Dictionary syncMessageDic = new Dictionary(); 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(); } } } /// /// 重连成功后后,调用 /// 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 } }