mirror of
https://github.com/esiur/esiur-dotnet.git
synced 2026-07-31 01:40:42 +00:00
Sockets
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user