/// <summary> /// Begins a communication task with a TPC client using the worker instance to perform requested operations. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <param name="worker">The TService instance which will fulfill requested operations.</param> /// <param name="client">The TCP client connection.</param> /// <param name="onRequestReceived">(Optional) Action to perform when a client request has been received.</param> /// <param name="onSendingResponse">(Optional) Action to perform once the worker has processed the operation and prior to sending the server response.</param> /// <param name="onError">(Optional) Action to perform when processing encounters an error.</param> /// <returns>Awaitable task hosting the background processing of the client socket connection.</returns> public static async Task HandleClientAsync <TService>( this TService worker, TcpClient client, CancellationToken cancellationToken = default, Func <string, byte[][], Task> onRequestReceived = null, Func <string, byte[], Task> onSendingResponse = null, Func <Exception, Task> onError = null) { await Task.Run(async() => { try { await using NetworkStream stream = client.GetStream(); // Continue reading as long as client requests are available. OperationWrapper request; while (!cancellationToken.IsCancellationRequested && client.Connected && stream.CanRead && (request = await stream.GetWrapperResponseAsync(cancellationToken, client.ReceiveBufferSize).ConfigureAwait(false)) != OperationWrapper.SessionEnded) { if (onRequestReceived != null) { await onRequestReceived.Invoke(request.Operation, request.Arguments).ConfigureAwait(false); } object result = await worker.InvokeRequestAsync(request).ConfigureAwait(false); // Now send back a response. var response = OperationWrapper.FromResult(request.Operation, result); if (onSendingResponse != null) { await onSendingResponse.Invoke(request.Operation, response.Result).ConfigureAwait(false); } await stream.SendWrapperRequestAsync(response, cancellationToken).ConfigureAwait(false); await stream.FlushAsync().ConfigureAwait(false); } } catch (Exception ex) { if (onError == null) { throw; } await onError.Invoke(ex).ConfigureAwait(false); } }, cancellationToken); }
/// <summary> /// Sends a client request across the network socket and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="socket">The TCP client socket.</param> /// <param name="asyncServiceMethod">Projection of the requested asynchronous service method.</param> /// <param name="argument">The argument to send when making performing the service operation.</param> /// <returns>The result of the service operation.</returns> public static async Task <TResult> RequestAsync <TService, TResult>( this Socket socket, string methodName, params object[] arguments) { if (typeof(TService).GetMethod(methodName) == null) { throw new ArgumentException($"Target IWorker method '{methodName}' could not be found.", nameof(methodName)); } await SendWrapperRequestAsync(socket, OperationWrapper.ForRequest(methodName, arguments)).ConfigureAwait(false); return((await GetWrapperResponseAsync(socket).ConfigureAwait(false)).GetResultAs <TResult>()); }
/// <summary> /// Sends a client request across the network socket and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="socket">The TCP client socket.</param> /// <param name="methodName">The name of the method invoked over the service.</param> /// <param name="arguments">The arguments to send when performing the service operation.</param> /// <returns>The result of the service operation.</returns> public static TResult Request <TService, TResult>( this Socket socket, string methodName, params object[] arguments) { if (typeof(TService).GetMethod(methodName) == null) { throw new ArgumentException($"Target IWorker method '{methodName}' could not be found.", nameof(methodName)); } SendWrapperRequest(socket, OperationWrapper.ForRequest(methodName, arguments)); return(GetWrapperResponse(socket).GetResultAs <TResult>()); }
public static OperationWrapper FromResult(string operation, object content) { var wrapper = new OperationWrapper { Operation = operation }; if (content != null) { using var ms = new MemoryStream(); Serializer.Serialize(ms, content); wrapper.Result = ms.ToArray(); } return(wrapper); }
internal static async Task SendWrapperRequestAsync(this Socket socket, OperationWrapper request, CancellationToken cancellationToken = default) { // Serialize in memory so we can get the size before sending to the network buffer. await using var ms = new MemoryStream(); Serializer.Serialize(ms, request); // Send the size header. byte[] sizeHeader = new byte[14]; sizeHeader[0] = sizeHeader[2] = sizeHeader[12] = byte.MaxValue; Array.Copy(BitConverter.GetBytes(ms.Length), 0, sizeHeader, 3, sizeof(long)); await socket.SendAsync(sizeHeader, SocketFlags.None, cancellationToken).ConfigureAwait(false); // Send the client request. ms.Position = 0; await socket.SendAsync(ms.ToArray(), SocketFlags.None, cancellationToken).ConfigureAwait(false); }
private static async Task SendWrapperRequestAsync(this NetworkStream stream, OperationWrapper request, CancellationToken cancellationToken) { // Serialize in memory so we can get the size before sending to the network buffer. await using var ms = new MemoryStream(); Serializer.Serialize(ms, request); // Send the size header. byte[] sizeHeader = new byte[14]; sizeHeader[0] = sizeHeader[2] = sizeHeader[12] = byte.MaxValue; Array.Copy(BitConverter.GetBytes(ms.Length), 0, sizeHeader, 3, sizeof(long)); await stream.WriteAsync(sizeHeader, 0, sizeHeader.Length, cancellationToken).ConfigureAwait(false); // Send the client request. ms.Position = 0; await ms.CopyToAsync(stream, cancellationToken).ConfigureAwait(false); }
internal static void SendWrapperRequest(this Socket socket, OperationWrapper request) { // Serialize in memory so we can get the size before sending to the network buffer. using var ms = new MemoryStream(); Serializer.Serialize(ms, request); // Send the size header. byte[] sizeHeader = new byte[14]; sizeHeader[0] = sizeHeader[2] = sizeHeader[12] = byte.MaxValue; Array.Copy(BitConverter.GetBytes(ms.Length), 0, sizeHeader, 3, sizeof(long)); socket.Send(sizeHeader, SocketFlags.None); // Send the client request. ms.Position = 0; socket.Send(ms.ToArray(), SocketFlags.None); }
private static void SendWrapperRequest(this NetworkStream stream, OperationWrapper request) { // Serialize in memory so we can get the size before sending to the network buffer. using var ms = new MemoryStream(); Serializer.Serialize(ms, request); // Send the size header. byte[] sizeHeader = new byte[14]; sizeHeader[0] = sizeHeader[2] = sizeHeader[12] = byte.MaxValue; Array.Copy(BitConverter.GetBytes(ms.Length), 0, sizeHeader, 3, sizeof(long)); stream.Write(sizeHeader, 0, sizeHeader.Length); // Send the client request. ms.Position = 0; ms.CopyTo(stream); }
private static async Task <object> InvokeRequestAsync <T>(this T worker, OperationWrapper request) { MethodInfo method = typeof(T).GetMethod(request.Operation) ?? throw new ArgumentException("Request operation was invalid.", nameof(request)); object[] arguments = null; ParameterInfo[] methodParameters = method.GetParameters(); if (methodParameters.Length > 0) { if (!(request.Arguments.Length >= methodParameters.Length)) { throw new ArgumentException("Given request parameters did not match parameters for the operation."); } arguments = new object[methodParameters.Length]; for (int i = 0; i < methodParameters.Length; i++) { if (request.Arguments[i]?.Length > 0) { await using var stream = new MemoryStream(request.Arguments[i]); arguments[i] = Serializer.Deserialize(methodParameters[i].ParameterType, stream); } else { arguments[i] = null; } } } bool isAwaitable = method.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null; if (!isAwaitable) { return(method.Invoke(worker, arguments)); } var task = (Task)method.Invoke(worker, arguments); await task.ConfigureAwait(false); PropertyInfo resultProperty = task.GetType().GetProperty("Result") ?? throw new InvalidOperationException( $"Method {request.Operation} must return a result."); return(resultProperty.GetValue(task)); }
/// <summary> /// Sends a client request across the network socket and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="socket">The TCP client socket.</param> /// <param name="asyncServiceMethod">Projection of the requested asynchronous service method.</param> /// <returns>The result of the service operation.</returns> public static async Task <TResult> RequestAsync <TService, TResult>( this Socket socket, Expression <Func <TService, Func <Task <TResult> > > > asyncServiceMethod) { var targetMethod = (((asyncServiceMethod.Body as UnaryExpression) ?.Operand as MethodCallExpression) ?.Object as ConstantExpression) ?.Value as MethodInfo; if (targetMethod == null) { throw new ArgumentException("Target IWorker method could not be found from worker method expression.", nameof(asyncServiceMethod)); } await SendWrapperRequestAsync(socket, OperationWrapper.ForRequest(targetMethod.Name)).ConfigureAwait(false); return((await GetWrapperResponseAsync(socket).ConfigureAwait(false)).GetResultAs <TResult>()); }
public static OperationWrapper ForRequest(string operation, object[] arguments = null) { var wrapper = new OperationWrapper { Operation = operation }; if (arguments?.Length > 0) { wrapper.Arguments = new byte[arguments.Length][]; for (int i = 0; i < arguments.Length; i++) { using var ms = new MemoryStream(); Serializer.Serialize(ms, arguments[i]); wrapper.Arguments[i] = ms.ToArray(); } } return(wrapper); }
/// <summary> /// Sends a client request across the network socket and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="socket">The TCP client socket.</param> /// <param name="serviceMethod">Projection of the requested service method.</param> /// <returns>The result of the service operation.</returns> public static TResult Request <TService, TResult>( this Socket socket, Expression <Func <TService, Func <TResult> > > serviceMethod) { var targetMethod = (((serviceMethod.Body as UnaryExpression) ?.Operand as MethodCallExpression) ?.Object as ConstantExpression) ?.Value as MethodInfo; if (targetMethod == null) { throw new ArgumentException("Target IWorker method could not be found from worker method expression.", nameof(serviceMethod)); } SendWrapperRequest(socket, OperationWrapper.ForRequest(targetMethod.Name)); return(GetWrapperResponse(socket).GetResultAs <TResult>()); }
/// <summary> /// Sends a client request across the network stream and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TArgument">The type of the argument for the requested service operation.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="stream">The network stream from the TCP client.</param> /// <param name="asyncServiceMethod">Projection of the requested asynchronous service method.</param> /// <param name="argument">The argument to send when making performing the service operation.</param> /// <returns>The result of the service operation.</returns> public static TResult Request <TService, TArgument, TResult>( this NetworkStream stream, Expression <Func <TService, Func <TArgument, Task <TResult> > > > asyncServiceMethod, TArgument argument) { var targetMethod = (((asyncServiceMethod.Body as UnaryExpression) ?.Operand as MethodCallExpression) ?.Object as ConstantExpression) ?.Value as MethodInfo; if (targetMethod == null) { throw new ArgumentException("Target IWorker method could not be found from worker method expression.", nameof(asyncServiceMethod)); } SendWrapperRequest(stream, OperationWrapper.ForRequest(targetMethod.Name, new object[] { argument })); return(GetWrapperResponse(stream).GetResultAs <TResult>()); }
/// <summary> /// Sends a client request across the network stream and retrieves the result. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <typeparam name="TArgument">The type of the argument for the requested service operation.</typeparam> /// <typeparam name="TResult">The type of the return value of the requested service operation.</typeparam> /// <param name="stream">The network stream from the TCP client.</param> /// <param name="asyncServiceMethod">Projection of the requested asynchronous service method.</param> /// <param name="argument">The argument to send when making performing the service operation.</param> /// <returns>The result of the service operation.</returns> public static async Task <TResult> RequestAsync <TService, TArgument, TResult>( this NetworkStream stream, Expression <Func <TService, Func <TArgument, Task <TResult> > > > asyncServiceMethod, TArgument argument, CancellationToken cancellationToken = default) { var targetMethod = (((asyncServiceMethod.Body as UnaryExpression) ?.Operand as MethodCallExpression) ?.Object as ConstantExpression) ?.Value as MethodInfo; if (targetMethod == null) { throw new ArgumentException("Target IWorker method could not be found from worker method expression.", nameof(asyncServiceMethod)); } await SendWrapperRequestAsync(stream, OperationWrapper.ForRequest(targetMethod.Name, new object[] { argument }), cancellationToken).ConfigureAwait(false); return((await GetWrapperResponseAsync(stream, cancellationToken).ConfigureAwait(false)).GetResultAs <TResult>()); }
/// <summary> /// Begins a communication task with a client socket using the worker instance to perform requested operations. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <param name="worker">The TService instance which will fulfill requested operations.</param> /// <param name="clientSocket">The client socket connection.</param> /// <param name="onRequestReceived">(Optional) Action to perform when a client request has been received.</param> /// <param name="onSendingResponse">(Optional) Action to perform once the worker has processed the operation and prior to sending the server response.</param> /// <param name="onError">(Optional) Action to perform when processing encounters an error.</param> /// <returns>Awaitable task hosting the background processing of the client socket connection.</returns> public static async Task HandleClientAsync <TService>( this TService worker, Socket clientSocket, CancellationToken cancellationToken = default, Action <string, byte[][]> onRequestReceived = null, Action <string, byte[]> onSendingResponse = null, Action <Exception> onError = null) { await Task.Run(async() => { try { // Continue reading as long as client requests are available. OperationWrapper request; while (!cancellationToken.IsCancellationRequested && clientSocket.Connected && (request = await clientSocket.GetWrapperResponseAsync().ConfigureAwait(false)) != OperationWrapper.SessionEnded) { onRequestReceived?.Invoke(request.Operation, request.Arguments); object result = await worker.InvokeRequestAsync(request).ConfigureAwait(false); // Now send back a response. var response = OperationWrapper.FromResult(request.Operation, result); onSendingResponse?.Invoke(request.Operation, response.Result); await clientSocket.SendWrapperRequestAsync(response, cancellationToken).ConfigureAwait(false); } } catch (Exception ex) { if (onError == null) { throw; } onError(ex); } }); }
/// <summary> /// Begins a communication task with a TCP client using the worker instance to perform requested operations. /// </summary> /// <typeparam name="TService">The contract for the service.</typeparam> /// <param name="worker">The TService instance which will fulfill requested operations.</param> /// <param name="client">The TCP client connection.</param> /// <param name="onRequestReceived">(Optional) Action to perform when a client request has been received.</param> /// <param name="onSendingResponse">(Optional) Action to perform once the worker has processed the operation and prior to sending the server response.</param> /// <param name="onError">(Optional) Action to perform when processing encounters an error.</param> /// <returns>Awaitable task hosting the background processing of the client socket connection.</returns> public static void HandleClient <TService>( this TService worker, TcpClient client, Action <string, byte[][]> onRequestReceived = null, Action <string, byte[]> onSendingResponse = null, Action <Exception> onError = null) { try { using NetworkStream stream = client.GetStream(); // Continue reading as long as client requests are available. OperationWrapper request; while (client.Connected && stream.CanRead && (request = stream.GetWrapperResponse(client.ReceiveBufferSize)) != OperationWrapper.SessionEnded) { onRequestReceived?.Invoke(request.Operation, request.Arguments); object result = worker.InvokeRequest(request); // Now send back a response. var response = OperationWrapper.FromResult(request.Operation, result); onSendingResponse?.Invoke(request.Operation, response.Result); stream.SendWrapperRequest(response); stream.Flush(); } } catch (Exception ex) { if (onError == null) { throw; } onError(ex); } }