/// <summary> /// Performs protocol specific asynchronous 'Send' operation. /// </summary> /// <param name="context">Context information.</param> protected sealed override void SendCore(ClientRequestContext context) { if (!BoundSocket.SendToAsync(context.SocketContext)) { context.SetCompletedSynchronously(); OnSent(context); } }
/// <summary> /// Returns the request context to the pool. /// </summary> /// <param name="context">The context to the pool.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="context"/> is <c>null</c>. /// </exception> protected internal void ReturnRequestContext(ClientRequestContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } Contract.EndContractBlock(); context.Clear(); context.UnboundTransport(); RequestContextPool.Return(context); }
/// <summary> /// Returns the specified context to the <see cref="Manager"/>. /// </summary> /// <param name="context">The <see cref="ClientRequestContext"/> to be returned.</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> public void ReturnContext(ClientRequestContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!ReferenceEquals(context.BoundTransport, this)) { throw new ArgumentException("Context is not bound to this transport.", nameof(context)); } Contract.EndContractBlock(); Manager.ReturnRequestContext(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> /// Performs protocol specific asynchronous 'Send' operation. /// </summary> /// <param name="context">Context information.</param> protected abstract void SendCore(ClientRequestContext context);
/// <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); }