private 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}", this.Configuration.ConnectTimeout)
                            )
                        );
                    return;
                }

#if !API_SIGNATURE_TEST
                MsgPackRpcClientProtocolsTrace.TraceEvent(
                    MsgPackRpcClientProtocolsTrace.EndConnect,
                    "Connected. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}",
                    ClientTransport.GetHandle(connectSocket),
                    ClientTransport.GetRemoteEndPoint(connectSocket, context),
                    ClientTransport.GetLocalEndPoint(connectSocket)
                    );
#endif

                taskCompletionSource.SetResult(this.GetTransport(connectSocket));
            }
            finally
            {
                context.Dispose();
            }
        }
Example #2
0
        /// <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>
        private 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)
            {
                this.HandleDeserializationError(
                    context,
                    "Invalid response message stream. ID must be UInt32 compatible integer.",
                    () => context.UnpackingBuffer.ToArray()
                    );
                return(context.NextProcess(context));
            }

            context.NextProcess = this.UnpackError;
            return(context.NextProcess(context));
        }
        /// <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>
        private 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))
            {
                this.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 = this.BoundSocket;
                MsgPackRpcClientProtocolsTrace.TraceEvent(
                    MsgPackRpcClientProtocolsTrace.BeginReceive,
                    "Receive inbound data. {{  \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}",
                    GetHandle(socket),
                    GetRemoteEndPoint(socket, context),
                    GetLocalEndPoint(socket)
                    );
                this.ReceiveCore(context);
            }
        }
        private void OnCompleted(object sender, SocketAsyncEventArgs e)
        {
            var socket = sender as Socket;

#if MONO
            var userToken = e.UserToken as Tuple <TaskCompletionSource <ClientTransport>, ConnectTimeoutWatcher, Socket>;
#else
            var userToken = e.UserToken as Tuple <TaskCompletionSource <ClientTransport>, ConnectTimeoutWatcher>;
#endif
            var taskCompletionSource = userToken.Item1;
            var watcher = userToken.Item2;
            if (watcher != null)
            {
                this.EndConnectTimeoutWatch(watcher);
            }

#if MONO
            var error = this.HandleSocketError(userToken.Item3 ?? socket, e);
#else
            var error = this.HandleSocketError(e.ConnectSocket ?? socket, e);
#endif
            if (error != null)
            {
                taskCompletionSource.SetException(error.Value.ToException());
                return;
            }

            switch (e.LastOperation)
            {
            case SocketAsyncOperation.Connect:
            {
#if MONO
                this.OnConnected(userToken.Item3, e, taskCompletionSource);
#else
                this.OnConnected(e.ConnectSocket, e, taskCompletionSource);
#endif
                break;
            }

            default:
            {
#if !API_SIGNATURE_TEST
                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
                    );
#endif
                taskCompletionSource.SetException(new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Unknown socket operation : {0}", e.LastOperation)));
                break;
            }
            }
        }
        /// <summary>
        ///		When overridden in the derived class, shutdowns the sending.
        /// </summary>
        protected virtual void ShutdownSending()
        {
            var socket = this.BoundSocket;

            MsgPackRpcClientProtocolsTrace.TraceEvent(
                MsgPackRpcClientProtocolsTrace.ShutdownSending,
                "Shutdown sending. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}",
                GetHandle(socket),
                GetRemoteEndPoint(socket, default(MessageContext)),
                GetLocalEndPoint(socket)
                );
        }
        private void OnSocketOperationCompleted(object sender, SocketAsyncEventArgs e)
        {
            var socket  = sender as Socket;
            var context = e.GetContext();

            if (!this.HandleSocketError(socket, context))
            {
                return;
            }

            switch (context.LastOperation)
            {
            case SocketAsyncOperation.Send:
#if !SILVERLIGHT
            case SocketAsyncOperation.SendTo:
            case SocketAsyncOperation.SendPackets:
#endif
            {
                var requestContext = context as ClientRequestContext;
                Contract.Assert(requestContext != null);
                this.OnSent(requestContext);
                break;
            }

            case SocketAsyncOperation.Receive:
#if !SILVERLIGHT
            case SocketAsyncOperation.ReceiveFrom:
            case SocketAsyncOperation.ReceiveMessageFrom:
#endif
            {
                var responseContext = context as ClientResponseContext;
                Contract.Assert(responseContext != null);
                this.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>
        ///		When overridden in the derived class, shutdowns the receiving.
        /// </summary>
        protected virtual void ShutdownReceiving()
        {
            var socket = this.BoundSocket;

            MsgPackRpcClientProtocolsTrace.TraceEvent(
                MsgPackRpcClientProtocolsTrace.ShutdownReceiving,
                "Shutdown receiving. {{ \"Socket\" : 0x{0:X}, \"RemoteEndPoint\" : \"{1}\", \"LocalEndPoint\" : \"{2}\" }}",
                GetHandle(socket),
                GetRemoteEndPoint(socket, default(MessageContext)),
                GetLocalEndPoint(socket)
                );

            this.OnShutdownCompleted(new ShutdownCompletedEventArgs(( ShutdownSource )this._shutdownSource));
        }
        /// <summary>
        ///		Raises internal shutdown completion routine.
        /// </summary>
        /// <param name="e">The <see cref="MsgPack.Rpc.Protocols.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("e");
            }

            Contract.EndContractBlock();

            var socket = Interlocked.Exchange(ref this._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 this._pendingRequestTable)
            {
                entry.Value(null, errorMessage.ToException(), false);
            }

            foreach (var entry in this._pendingNotificationTable)
            {
                entry.Value(errorMessage.ToException(), false);
            }

            this._pendingRequestTable.Clear();
            this._pendingNotificationTable.Clear();

            var handler = Interlocked.CompareExchange(ref this._shutdownCompleted, null, null);

            if (handler != null)
            {
                handler(this, e);
            }
        }
        private void HandleDeserializationError(ClientResponseContext context, int?messageId, RpcErrorMessage rpcError, string message, Func <byte[]> invalidRequestHeaderProvider)
        {
            MsgPackRpcClientProtocolsTrace.TraceRpcError(
                rpcError.Error,
                "Deserialization error. {0} {{ \"Message ID\" : {1}, \"Error\" : {2} }}",
                message,
                messageId == null ? "(null)" : messageId.ToString(),
                rpcError
                );

            if (invalidRequestHeaderProvider != null && MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.DumpInvalidResponseHeader))
            {
                var array = invalidRequestHeaderProvider();
                MsgPackRpcClientProtocolsTrace.TraceData(MsgPackRpcClientProtocolsTrace.DumpInvalidResponseHeader, BitConverter.ToString(array), array);
            }

            this.RaiseError(messageId, context.SessionId, GetRemoteEndPoint(this.BoundSocket, context), rpcError, context.CompletedSynchronously);

            context.NextProcess = this.DumpCorrupttedData;
        }
        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());
        }
