using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; namespace BF { internal partial class TCPChannel { private const uint Login_Req_Group = 1070841461; private const string PUBLIC_KEY_MAIN = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC0pebTeJfJv6J08OHNRfi5HKc6UlUl44U+mx2xwdici4jMzXCuVq9lEdUCdqw6/IRz6+668L15vamjiIYwsoYpKGo4A6CziaqqjeTet6Qp2lGVKbFeP/9NdhxDPHTQigX3D17nE0hT+IPHo5JPCBIyRz78qVcl7H5/aYik5Hhh3QIDAQAB"; private const string PUBLIC_KEY_CHAT = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoI50YSc4x1L8Oj/xmVKWYGt2Ehkf3d+aGTeTwvDMIl32PMpYbK97MAYEf9l50Vu9wIZPKR+QP57eysN6ugYQ+yshP33TNTwvp3Hk7PY667vB2VWMc2fUhyPsH7R3Q60RYhT+5PUr2Rk0XQqLe0Ym4WyZoxomtA722vmlzV7SAMQIDAQAB"; private readonly object mSocketLock = new object(); private Socket socket = null; private IPEndPoint remoteEndPoint; public IPEndPoint RemoteEndPoint => remoteEndPoint; private SocketAsyncEventArgs innArgs; private SocketAsyncEventArgs outArgs; private NetConnectContext connectContext = NetConnectContext.Invalid; /// /// 连接成功,交互密钥是获得session /// private long sessionId = 0; /// /// 重连时用到的旧sessionId /// private long oldSessionId = 0; private string newRc4 = String.Empty; private string oldRc4 = string.Empty; private string rsaKey = string.Empty; private string aesKey = string.Empty; /// /// 连接超时的参数 /// private double startConnectTime = 0; private bool isStartConnectTimeout = false; private NetSafeQueue serverDisconnectMessageQueue; private void InitializeConnect(IPEndPoint remoteEndPoint) { if (remoteEndPoint.AddressFamily != AddressFamily.InterNetwork && remoteEndPoint.AddressFamily != AddressFamily.InterNetworkV6) { throw new NetException($"Invalid address family : {remoteEndPoint.AddressFamily}"); } SetConnectContext(NetConnectContext.NewConnect); this.remoteEndPoint = remoteEndPoint; ResetSocketAsyncEventArgs(); serverDisconnectMessageQueue = new NetSafeQueue(2); } #region newRc4 & oldRc4 private void SetSessionRc4Key(long newSessionId, string newKey) { //新旧rc4 交换逻辑,如果重连失败不改变,直到重连成功,重置为空,可以重新设置 if (string.IsNullOrEmpty(this.oldRc4)) { this.oldRc4 = this.newRc4; this.oldSessionId = this.sessionId; } this.newRc4 = newKey; this.sessionId = newSessionId; #if BF_DEBUG NetStatistics.SetSessionId(ownerConnection.configuration.UniqueIdentifier, newSessionId, oldSessionId); #endif } private void ResetOldSessionRc4Key() { this.oldRc4 = String.Empty; this.oldSessionId = 0; } #endregion private void ResetSocketAsyncEventArgs() { innArgs?.Dispose(); outArgs?.Dispose(); innArgs = null; outArgs = null; innArgs = new SocketAsyncEventArgs(); outArgs = new SocketAsyncEventArgs(); innArgs.Completed += OnComplete; outArgs.Completed += OnComplete; } /// /// 设定当前连接上下文 /// /// private void SetConnectContext(NetConnectContext context) { bool isChangeContext = (connectContext != NetConnectContext.Invalid) && connectContext != context; connectContext = context; //如果进入重连上下文,抛出正在重连,状态改变消息, if (isChangeContext && connectContext == NetConnectContext.Reconnect) { LogConnectStatusChange(NetConnectStatus.Reconnecting,"ConnectStatusChange"); } else if(isChangeContext && connectContext == NetConnectContext.NewConnect) { LogConnectStatusChange(NetConnectStatus.Connected,"ConnectStatusChange"); } } private void SetActualStatus(NetConnectStatus status) { actualStatus = status; SetVisibleStatus(status); } private void SetVisibleStatus(NetConnectStatus status) { // BFLog.Log("===== connectContext = " + connectContext.ToString()); // if (connectContext == NetConnectContext.Reconnect) // { // return; // } bool isChangeStatus = visibleStatus != status; visibleStatus = status; if (isChangeStatus) { LogConnectStatusChange(visibleStatus,"ConnectStatusChange"); } } /// /// /// private void Disconnect() { LogDebug("Disconnect"); lock (mSocketLock) { //close socket if (socket == null) { return; } try { socket.Shutdown(SocketShutdown.Both); } catch { } finally { socket.Close(); innArgs?.Dispose(); outArgs?.Dispose(); socket = null; } innArgs = null; outArgs = null; SetActualStatus(NetConnectStatus.Disconnected); ResetReceiveBuffer(); ResetSendStream(); ResetConnectTimeout(); LogDebug("Disconnect End"); } } private void ClearSendReceiveMessageQueue() { sendMessageQueue.Clear(); receiveMessageQueue.Clear(); } private void ConnectInternalAsync() { try { LogDebug("ConnectInternalAsync"); if (socket == null) { // BFLog.Log("ConnectInternalAsync socket"); ResetSocketAsyncEventArgs(); socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); socket.NoDelay = true; ResetSendStream(); StartConnectTimeout(); LogDebug("Create new socket"); } SetActualStatus(NetConnectStatus.Connecting); outArgs.RemoteEndPoint = remoteEndPoint; if (socket.ConnectAsync(outArgs)) { return; } OnConnectComplete(outArgs); } catch (SocketException socketException) { if (connectContext == NetConnectContext.NewConnect) { ownerConnection.ChannelConnectVerifyFailed(this); SetActualStatus(NetConnectStatus.Unconnected); LogError(NetErrorCode.ConnectFailed, $"Begin Connect socket error : socketError = {socketException.SocketErrorCode}, {socketException}"); } else { SetActualStatus(NetConnectStatus.Disconnected); HandleSocketError(socketException.SocketErrorCode, "Socket connect complete, Error!!"); } } catch (Exception exception) { if (connectContext == NetConnectContext.NewConnect) { ownerConnection.ChannelConnectVerifyFailed(this); SetActualStatus(NetConnectStatus.Unconnected); LogError(NetErrorCode.ConnectFailed, $"Begin Connect error : {exception.ToString()}"); } else { SetActualStatus(NetConnectStatus.Disconnected); HandleSocketError(SocketError.ConnectionAborted, "Socket connect complete, Error!!"); } } } private void OnConnectComplete(SocketAsyncEventArgs args) { if (socket == null) { return; } if (args.SocketError != SocketError.Success) { if (connectContext == NetConnectContext.Reconnect) { SetActualStatus(NetConnectStatus.Disconnected); HandleSocketError(args.SocketError, "Socket connect complete, Error!!"); } else if(connectContext == NetConnectContext.NewConnect) { ownerConnection.ChannelConnectVerifyFailed(this); Disconnect(); LogError(NetErrorCode.ConnectFailed, $"Socket connect complete, Error!! SocketError = {args.SocketError}"); } return; } args.RemoteEndPoint = null; SetActualStatus(NetConnectStatus.VerifyConnecting); ExchangeKeyWithServer(); StartReceiveAsync(); } private void OnDisconnectComplete(SocketAsyncEventArgs args) { if (args.SocketError == SocketError.ConnectionAborted) { return; } LogError((int)args.SocketError, "Socket disconnect complete, Error!"); } private void OnComplete(object sender, SocketAsyncEventArgs e) { switch (e.LastOperation) { case SocketAsyncOperation.Connect: OnConnectComplete(e); break; case SocketAsyncOperation.Receive: OnReceiveComplete(e); break; case SocketAsyncOperation.Send: OnSendComplete(e); break; case SocketAsyncOperation.Disconnect: OnDisconnectComplete(e); break; default: throw new NotImplementedException($"socket error: {e.LastOperation}"); } } private void ExchangeKeyWithServer() { rsaKey = NetUtility.GetRandomString(16); //发送交换密钥消息 var outgoingMessage = ownerConnection.CreateOutgoingMessage(NetOutgoingMessageType.UserData); byte[] content; if(ownerConnection.UniqueIdentifier.CompareTo(BF.NetManager.MAIN_SOCKET_NAME) == 0) { content = NetRsaEncrypt.RsaEncrypt(rsaKey, PUBLIC_KEY_MAIN); } else { content = NetRsaEncrypt.RsaEncrypt(rsaKey, PUBLIC_KEY_CHAT); } outgoingMessage.Encode(0, 0, content); EnqueueRawSendMessage(outgoingMessage); } #region connect time out task private void StartConnectTimeout() { startConnectTime = NetTime.Now; isStartConnectTimeout = true; } private void ResetConnectTimeout() { startConnectTime = NetTime.Now; isStartConnectTimeout = false; } private void ConnectTimeout() { LogDebug($"ConnectTimeout..."); if (connectContext == NetConnectContext.NewConnect) { ownerConnection.ChannelConnectVerifyFailed(this); ResetConnectTimeout(); Disconnect(); LogError(NetErrorCode.PeerDisconnect, "Connect timeout, channel disconnect."); } else { ResetConnectTimeout(); StartSmartReconnectCountdown(); } } private void UpdateTimer() { if (!isStartConnectTimeout) { return; } var duration = ownerConnection.configuration.ConnectTimeout; if (connectContext == NetConnectContext.Reconnect) { duration = ownerConnection.configuration.ReconnectTimeoutTime; } if (NetTime.Now - startConnectTime > duration) { ConnectTimeout(); } } #endregion #region Server Disconnect private void CreateServerDisconnectMessage() { var incomingMessage = ownerConnection.CreateLoggingMessage(NetIncomingMessageType.ErrorMessage, "Server disconnect."); incomingMessage.ErrorCode = NetErrorCode.PeerDisconnect; serverDisconnectMessageQueue.Enqueue(incomingMessage); } private void UpdateFilterServerDisconnectMessage() { while (serverDisconnectMessageQueue.Count > 0) { serverDisconnectMessageQueue.TryDequeue(out var releaseMessage); if (releaseMessage.ErrorCode == NetErrorCode.PeerDisconnect) { SetConnectContext(NetConnectContext.Invalid); Disconnect(); } ReleaseMessage(releaseMessage); break; } } #endregion } }