|
|
@@ -153,6 +153,68 @@ namespace Renci.SshNet.Abstractions
|
|
|
#endif
|
|
|
}
|
|
|
|
|
|
+ public static void ReadContinuous(Socket socket, byte[] buffer, int offset, int size, Action<byte[], int, int> processReceivedBytesAction)
|
|
|
+ {
|
|
|
+#if FEATURE_SOCKET_SYNC
|
|
|
+ // do not time-out receive
|
|
|
+ socket.ReceiveTimeout = 0;
|
|
|
+
|
|
|
+ while (socket.Connected)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var bytesRead = socket.Receive(buffer, offset, size, SocketFlags.None);
|
|
|
+ if (bytesRead == 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ processReceivedBytesAction(buffer, offset, bytesRead);
|
|
|
+ }
|
|
|
+ catch (SocketException ex)
|
|
|
+ {
|
|
|
+ if (IsErrorResumable(ex.SocketErrorCode))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ switch (ex.SocketErrorCode)
|
|
|
+ {
|
|
|
+ case SocketError.ConnectionAborted:
|
|
|
+ case SocketError.ConnectionReset:
|
|
|
+ // connection was closed
|
|
|
+ return;
|
|
|
+ case SocketError.Interrupted:
|
|
|
+ // connection was closed because FIN/ACK was not received in time after
|
|
|
+ // shutting down the (send part of the) socket
|
|
|
+ return;
|
|
|
+ default:
|
|
|
+ throw; // throw any other error
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+#elif FEATURE_SOCKET_EAP
|
|
|
+ var completionWaitHandle = new ManualResetEvent(false);
|
|
|
+ var readToken = new ContinuousReceiveToken(socket, processReceivedBytesAction, completionWaitHandle);
|
|
|
+ var args = new SocketAsyncEventArgs
|
|
|
+ {
|
|
|
+ RemoteEndPoint = socket.RemoteEndPoint,
|
|
|
+ UserToken = readToken
|
|
|
+ };
|
|
|
+ args.Completed += ReceiveCompleted;
|
|
|
+ args.SetBuffer(buffer, offset, size);
|
|
|
+
|
|
|
+ if (!socket.ReceiveAsync(args))
|
|
|
+ {
|
|
|
+ ReceiveCompleted(null, args);
|
|
|
+ }
|
|
|
+
|
|
|
+ completionWaitHandle.WaitOne();
|
|
|
+ completionWaitHandle.Dispose();
|
|
|
+
|
|
|
+ if (readToken.Exception != null)
|
|
|
+ throw readToken.Exception;
|
|
|
+#else
|
|
|
+#error Receiving data from a Socket is not implemented.
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Reads a byte from the specified <see cref="Socket"/>.
|
|
|
/// </summary>
|
|
|
@@ -502,6 +564,67 @@ namespace Renci.SshNet.Abstractions
|
|
|
private readonly EventWaitHandle _completionWaitHandle;
|
|
|
private readonly Socket _socket;
|
|
|
}
|
|
|
+
|
|
|
+ private class ContinuousReceiveToken : Token
|
|
|
+ {
|
|
|
+ public ContinuousReceiveToken(Socket socket, Action<byte[], int, int> processReceivedBytesAction, EventWaitHandle completionWaitHandle)
|
|
|
+ {
|
|
|
+ _socket = socket;
|
|
|
+ _processReceivedBytesAction = processReceivedBytesAction;
|
|
|
+ _completionWaitHandle = completionWaitHandle;
|
|
|
+ }
|
|
|
+
|
|
|
+ public Exception Exception { get; private set; }
|
|
|
+
|
|
|
+ public void Process(SocketAsyncEventArgs args)
|
|
|
+ {
|
|
|
+ if (args.SocketError == SocketError.Success)
|
|
|
+ {
|
|
|
+ if (args.BytesTransferred == 0)
|
|
|
+ {
|
|
|
+ // remote socket was closed
|
|
|
+ _completionWaitHandle.Set();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ _processReceivedBytesAction(args.Buffer, args.Offset, args.BytesTransferred);
|
|
|
+ ResumeOperation(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (IsErrorResumable(args.SocketError))
|
|
|
+ {
|
|
|
+ ThreadAbstraction.Sleep(30);
|
|
|
+ ResumeOperation(args);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (args.SocketError != SocketError.OperationAborted)
|
|
|
+ {
|
|
|
+ Exception = new SocketException((int) args.SocketError);
|
|
|
+ }
|
|
|
+
|
|
|
+ // we're dealing with a (fatal) error
|
|
|
+ _completionWaitHandle.Set();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ResumeOperation(SocketAsyncEventArgs args)
|
|
|
+ {
|
|
|
+ switch (args.LastOperation)
|
|
|
+ {
|
|
|
+ case SocketAsyncOperation.Receive:
|
|
|
+ _socket.ReceiveAsync(args);
|
|
|
+ break;
|
|
|
+ case SocketAsyncOperation.Send:
|
|
|
+ _socket.SendAsync(args);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private readonly EventWaitHandle _completionWaitHandle;
|
|
|
+ private readonly Socket _socket;
|
|
|
+ private readonly Action<byte[], int, int> _processReceivedBytesAction;
|
|
|
+ }
|
|
|
#endif // FEATURE_SOCKET_EAP && !FEATURE_SOCKET_SYNC
|
|
|
}
|
|
|
}
|