Example #11
0
        /// <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)
            {
                this.HandleDeserializationError(context, "Invalid response message stream. Message must be array.", () => context.UnpackingBuffer.ToArray());
                return(context.NextProcess(context));
            }

            if (context.RootUnpacker.ItemsCount != 4)
            {
                this.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));
        }
Example #12
0
        /// <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>
        private 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  = this.Dispatch;

            return(context.NextProcess(context));
        }
        private void OnWaitTimeout(MessageContext context)
        {
            Contract.Assert(context != null);

            var asClientRequestContext = context as ClientRequestContext;

            var socket = this.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,
                this._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, this.Manager.Configuration.WaitTimeout.Value.Ticks }
            },
                        true
                        )
                    );

            this.RaiseError(context.MessageId, context.SessionId, GetRemoteEndPoint(socket, context), rpcError, false);
            this.ResetConnection();
        }
        private void HandleOrphan(int?messageId, long sessionId, EndPoint remoteEndPoint, RpcErrorMessage rpcError, MessagePackObject?returnValue)
        {
            var socket = this.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,
#if !SILVERLIGHT
                new StackTrace(0, true)
#else
                new StackTrace()
#endif
                );

            this._manager.HandleOrphan(messageId, rpcError, returnValue);
        }
        /// <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 this._isDisposed, 1) == 0)
                {
                    try
                    {
                        var socket = this.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 this._shutdownSource, ( int )ShutdownSource.Disposing, 0) == 0)
                        {
                            var closingSocket = Interlocked.Exchange(ref this._boundSocket, null);
                            if (closingSocket != null)
                            {
                                closingSocket.Close();
                            }
                        }
                    }
                    catch (ObjectDisposedException)
                    {
                        MsgPackRpcClientProtocolsTrace.TraceEvent(
                            MsgPackRpcClientProtocolsTrace.DisposeTransport,
                            "Dispose transport. {{ \"Socket\" : \"Disposed\", \"RemoteEndPoint\" : \"Disposed\", \"LocalEndPoint\" : \"Disposed\" }}"
                            );
                    }
                }
            }
        }
