2023-04-03 11:04:31 +08:00

421 lines
14 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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