/// <summary>
        /// Retrieve the real remote endpoint address that replaced by the LspClient endpoint.
        /// </summary>
        /// <param name="socketToRetrieve">Socket of Local LspClient. </param>
        /// <param name="destinationEndpoint">destination endpoint of the connection. </param>
        /// <returns>Local IPEndPoint.</returns>
        internal IPEndPoint RetrieveRemoteEndPoint(Socket socketToRetrieve, out IPEndPoint destinationEndpoint)
        {
            destinationEndpoint = null;

            if (disposed)
            {
                return(null);
            }

            if (!isConnected)
            {
                Connect();
            }

            IPEndPoint remoteEndPoint = socketToRetrieve.RemoteEndPoint as IPEndPoint;

            //find the session by EndPoint
            if (this[socketToRetrieve.LocalEndPoint as IPEndPoint, StackTransportType.Tcp] != null)
            {
                remoteEndPoint = GetMappedIPEndPoint((IPEndPoint)socketToRetrieve.LocalEndPoint,
                                                     socketToRetrieve.RemoteEndPoint as IPEndPoint,
                                                     StackTransportType.Tcp);

                if (remoteEndPoint == null)
                {
                    LspSession session = this[socketToRetrieve.LocalEndPoint as IPEndPoint,
                                              StackTransportType.Tcp].lspSession;
                    IPEndPoint lspClientEndPoint = socketToRetrieve.RemoteEndPoint as IPEndPoint;

                    //retrieve the remote EndPoint
                    LspRetrieveEndPointRequest request = new LspRetrieveEndPointRequest(session, lspClientEndPoint);
                    InternalSend(request.Encode());

                    byte[] recvBuf;
                    int    recvLen = LspMessage.ReceiveWholeMessage(this.socket, Marshal.SizeOf(typeof(LspRetrieveEndPointResponseMsg)), out recvBuf);
                    if (recvLen != Marshal.SizeOf(typeof(LspRetrieveEndPointResponseMsg)))
                    {
                        throw new InvalidOperationException("Failed to retrieve remote endpoint");
                    }

                    LspRetrieveEndPointResponse response = LspRetrieveEndPointResponse.Decode(recvBuf)
                                                           as LspRetrieveEndPointResponse;

                    if (response == null || response.Status != 0)
                    {
                        throw new InvalidOperationException("Failed to retrieve remote endpoint");
                    }

                    //map from local endpoint to real endpoint
                    this.SetMappedIPEndPoint((IPEndPoint)socketToRetrieve.LocalEndPoint,
                                             lspClientEndPoint, response.RemoteClientEndPoint, StackTransportType.Tcp);

                    remoteEndPoint      = response.RemoteClientEndPoint;
                    destinationEndpoint = response.DestinationEndPoint;
                }
            }

            return(remoteEndPoint);
        }
        /// <summary>
        /// Retrieve the real remote endpoint address that replaced by the LspClient endpoint.
        /// </summary>
        /// <param name="socketToRetrieve">Socket of Local LspClient. </param>
        /// <param name="destinationEndpoint">destination endpoint of the connection. </param>
        /// <returns>Local IPEndPoint.</returns>
        internal IPEndPoint RetrieveRemoteEndPoint(Socket socketToRetrieve, out IPEndPoint destinationEndpoint)
        {
            destinationEndpoint = null;

            if (disposed)
            {
                return null;
            }

            if (!isConnected)
            {
                Connect();
            }

            IPEndPoint remoteEndPoint = socketToRetrieve.RemoteEndPoint as IPEndPoint;
            //find the session by EndPoint
            if (this[socketToRetrieve.LocalEndPoint as IPEndPoint, StackTransportType.Tcp] != null)
            {
                remoteEndPoint = GetMappedIPEndPoint((IPEndPoint)socketToRetrieve.LocalEndPoint,
                    socketToRetrieve.RemoteEndPoint as IPEndPoint,
                    StackTransportType.Tcp);

                if (remoteEndPoint == null)
                {
                    LspSession session = this[socketToRetrieve.LocalEndPoint as IPEndPoint,
                        StackTransportType.Tcp].lspSession;
                    IPEndPoint lspClientEndPoint = socketToRetrieve.RemoteEndPoint as IPEndPoint;

                    //retrieve the remote EndPoint
                    LspRetrieveEndPointRequest request = new LspRetrieveEndPointRequest(session, lspClientEndPoint);
                    InternalSend(request.Encode());

                    byte[] recvBuf;
                    int recvLen = LspMessage.ReceiveWholeMessage(this.socket, Marshal.SizeOf(typeof(LspRetrieveEndPointResponseMsg)), out recvBuf);
                    if (recvLen != Marshal.SizeOf(typeof(LspRetrieveEndPointResponseMsg)))
                    {
                        throw new InvalidOperationException("Failed to retrieve remote endpoint");
                    }

                    LspRetrieveEndPointResponse response = LspRetrieveEndPointResponse.Decode(recvBuf)
                        as LspRetrieveEndPointResponse;

                    if (response == null || response.Status != 0)
                    {
                        throw new InvalidOperationException("Failed to retrieve remote endpoint");
                    }

                    //map from local endpoint to real endpoint
                    this.SetMappedIPEndPoint((IPEndPoint)socketToRetrieve.LocalEndPoint,
                        lspClientEndPoint, response.RemoteClientEndPoint, StackTransportType.Tcp);

                    remoteEndPoint = response.RemoteClientEndPoint;
                    destinationEndpoint = response.DestinationEndPoint;
                }
            }

            return remoteEndPoint;
        }