421 lines
14 KiB
C#
421 lines
14 KiB
C#
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;
|
||
|
||
/// <summary>
|
||
/// 连接成功,交互密钥是获得session
|
||
/// </summary>
|
||
private long sessionId = 0;
|
||
/// <summary>
|
||
/// 重连时用到的旧sessionId
|
||
/// </summary>
|
||
private long oldSessionId = 0;
|
||
private string newRc4 = String.Empty;
|
||
private string oldRc4 = string.Empty;
|
||
|
||
private string rsaKey = string.Empty;
|
||
private string aesKey = string.Empty;
|
||
|
||
/// <summary>
|
||
/// 连接超时的参数
|
||
/// </summary>
|
||
private double startConnectTime = 0;
|
||
private bool isStartConnectTimeout = false;
|
||
|
||
private NetSafeQueue<NetIncomingMessage> 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<NetIncomingMessage>(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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设定当前连接上下文
|
||
/// </summary>
|
||
/// <param name="context"></param>
|
||
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");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
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
|
||
}
|
||
} |