void OnConnected(Socket connectSocket, SocketAsyncEventArgs context, TaskCompletionSource <ClientTransport> taskCompletionSource) { try { if (connectSocket == null || !connectSocket.Connected) { // canceled. taskCompletionSource.SetException( new RpcTransportException( RpcError.ConnectionTimeoutError, "Connect timeout.", string.Format(CultureInfo.CurrentCulture, "Timeout: {0}", Configuration.ConnectTimeout) ) ); return; } MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.EndConnect, "Connected. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", ClientTransport.GetHandle(connectSocket), ClientTransport.GetRemoteEndPoint(connectSocket, context), ClientTransport.GetLocalEndPoint(connectSocket) ); taskCompletionSource.SetResult(GetTransport(connectSocket)); } finally { context.Dispose(); } }
/// <summary> /// Releases unmanaged and - optionally - managed resources. /// </summary> /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> protected virtual void Dispose(bool disposing) { if (disposing) { if (Interlocked.Exchange(ref isDisposed, 1) == 0) { try { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DisposeTransport, "Dispose transport. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); if (Interlocked.CompareExchange(ref shutdownSource, (int)ShutdownSource.Disposing, 0) == 0) { var closingSocket = Interlocked.Exchange(ref boundSocket, null); if (closingSocket != null) { closingSocket.Close(); } } } catch (ObjectDisposedException) { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DisposeTransport, "Dispose transport. {{ \"Socket\" : \"Disposed\", \"RemoteEndPoint\" : \"Disposed\", \"LocalEndPoint\" : \"Disposed\" }}" ); } } } }
/// <summary> /// Receives byte stream from remote end point. /// </summary> /// <param name="context">Context information.</param> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Idle' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> void Receive(ClientResponseContext context) { Contract.Assert(context != null); Contract.Assert(context.BoundTransport == this, "Context is not bound to this object."); // First, drain last received request. if (context.ReceivedData.Any(segment => 0 < segment.Count)) { DrainRemainingReceivedData(context); } else { // There might be dirty data due to client shutdown. context.ReceivedData.Clear(); Array.Clear(context.CurrentReceivingBuffer, 0, context.CurrentReceivingBuffer.Length); context.PrepareReceivingBuffer(); var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.BeginReceive, "Receive inbound data. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket) ); ReceiveCore(context); } }
/// <summary> /// Unpack Message Type part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackMessageType(ClientResponseContext context) { if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedMessageType, "Message Type is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } int numericType; try { numericType = context.HeaderUnpacker.LastReadData.AsInt32(); } catch (InvalidOperationException) { HandleDeserializationError(context, "Invalid response message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray()); return(context.nextProcess(context)); } var type = (MessageType)numericType; if (type != MessageType.Response) { HandleDeserializationError( context, string.Format(CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType), () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.nextProcess = UnpackMessageId; return(context.nextProcess(context)); }
/// <summary> /// When overridden in the derived class, shutdowns the sending. /// </summary> protected virtual void ShutdownSending() { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ShutdownSending, "Shutdown sending. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); }
/// <summary> /// When overridden in the derived class, shutdowns the receiving. /// </summary> protected virtual void ShutdownReceiving() { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ShutdownReceiving, "Shutdown receiving. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); OnShutdownCompleted(new ShutdownCompletedEventArgs((ShutdownSource)shutdownSource)); }
void OnSocketOperationCompleted(object sender, SocketAsyncEventArgs e) { var socket = sender as Socket; var context = e.GetContext(); if (!HandleSocketError(socket, context)) { return; } switch (context.LastOperation) { case SocketAsyncOperation.Send: case SocketAsyncOperation.SendTo: case SocketAsyncOperation.SendPackets: { var requestContext = context as ClientRequestContext; Contract.Assert(requestContext != null); OnSent(requestContext); break; } case SocketAsyncOperation.Receive: case SocketAsyncOperation.ReceiveFrom: case SocketAsyncOperation.ReceiveMessageFrom: { var responseContext = context as ClientResponseContext; Contract.Assert(responseContext != null); OnReceived(responseContext); break; } default: { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.UnexpectedLastOperation, "Unexpected operation. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"LastOperation\" : \"{3}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, e), GetLocalEndPoint(socket), context.LastOperation ); break; } } }
/// <summary> /// Establishes logical connection, which specified to the managed transport protocol, for the server. /// </summary> /// <param name="targetEndPoint">The end point of target server.</param> /// <returns> /// <see cref="Task{T}"/> of <see cref="ClientTransport"/> which represents asynchronous establishment process specific to the managed transport. /// This value will not be <c>null</c>. /// </returns> protected sealed override Task <ClientTransport> ConnectAsyncCore(EndPoint targetEndPoint) { var source = new TaskCompletionSource <ClientTransport>(); var context = new SocketAsyncEventArgs { RemoteEndPoint = targetEndPoint }; context.Completed += OnCompleted; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.BeginConnect, "Connecting. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3} }}", targetEndPoint, targetEndPoint.AddressFamily, Configuration.PreferIPv4, Socket.OSSupportsIPv6 ); context.UserToken = Tuple.Create( source, BeginConnectTimeoutWatch( () => { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ConnectTimeout, "Connect timeout. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3}, \"ConnectTimeout\" : {4} }}", targetEndPoint, targetEndPoint.AddressFamily, Configuration.PreferIPv4, Socket.OSSupportsIPv6, Configuration.ConnectTimeout ); Socket.CancelConnectAsync(context); } ) ); if (!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, context)) { OnCompleted(null, context); } return(source.Task); }
/// <summary> /// Raises internal shutdown completion routine. /// </summary> /// <param name="e">The <see cref="ShutdownCompletedEventArgs"/> instance containing the event data.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="e"/> is <c>null</c>. /// </exception> protected virtual void OnShutdownCompleted(ShutdownCompletedEventArgs e) { if (e == null) { throw new ArgumentNullException(nameof(e)); } Contract.EndContractBlock(); var socket = Interlocked.Exchange(ref boundSocket, null); MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.TransportShutdownCompleted, "Transport shutdown is completed. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, default(MessageContext)), GetLocalEndPoint(socket) ); if (socket != null) { socket.Close(); } // Notify shutdown to waiting clients. // Note that it is OK from concurrency point of view because additional modifications are guarded via shutdown flag. var errorMessage = new RpcErrorMessage(RpcError.TransportError, string.Format(CultureInfo.CurrentCulture, "Transport is shutdown. Shutdown source is: {0}", e.Source), null); foreach (var entry in pendingRequestTable) { entry.Value(null, errorMessage.ToException(), false); } foreach (var entry in pendingNotificationTable) { entry.Value(errorMessage.ToException(), false); } pendingRequestTable.Clear(); pendingNotificationTable.Clear(); Interlocked.CompareExchange(ref shutdownCompleted, null, null)?.Invoke(this, e); }
void HandleOrphan(int?messageId, long sessionId, EndPoint remoteEndPoint, RpcErrorMessage rpcError, MessagePackObject?returnValue) { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.OrphanError, "There are no handlers to handle message which has MessageID:{0}, SessionID:{1}. This may indicate runtime problem or due to client recycling. {{ \"Socket\" : 0x{2:X}, \"RemoteEndPoint\" : \"{3}\", \"LocalEndPoint\" : \"{4}\", \"SessionID\" :{1}, \"MessageID\" : {0}, \"Error\" : {5}, \"ReturnValue\" : {6}, \"CallStack\" : \"{7}\" }}", messageId == null ? "(null)" : messageId.Value.ToString(CultureInfo.InvariantCulture), sessionId, GetHandle(socket), remoteEndPoint, GetLocalEndPoint(socket), rpcError, returnValue, new StackTrace(0, true) ); manager.HandleOrphan(messageId, rpcError, returnValue); }
/// <summary> /// Handles socket error. /// </summary> /// <param name="socket">The <see cref="Socket"/> which might cause socket error.</param> /// <param name="context">The <see cref="SocketAsyncEventArgs"/> which holds actual error information.</param> /// <returns> /// <see cref="RpcErrorMessage"/> corresponds for the socket error. /// <c>null</c> if the operation result is not socket error. /// </returns> protected internal RpcErrorMessage?HandleSocketError(Socket socket, SocketAsyncEventArgs context) { if (context.SocketError.IsError() == false) { return(null); } MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SocketError, "Socket error. {{ \"Socket\" : 0x{0:X}, \"RemoteEndpoint\" : \"{1}\", \"LocalEndpoint\" : \"{2}\", \"LastOperation\" : \"{3}\", \"SocketError\" : \"{4}\", \"ErrorCode\" : 0x{5:X} }}", ClientTransport.GetHandle(socket), ClientTransport.GetRemoteEndPoint(socket, context), ClientTransport.GetLocalEndPoint(socket), context.LastOperation, context.SocketError, (int)context.SocketError ); return(context.SocketError.ToClientRpcError()); }
/// <summary> /// Unpack response message array header. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> internal bool UnpackResponseHeader(ClientResponseContext context) { Contract.Assert(context != null); if (context.RootUnpacker == null) { context.UnpackingBuffer = new ByteArraySegmentStream(context.ReceivedData); context.RootUnpacker = Unpacker.Create(context.UnpackingBuffer, false); context.RenewSessionId(); } if (!context.ReadFromRootUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedRequestHeader, "Array header is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } if (!context.RootUnpacker.IsArrayHeader) { HandleDeserializationError(context, "Invalid response message stream. Message must be array.", () => context.UnpackingBuffer.ToArray()); return(context.nextProcess(context)); } if (context.RootUnpacker.ItemsCount != 4) { HandleDeserializationError( context, string.Format( CultureInfo.CurrentCulture, "Invalid response message stream. Message must be valid size array. Actual size is {0}.", context.RootUnpacker.ItemsCount ), () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.HeaderUnpacker = context.RootUnpacker.ReadSubtree(); context.nextProcess = UnpackMessageType; return(context.nextProcess(context)); }
void OnCompleted(object sender, SocketAsyncEventArgs e) { var socket = sender as Socket; var userToken = e.UserToken as Tuple <TaskCompletionSource <ClientTransport>, ConnectTimeoutWatcher>; var taskCompletionSource = userToken.Item1; var watcher = userToken.Item2; if (watcher != null) { EndConnectTimeoutWatch(watcher); } var error = HandleSocketError(e.ConnectSocket ?? socket, e); if (error != null) { taskCompletionSource.SetException(error.Value.ToException()); return; } switch (e.LastOperation) { case SocketAsyncOperation.Connect: { OnConnected(e.ConnectSocket, e, taskCompletionSource); break; } default: { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.UnexpectedLastOperation, "Unexpected operation. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"LastOperation\" : \"{3}\" }}", ClientTransport.GetHandle(socket), ClientTransport.GetRemoteEndPoint(socket, e), ClientTransport.GetLocalEndPoint(socket), e.LastOperation ); taskCompletionSource.SetException(new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Unknown socket operation : {0}", e.LastOperation))); break; } } }
/// <summary> /// Unpack result part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackResult(ClientResponseContext context) { Contract.Assert(context.UnpackingBuffer.CanSeek); if (context.resultStartAt == -1) { context.resultStartAt = context.UnpackingBuffer.Position; } var skipped = context.SkipResultSegment(); if (skipped == null) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedResult, "Result value is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } context.resultBuffer = new ByteArraySegmentStream(context.UnpackingBuffer.GetBuffer(context.resultStartAt, context.UnpackingBuffer.Position - context.resultStartAt)); context.nextProcess = Dispatch; return(context.nextProcess(context)); }
void OnWaitTimeout(MessageContext context) { Contract.Assert(context != null); var asClientRequestContext = context as ClientRequestContext; var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.WaitTimeout, "Wait timeout. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\", \"Operation\" : \"{3}\", \"MessageType\" : \"{4}\" \"MessageId\" : {5}, \"BytesTransferred\" : {6}, \"Timeout\" : \"{7}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), asClientRequestContext != null ? "Send" : "Receive", asClientRequestContext == null ? MessageType.Response : asClientRequestContext.MessageType, context.MessageId, context.BytesTransferred, manager.Configuration.WaitTimeout ); var rpcError = new RpcErrorMessage( RpcError.TimeoutError, new MessagePackObject( new MessagePackObjectDictionary(3) { { RpcException.messageKeyUtf8, asClientRequestContext != null ? "Wait timeout on sending." : "Wait timeout on receiving." }, { RpcException.debugInformationKeyUtf8, string.Empty }, { RpcTimeoutException.ClientTimeoutKeyUtf8, Manager.Configuration.WaitTimeout.Value.Ticks } }, true ) ); RaiseError(context.MessageId, context.SessionId, GetRemoteEndPoint(socket, context), rpcError, false); ResetConnection(); }
/// <summary> /// Unpack Message ID part on response message. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the pipeline is finished; /// <c>false</c>, the pipeline is interruppted because extra data is needed. /// </returns> bool UnpackMessageId(ClientResponseContext context) { if (!context.ReadFromHeaderUnpacker()) { MsgPackRpcClientProtocolsTrace.TraceEvent(MsgPackRpcClientProtocolsTrace.NeedMessageId, "Message ID is needed. {{ \"SessionID\" : {0} }}", context.SessionId); return(false); } try { context.MessageId = unchecked ((int)context.HeaderUnpacker.LastReadData.AsUInt32()); } catch (InvalidOperationException) { HandleDeserializationError( context, "Invalid response message stream. ID must be UInt32 compatible integer.", () => context.UnpackingBuffer.ToArray() ); return(context.nextProcess(context)); } context.nextProcess = UnpackError; return(context.nextProcess(context)); }
/// <summary> /// Called when asynchronous 'Receive' operation is completed. /// </summary> /// <param name="context">Context information.</param> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Idle' nor 'Receiving' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> protected virtual void OnReceived(ClientResponseContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Contract.EndContractBlock(); if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.ReceiveInboundData)) { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.ReceiveInboundData, "Receive response. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"BytesTransfered\" : {4} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.BytesTransferred ); } if (context.BytesTransferred == 0) { if (Interlocked.CompareExchange(ref shutdownSource, (int)ShutdownSource.Server, 0) == 0) { // Server sent shutdown response. ShutdownReceiving(); // recv() returns 0 when the server socket shutdown gracefully. var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DetectServerShutdown, "Server shutdown current socket. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}", GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket) ); } else if (IsClientShutdown) { // Client was started shutdown. ShutdownReceiving(); } if (!context.ReceivedData.Any(segment => 0 < segment.Count)) { // There are no data to handle. FinishReceiving(context); return; } } else { context.ShiftCurrentReceivingBuffer(); } if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.DeserializeResponse)) { MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.DeserializeResponse, "Deserialize response. {{ \"SessionID\" : {0}, \"Length\" : {1} }}", context.SessionId, context.ReceivedData.Sum(item => (long)item.Count) ); } // Go deserialization pipeline. // Exceptions here means message error. try { ApplyFilters(BeforeDeserializationFilters, context); } catch (RpcException ex) { HandleDeserializationError(context, TryDetectMessageId(context), new RpcErrorMessage(ex.RpcError, ex.Message, ex.DebugInformation), "Filter rejects message.", () => context.ReceivedData.SelectMany(s => s.AsEnumerable()).ToArray()); FinishReceiving(context); return; } if (!context.nextProcess(context)) { if (IsServerShutdown) { ShutdownReceiving(); } else if (CanResumeReceiving) { // Wait to arrive more data from server. ReceiveCore(context); return; } //this.FinishReceiving( context ); //return; } FinishReceiving(context); }
/// <summary> /// Called when asynchronous 'Send' operation is completed. /// </summary> /// <param name="context">Context information.</param> /// <returns> /// <c>true</c>, if the subsequent request is already received; /// <c>false</c>, otherwise. /// </returns> /// <exception cref="InvalidOperationException"> /// This instance is not in 'Sending' state. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance is disposed. /// </exception> protected virtual void OnSent(ClientRequestContext context) { if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.SentOutboundData)) { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SentOutboundData, "Sent request/notification. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"Type\" : \"{4}\", \"MessageID\" : {5}, \"Method\" : \"{6}\", \"BytesTransferred\" : {7} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.MessageType, context.MessageId, context.MethodName, context.BytesTransferred ); } context.StopWatchTimeout(); context.Timeout -= OnSendTimeout; context.ClearBuffers(); if (context.MessageType == MessageType.Notification) { try { Action <Exception, bool> handler = null; try { pendingNotificationTable.TryRemove(context.SessionId, out handler); } finally { var rpcError = context.SocketError.ToClientRpcError(); handler?.Invoke(rpcError.IsSuccess ? null : rpcError.ToException(), context.CompletedSynchronously); } } finally { Manager.ReturnRequestContext(context); OnProcessFinished(); Manager.ReturnTransport(this); } } else { if (Manager.Configuration.WaitTimeout != null && (Manager.Configuration.WaitTimeout.Value - context.ElapsedTime).TotalMilliseconds < 1.0) { OnWaitTimeout(context); Manager.ReturnRequestContext(context); Manager.ReturnTransport(this); return; } var responseContext = Manager.GetResponseContext(this, context.RemoteEndPoint); if (Manager.Configuration.WaitTimeout != null) { responseContext.Timeout += OnReceiveTimeout; responseContext.StartWatchTimeout(Manager.Configuration.WaitTimeout.Value - context.ElapsedTime); } Manager.ReturnRequestContext(context); Receive(responseContext); } }
/// <summary> /// Sends a request or notification message with the specified context. /// </summary> /// <param name="context">The context information.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> /// <exception cref="ArgumentException"> /// <paramref name="context"/> is not bound to this transport. /// </exception> /// <exception cref="ObjectDisposedException"> /// This instance has been disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// This instance is in shutdown. /// Or the message ID or session ID is duplicated. /// </exception> /// <exception cref="RpcException"> /// Failed to send request or notification to the server. /// </exception> public void Send(ClientRequestContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!ReferenceEquals(context.BoundTransport, this)) { throw new ArgumentException("Context is not bound to this object.", nameof(context)); } VerifyIsNotDisposed(); if (IsClientShutdown) { throw new InvalidOperationException("This transport is in shutdown."); } Contract.EndContractBlock(); if (IsServerShutdown) { throw new RpcErrorMessage(RpcError.TransportError, "Server did shutdown socket.", null).ToException(); } context.Prepare(); if (context.MessageType == MessageType.Request) { if (!pendingRequestTable.TryAdd(context.MessageId.Value, context.RequestCompletionCallback)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Message ID '{0}' is already used.", context.MessageId)); } } else { if (!pendingNotificationTable.TryAdd(context.SessionId, context.NotificationCompletionCallback)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Session ID '{0}' is already used.", context.MessageId)); } } if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.SendOutboundData)) { var socket = BoundSocket; MsgPackRpcClientProtocolsTrace.TraceEvent( MsgPackRpcClientProtocolsTrace.SendOutboundData, "Send request/notification. {{ \"SessionID\" : {0}, \"Socket\" : 0x{1:X}, \"RemoteEndPoint\" : \"{2}\", \"LocalEndPoint\" : \"{3}\", \"Type\" : \"{4}\", \"MessageID\" : {5}, \"Method\" : \"{6}\", \"BytesTransferring\" : {7} }}", context.SessionId, GetHandle(socket), GetRemoteEndPoint(socket, context), GetLocalEndPoint(socket), context.MessageType, context.MessageId, context.MethodName, context.sendingBuffer.Sum(segment => (long)segment.Count) ); } // Because exceptions here means client error, it should be handled like other client error. // Therefore, no catch clauses here. ApplyFilters(AfterSerializationFilters, context); if (Manager.Configuration.WaitTimeout != null) { context.Timeout += OnSendTimeout; context.StartWatchTimeout(Manager.Configuration.WaitTimeout.Value); } SendCore(context); }