Example #16
0
        /// <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>
        private 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)
            {
                this.HandleDeserializationError(context, "Invalid response message stream. Message Type must be Int32 compatible integer.", () => context.UnpackingBuffer.ToArray());
                return(context.NextProcess(context));
            }

            MessageType type = ( MessageType )numericType;

            if (type != MessageType.Response)
            {
                this.HandleDeserializationError(
                    context,
                    String.Format(CultureInfo.CurrentCulture, "Unknown message type '{0:x8}'", numericType),
                    () => context.UnpackingBuffer.ToArray()
                    );
                return(context.NextProcess(context));
            }

            context.NextProcess = this.UnpackMessageId;

            return(context.NextProcess(context));
        }
        /// <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)
        {
            TaskCompletionSource <ClientTransport> source = new TaskCompletionSource <ClientTransport>();
            var context = new SocketAsyncEventArgs();

            context.RemoteEndPoint = targetEndPoint;
            context.Completed     += this.OnCompleted;

#if !API_SIGNATURE_TEST
            MsgPackRpcClientProtocolsTrace.TraceEvent(
                MsgPackRpcClientProtocolsTrace.BeginConnect,
                "Connecting. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3} }}",
                targetEndPoint,
                targetEndPoint.AddressFamily,
                this.Configuration.PreferIPv4,
                Socket.OSSupportsIPv6
                );
#endif
#if MONO
            var connectingSocket = new Socket(targetEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
#endif
            context.UserToken =
                Tuple.Create(
                    source,
                    this.BeginConnectTimeoutWatch(
                        () =>
            {
#if !API_SIGNATURE_TEST
                MsgPackRpcClientProtocolsTrace.TraceEvent(
                    MsgPackRpcClientProtocolsTrace.ConnectTimeout,
                    "Connect timeout. {{ \"EndPoint\" : \"{0}\", \"AddressFamily\" : {1}, \"PreferIPv4\" : {2}, \"OSSupportsIPv6\" : {3}, \"ConnectTimeout\" : {4} }}",
                    targetEndPoint,
                    targetEndPoint.AddressFamily,
                    this.Configuration.PreferIPv4,
                    Socket.OSSupportsIPv6,
                    this.Configuration.ConnectTimeout
                    );
#endif
#if MONO
                // Cancel ConnectAsync.
                connectingSocket.Close();
#else
                Socket.CancelConnectAsync(context);
#endif
            }
                        )
#if MONO
                    , connectingSocket
#endif
                    );

#if MONO
            if (!connectingSocket.ConnectAsync(context))
#else
            if (!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, context))
#endif
            {
                this.OnCompleted(null, context);
            }

            return(source.Task);
        }
        /// <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("context");
            }

            Contract.EndContractBlock();

            if (MsgPackRpcClientProtocolsTrace.ShouldTrace(MsgPackRpcClientProtocolsTrace.ReceiveInboundData))
            {
                var socket = this.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 this._shutdownSource, ( int )ShutdownSource.Server, 0) == 0)
                {
                    // Server sent shutdown response.
                    this.ShutdownReceiving();

                    // recv() returns 0 when the server socket shutdown gracefully.
                    var socket = this.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 (this.IsClientShutdown)
                {
                    // Client was started shutdown.
                    this.ShutdownReceiving();
                }

                if (!context.ReceivedData.Any(segment => 0 < segment.Count))
                {
                    // There are no data to handle.
                    this.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(this._beforeDeserializationFilters, context);
            }
            catch (RpcException ex)
            {
                this.HandleDeserializationError(context, TryDetectMessageId(context), new RpcErrorMessage(ex.RpcError, ex.Message, ex.DebugInformation), "Filter rejects message.", () => context.ReceivedData.SelectMany(s => s.AsEnumerable()).ToArray());
                this.FinishReceiving(context);
                return;
            }

            if (!context.NextProcess(context))
            {
                if (this.IsServerShutdown)
                {
                    this.ShutdownReceiving();
                }
                else if (this.CanResumeReceiving)
                {
                    // Wait to arrive more data from server.
                    this.ReceiveCore(context);
                    return;
                }

                this.FinishReceiving(context);
                return;
            }
        }
        /// <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 = this.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 -= this.OnSendTimeout;

            context.ClearBuffers();

            if (context.MessageType == MessageType.Notification)
            {
                try
                {
                    Action <Exception, bool> handler = null;
                    try
                    {
                        this._pendingNotificationTable.TryRemove(context.SessionId, out handler);
                    }
                    finally
                    {
                        var rpcError = context.SocketError.ToClientRpcError();
                        if (handler != null)
                        {
                            handler(rpcError.IsSuccess ? null : rpcError.ToException(), context.CompletedSynchronously);
                        }
                    }
                }
                finally
                {
                    this.Manager.ReturnRequestContext(context);
                    this.OnProcessFinished();
                    this.Manager.ReturnTransport(this);
                }
            }
            else
            {
                if (this.Manager.Configuration.WaitTimeout != null &&
                    (this.Manager.Configuration.WaitTimeout.Value - context.ElapsedTime).TotalMilliseconds < 1.0)
                {
                    this.OnWaitTimeout(context);
                    this.Manager.ReturnRequestContext(context);
                    this.Manager.ReturnTransport(this);
                    return;
                }

                var responseContext = this.Manager.GetResponseContext(this, context.RemoteEndPoint);

                if (this.Manager.Configuration.WaitTimeout != null)
                {
                    responseContext.Timeout += this.OnReceiveTimeout;
                    responseContext.StartWatchTimeout(this.Manager.Configuration.WaitTimeout.Value - context.ElapsedTime);
                }

                this.Manager.ReturnRequestContext(context);
                this.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("context");
            }

            if (!Object.ReferenceEquals(context.BoundTransport, this))
            {
                throw new ArgumentException("Context is not bound to this object.", "context");
            }

            this.VerifyIsNotDisposed();

            if (this.IsClientShutdown)
            {
                throw new InvalidOperationException("This transport is in shutdown.");
            }

            Contract.EndContractBlock();

            if (this.IsServerShutdown)
            {
                throw new RpcErrorMessage(RpcError.TransportError, "Server did shutdown socket.", null).ToException();
            }

            context.Prepare(this.CanUseChunkedBuffer);

            if (context.MessageType == MessageType.Request)
            {
                if (!this._pendingRequestTable.TryAdd(context.MessageId.Value, context.RequestCompletionCallback))
                {
                    throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "Message ID '{0}' is already used.", context.MessageId));
                }
            }
            else
            {
                if (!this._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 = this.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(this._afterSerializationFilters, context);

            if (this.Manager.Configuration.WaitTimeout != null)
            {
                context.Timeout += this.OnSendTimeout;
                context.StartWatchTimeout(this.Manager.Configuration.WaitTimeout.Value);
            }

            this.SendCore(context);
        }