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