This commit is contained in:
2026-07-19 23:06:54 +03:00
parent cf7b2f92a0
commit 6b90ac13e8
+90 -21
View File
@@ -38,9 +38,9 @@ public class TcpSocket : ISocket
public INetworkReceiver<ISocket> Receiver { get; set; } public INetworkReceiver<ISocket> Receiver { get; set; }
public event DestroyedEvent OnDestroy; public event DestroyedEvent OnDestroy;
private readonly Socket sock; private Socket sock;
private readonly byte[] receiveBuffer; private byte[] receiveBuffer;
private readonly NetworkBuffer receiveNetworkBuffer = new NetworkBuffer(); private NetworkBuffer receiveNetworkBuffer = new NetworkBuffer();
private readonly object stateLock = new object(); private readonly object stateLock = new object();
private readonly object sendLock = new object(); private readonly object sendLock = new object();
@@ -142,13 +142,19 @@ public class TcpSocket : ISocket
public AsyncReply<bool> Connect(string hostname, ushort port) public AsyncReply<bool> Connect(string hostname, ushort port)
{ {
var rt = new AsyncReply<bool>(); var rt = new AsyncReply<bool>();
Socket connectingSocket;
lock (stateLock) lock (stateLock)
{ {
if (destroyed || state == SocketState.Closed) if (destroyed || state == SocketState.Closed)
{ {
rt.Trigger(false); if (destroyed)
return rt; {
rt.Trigger(false);
return rt;
}
ResetForReconnect_NoLock();
} }
if (state == SocketState.Established) if (state == SocketState.Established)
@@ -157,23 +163,30 @@ public class TcpSocket : ISocket
return rt; return rt;
} }
if (state == SocketState.Connecting)
{
rt.Trigger(false);
return rt;
}
state = SocketState.Connecting; state = SocketState.Connecting;
connectingSocket = sock;
} }
try try
{ {
var args = new SocketAsyncEventArgs(); var args = new SocketAsyncEventArgs();
args.RemoteEndPoint = new DnsEndPoint(hostname, port); args.RemoteEndPoint = new DnsEndPoint(hostname, port);
args.UserToken = rt; args.UserToken = new KeyValuePair<Socket, AsyncReply<bool>>(connectingSocket, rt);
args.Completed += ConnectCompleted; args.Completed += ConnectCompleted;
bool pending = sock.ConnectAsync(args); bool pending = connectingSocket.ConnectAsync(args);
if (!pending) if (!pending)
ProcessConnect(args); ProcessConnect(args);
} }
catch (Exception ex) catch (Exception ex)
{ {
SafeClose(ex, false); SafeClose(ex, false, connectingSocket);
rt.TriggerError(ex); rt.TriggerError(ex);
} }
@@ -187,15 +200,27 @@ public class TcpSocket : ISocket
private void ProcessConnect(SocketAsyncEventArgs e) private void ProcessConnect(SocketAsyncEventArgs e)
{ {
var rt = e.UserToken as AsyncReply<bool>; var context = (KeyValuePair<Socket, AsyncReply<bool>>)e.UserToken;
var connectingSocket = context.Key;
var rt = context.Value;
try try
{ {
lock (stateLock)
{
if (!ReferenceEquals(sock, connectingSocket))
{
rt.Trigger(false);
return;
}
}
if (e.SocketError == SocketError.Success) if (e.SocketError == SocketError.Success)
{ {
lock (stateLock) lock (stateLock)
{ {
if (destroyed || state == SocketState.Closed) if (destroyed || state == SocketState.Closed ||
!ReferenceEquals(sock, connectingSocket))
{ {
rt?.Trigger(false); rt?.Trigger(false);
return; return;
@@ -211,7 +236,7 @@ public class TcpSocket : ISocket
else else
{ {
var ex = new SocketException((int)e.SocketError); var ex = new SocketException((int)e.SocketError);
SafeClose(ex, false); SafeClose(ex, false, connectingSocket);
rt?.TriggerError(ex); rt?.TriggerError(ex);
} }
} }
@@ -445,6 +470,7 @@ public class TcpSocket : ISocket
receiveArgs = new SocketAsyncEventArgs(); receiveArgs = new SocketAsyncEventArgs();
receiveArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); receiveArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length);
receiveArgs.UserToken = sock;
receiveArgs.Completed += IOCompleted; receiveArgs.Completed += IOCompleted;
StartReceive(); StartReceive();
@@ -452,23 +478,28 @@ public class TcpSocket : ISocket
private void StartReceive() private void StartReceive()
{ {
var receivingSocket = sock;
try try
{ {
if (destroyed || state != SocketState.Established) if (destroyed || state != SocketState.Established)
return; return;
bool pending = sock.ReceiveAsync(receiveArgs); bool pending = receivingSocket.ReceiveAsync(receiveArgs);
if (!pending) if (!pending)
ProcessReceive(receiveArgs); ProcessReceive(receiveArgs);
} }
catch (Exception ex) catch (Exception ex)
{ {
SafeClose(ex, true); SafeClose(ex, true, receivingSocket);
} }
} }
private void IOCompleted(object sender, SocketAsyncEventArgs e) private void IOCompleted(object sender, SocketAsyncEventArgs e)
{ {
if (!ReferenceEquals(e.UserToken, sock))
return;
switch (e.LastOperation) switch (e.LastOperation)
{ {
case SocketAsyncOperation.Receive: case SocketAsyncOperation.Receive:
@@ -487,13 +518,13 @@ public class TcpSocket : ISocket
{ {
if (e.SocketError != SocketError.Success) if (e.SocketError != SocketError.Success)
{ {
SafeClose(new SocketException((int)e.SocketError), true); SafeClose(new SocketException((int)e.SocketError), true, e.UserToken as Socket);
return; return;
} }
if (e.BytesTransferred <= 0) if (e.BytesTransferred <= 0)
{ {
SafeClose(null, true); SafeClose(null, true, e.UserToken as Socket);
return; return;
} }
@@ -505,7 +536,7 @@ public class TcpSocket : ISocket
} }
catch (Exception ex) catch (Exception ex)
{ {
SafeClose(ex, true); SafeClose(ex, true, e.UserToken as Socket);
} }
} }
@@ -546,6 +577,7 @@ public class TcpSocket : ISocket
if (sendArgs == null) if (sendArgs == null)
{ {
sendArgs = new SocketAsyncEventArgs(); sendArgs = new SocketAsyncEventArgs();
sendArgs.UserToken = sock;
sendArgs.Completed += IOCompleted; sendArgs.Completed += IOCompleted;
} }
@@ -743,19 +775,24 @@ public class TcpSocket : ISocket
} }
} }
private void SafeClose(Exception ex, bool notifyReceiver) private void SafeClose(Exception ex, bool notifyReceiver, Socket expectedSocket = null)
{ {
bool notify = false; bool notify = false;
List<SendReplyCompletion> completions = null; List<SendReplyCompletion> completions = null;
Socket socketToClose;
lock (stateLock) lock (stateLock)
{ {
if (expectedSocket != null && !ReferenceEquals(sock, expectedSocket))
return;
if (state == SocketState.Closed) if (state == SocketState.Closed)
return; return;
state = SocketState.Closed; state = SocketState.Closed;
notify = notifyReceiver && !closeNotified; notify = notifyReceiver && !closeNotified;
closeNotified = true; closeNotified = true;
socketToClose = sock;
} }
lock (sendLock) lock (sendLock)
@@ -782,9 +819,9 @@ public class TcpSocket : ISocket
Interlocked.Exchange(ref pendingSendBytes, 0); Interlocked.Exchange(ref pendingSendBytes, 0);
} }
try { sock.Shutdown(SocketShutdown.Both); } catch { } try { socketToClose.Shutdown(SocketShutdown.Both); } catch { }
try { sock.Close(); } catch { } try { socketToClose.Close(); } catch { }
try { sock.Dispose(); } catch { } try { socketToClose.Dispose(); } catch { }
CompleteSendReplies(completions); CompleteSendReplies(completions);
@@ -798,6 +835,38 @@ public class TcpSocket : ISocket
} }
} }
/// <summary>
/// Esiur 2 allowed callers to invoke Connect after Close. Recreate all
/// per-connection state so that existing clients retain that behavior.
/// </summary>
private void ResetForReconnect_NoLock()
{
DisposeEventArgs(ref receiveArgs);
DisposeEventArgs(ref sendArgs);
sock = CreateSocket();
receiveBuffer = new byte[Math.Max(sock.ReceiveBufferSize, 8192)];
receiveNetworkBuffer = new NetworkBuffer();
began = false;
held = false;
closeNotified = false;
}
private void DisposeEventArgs(ref SocketAsyncEventArgs args)
{
if (args == null)
return;
try
{
args.Completed -= IOCompleted;
args.Dispose();
}
catch { }
args = null;
}
private static void ValidateRange(byte[] message, int offset, int length) private static void ValidateRange(byte[] message, int offset, int length)
{ {
if (offset < 0 || length < 0 || offset > message.Length - length) if (offset < 0 || length < 0 || offset > message.Length - length)