using System; using System.Collections.Generic; using System.IO; using System.Net.Sockets; using System.Text; using System.Threading; using Facebook.MiniJSON; using System.Security.Cryptography; namespace BF { internal partial class TCPChannel { //incoming message private readonly NetSafeQueue releasedIncomingMessage = new NetSafeQueue(10); private readonly NetSafeQueue receiveMessageQueue = new NetSafeQueue(10); private CircularBuffer receiveBuffer; private int receiveBodyLength = 0; //receive optimize private NetAesEncrypt aesEncrypt; private NetAesEncrypt aesDecrypt; /// /// last receive server message seq /// private uint receiveSeq = 0; private void InitializeReceive() { receiveSeq = 0; receiveBuffer = new CircularBuffer(ownerConnection.configuration.ReceiveBufferCapacity); } private void ResetReceiveBuffer() { receiveBodyLength = 0; receiveBuffer.Reset(); } private bool IsCanReceiveData() { return actualStatus == NetConnectStatus.VerifyConnecting || actualStatus == NetConnectStatus.Connected || actualStatus == NetConnectStatus.Reconnecting || actualStatus == NetConnectStatus.Authing; } private int GetReceiveBufferAvailableSize() { int size = 0; switch (receiveBuffer.EState) { case CircularBuffer.State.WriteAhead: size = receiveBuffer.Size - receiveBuffer.WritePosition; break; case CircularBuffer.State.ReadAhead: size = receiveBuffer.ReadPosition - receiveBuffer.WritePosition; break; case CircularBuffer.State.ReadEqualWrite: size = receiveBuffer.Size - receiveBuffer.WritePosition; break; case CircularBuffer.State.WriteEqualRead: CircularBuffer newReceiveBuffer = new CircularBuffer(2 * receiveBuffer.Size); int length = receiveBuffer.HowManyCanRead; if (length > 0) { newReceiveBuffer.Write(receiveBuffer.Read(length)); } receiveBuffer.Dispose(); receiveBuffer = newReceiveBuffer; size = receiveBuffer.Size - length; break; default: throw new NotImplementedException(); } return size; } private void StartReceiveAsync() { if (!IsCanReceiveData()) { return; } int size = GetReceiveBufferAvailableSize(); if (size <= 0) { LogError(NetErrorCode.ReceiveBufferError, "Receive buffer not enough. Is full ?"); return; } try { innArgs.SocketFlags = SocketFlags.None; innArgs.SetBuffer(receiveBuffer.Array, receiveBuffer.WritePosition, size); } catch (Exception exception) { throw new NetException(exception.ToString()); } try { if (socket.ReceiveAsync(innArgs)) { return; } OnReceiveComplete(innArgs); } catch (SocketException exception) { HandleSocketError(exception.SocketErrorCode, $"Begin receive socket error : {exception}"); } catch (Exception exception) { LogError(NetErrorCode.ExceptionError,$"Begin receive error : {exception}"); } } private void OnReceiveComplete(SocketAsyncEventArgs args) { if (socket == null) { return; } if (!IsCanReceiveData()) { return; } if (args.SocketError != SocketError.Success) { //LogError((int)args.SocketError, "Socket receive complete, Error!"); HandleSocketError(args.SocketError, "Socket receive complete, Error!"); return; } //BytesTransferred == 0 ,The remote end has closed the connection if (args.BytesTransferred <= 0) { CreateServerDisconnectMessage(); return; } receiveBuffer.MoveWritePosition(args.BytesTransferred); if (!ProcessReceiveBuffer()) { //data error LogError(NetErrorCode.DataParseError, "Process receive buffer error."); return; } StartReceiveAsync(); } private bool ProcessReceiveBuffer() { bool success = true; while (true) { if (receiveBodyLength > 0) { if (receiveBuffer.HowManyCanRead < receiveBodyLength) { break; } var data = receiveBuffer.Read(receiveBodyLength); var incomingMsg = ownerConnection.CreateIncomingMessage(NetIncomingMessageType.Data, data); EnqueueReceiveMessage(incomingMsg); receiveBodyLength = 0; } else { receiveBodyLength = messageService.ReadBodyLength(receiveBuffer); if (receiveBodyLength == 0) { break; } else if (receiveBodyLength < 0) { success = false; break; } } } return success; } private void ReceiveUpdate() { while (receiveMessageQueue.Count > 0) { NetIncomingMessage incomingMessage = null; bool result = receiveMessageQueue.TryDequeue(out incomingMessage); if (!result) { NetException.Assert(false, "Receive message queue TryDequeue error."); LogWarning("Receive message queue TryDequeue error."); break; } // BFLog.Log("ReceiveUpdate Length = " + incomingMessage.Data.Length.ToString()); //首次回包,特殊处理 if (actualStatus == NetConnectStatus.VerifyConnecting) { HandleFirstReceiveMessage(incomingMessage); return; } result = messageService.Deserialize(incomingMessage, incomingMessage.Seq); if (!result) { //deserialize error LogError(NetErrorCode.DataParseError,"Deserialize data error."); break; } // BFLog.Log($"Receive Message group = {incomingMessage.Group}, cmd = {incomingMessage.CMD}, seq = {incomingMessage.Seq}"); //心跳包,特殊处理 if (FilterHeartBeatIncomingMessage(incomingMessage)) { return; } //auth消息回包,特殊处理 if (FilterAuthIncomingMessage(incomingMessage)) { return; } //重连消息回包,特殊处理 if (FilterReconnectIncomingMessage(incomingMessage)) { return; } //处理重连成功后,接受丢包的补包消息 if (FilterMissingDataSyncIncomingMessage(incomingMessage)) { return; } if (HandleKickNtfMessage(incomingMessage)) { return; } if (HandleOtherDeviceLoginMessage(incomingMessage)) { return; } //update last success receive message seq //group == 0 不需要接收seq if (incomingMessage.Group != 0) { receiveSeq = incomingMessage.Seq; } ReleaseMessage(incomingMessage); #if BF_DEBUG NetStatistics.SetReceiveDataSeq(ownerConnection.configuration.UniqueIdentifier, receiveSeq); #endif } UpdateFilterServerDisconnectMessage(); } /// /// 其他人登陆同角色时,老的连接会收到挤号消息并主动断开连接。客户端在收到该消息后应提示玩家并返回登陆界面 /// /// /// private bool HandleOtherDeviceLoginMessage(NetIncomingMessage incomingMessage) { if (incomingMessage.Group != 0 || incomingMessage.CMD != 8) { return false; } SetConnectContext(NetConnectContext.Invalid); Disconnect(); LogError(NetErrorCode.OtherDeviceLogin, $"Other Device login.."); return true; } /// /// 处理玩家被服务器强制踢下线消息;收到消息后,应立刻断开TCP连接 /// @TODO logic /// /// private bool HandleKickNtfMessage(NetIncomingMessage incomingMessage) { if (incomingMessage.Group != 0 || incomingMessage.CMD != 9) { return false; } var reason = "KickNtf"; string str = Encoding.UTF8.GetString(incomingMessage.Data); Dictionary kickNtfRsp = Json.Deserialize(str) as Dictionary; if (kickNtfRsp != null) { reason = (string)kickNtfRsp["reason"]; } SetConnectContext(NetConnectContext.Invalid); Disconnect(); LogError(NetErrorCode.ServerKickNtfClient, $"{reason}"); return true; } /// /// 每次Socket连接成功后,接受到第一个包,做如下处理: /// 1. 根据收到的包,进行密钥交换; /// 2. 如果在重连状态,发送一个重连消息,收到重连成功消息后,进入连接成功状态;否则,在正常连接状态,直接连接成功 /// /// private void HandleFirstReceiveMessage(NetIncomingMessage incomingMessage) { if (connectContext == NetConnectContext.Reconnect) { SetActualStatus(NetConnectStatus.Reconnecting); ExchangeAESKeyWithServer(incomingMessage); SendAuthMessage(); } else { ownerConnection.ChannelConnectVerifySuccess(this); SetActualStatus(NetConnectStatus.Authing); ExchangeAESKeyWithServer(incomingMessage); SendAuthMessage(); } } /// /// 首次回包,默认是交换Rc4密钥 /// /// private void ExchangeAESKeyWithServer(NetIncomingMessage incomingMessage) { byte[] aesKey = new byte[incomingMessage.Data.Length]; byte[] aesKeyA = new byte[incomingMessage.Data.Length]; byte[] aesKeyB = new byte[incomingMessage.Data.Length]; byte[] llaveB = System.Text.Encoding.ASCII.GetBytes(rsaKey); NetAesEncrypt tmpAesEncrypt = new NetAesEncrypt(llaveB); tmpAesEncrypt.aesKey = System.Text.Encoding.ASCII.GetBytes(rsaKey); tmpAesEncrypt.Decrypt(incomingMessage.Data, 0, incomingMessage.Data.Length, aesKey, 0); Array.Copy(aesKey, aesKeyA, aesKey.Length); Array.Copy(aesKey, aesKeyB, aesKey.Length); aesEncrypt = new NetAesEncrypt(aesKeyA); aesEncrypt.aesKey = aesKey; messageService.SetEncrypt(aesEncrypt); aesDecrypt = new NetAesEncrypt(aesKeyB); aesDecrypt.aesKey = aesKey; messageService.SetDecrypt(aesDecrypt); } private void EnqueueReceiveMessage(NetIncomingMessage incomingMessage) { NetException.Assert(incomingMessage.MessageType != NetIncomingMessageType.Error); #if BF_DEBUG string connectId = ownerConnection.configuration.UniqueIdentifier; var result = NetStatistics.CheckEnableReceiveData(connectId); if (result) { receiveMessageQueue.Enqueue(incomingMessage); } else { NetStatistics.EnqueueCacheReceiveMessage(connectId, incomingMessage); } #else receiveMessageQueue.Enqueue(incomingMessage); #endif } private void ReleaseMessage(NetIncomingMessage message) { NetException.Assert(message.MessageType != NetIncomingMessageType.Error); releasedIncomingMessage.Enqueue(message); } } }