/// <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);
        }
        internal void UnblockTraffic(IPEndPoint localServerEndpoint, StackTransportType transportType)
        {
            if (disposed)
            {
                return;
            }

            String strKey = null;

            foreach (KeyValuePair <string, LspSessionInfoCollection> kvp in sessionMap)
            {
                //sessionMap key is local listening endpoint
                //sessionMap value is remote intertecpted endpoint
                if (kvp.Value.lspSession.InterceptedEndPoint.protocolType == transportType &&
                    kvp.Value.lspSession.InterceptedEndPoint.endPoint.Equals(localServerEndpoint))
                {
                    LspSession session = kvp.Value.lspSession;

                    if (!isConnected)
                    {
                        Connect();
                    }

                    LspUnblockRequest request = new LspUnblockRequest(session);
                    InternalSend(request.Encode());

                    byte[] recvBuf;
                    int    recvLen = LspMessage.ReceiveWholeMessage(this.socket, Marshal.SizeOf(typeof(LspUnblockResponseMsg)), out recvBuf);
                    if (recvLen != Marshal.SizeOf(typeof(LspUnblockResponseMsg)))
                    {
                        throw new InvalidOperationException("UnBlockTraffic failed");
                    }

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

                    strKey = kvp.Key;

                    if (response == null || response.Status != 0)
                    {
                        throw new InvalidOperationException("UnBlockTraffic failed");
                    }

                    break;
                }
            }

            //remove it from session map
            if (strKey != null)
            {
                sessionMap.Remove(strKey);
            }
        }
        internal void ChangeToBlockingMode(IPEndPoint localServerEndpoint, StackTransportType transportType)
        {
            if (disposed)
            {
                return;
            }

            if (!isConnected)
            {
                Connect();
            }

            foreach (KeyValuePair <string, LspSessionInfoCollection> kvp in sessionMap)
            {
                //sessionMap key is local listening endpoint
                //sessionMap value is remote intertecpted endpoint
                if (kvp.Value.lspSession.InterceptedEndPoint.protocolType == transportType &&
                    kvp.Value.lspSession.InterceptedEndPoint.endPoint.Equals(localServerEndpoint))
                {
                    LspSession      session = kvp.Value.lspSession;
                    LspBlockRequest request = new LspBlockRequest(session);
                    InternalSend(request.Encode());
                    byte[] recvBuf;
                    int    recvLen = LspMessage.ReceiveWholeMessage(this.socket, Marshal.SizeOf(typeof(LspBlockResponseMsg)), out recvBuf);
                    if (recvLen != Marshal.SizeOf(typeof(LspBlockResponseMsg)))
                    {
                        throw new InvalidOperationException("BlockTraffic failed");
                    }

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

                    if (response == null || response.Status != 0)
                    {
                        throw new InvalidOperationException("BlockTraffic failed");
                    }

                    return;
                }
            }

            throw new InvalidOperationException("The specified endpoint isn't in intercepted mode");
        }
        /// <summary>
        /// Intercept the ip traffic in a designated address. All the traffic will be sent to sdkListeningIpAddress.
        /// </summary>
        /// <param name="transportType">TCP or UDP . </param>
        /// <param name="isBlocking">Whether this request is a blocking request.</param>
        /// <param name="interceptedEndPoint">The intercepted IP/Port of the windows service . </param>
        /// <param name="sdkLocalListeningEndPoint">The IP/Port listened by SDK . </param>
        /// <returns>LspSession, which is corresponding to each intercepted endpoint. Return null if failed.</returns>
        internal void InterceptTraffic(StackTransportType transportType, bool isBlocking, IPEndPoint interceptedEndPoint,
                                       IPEndPoint sdkLocalListeningEndPoint)
        {
            if (disposed)
            {
                return;
            }

            if (!isConnected)
            {
                Connect();
            }

            if (sdkLocalListeningEndPoint == null)
            {
                throw new ArgumentNullException("sdkLocalListeningEndPoint");
            }

            if (sdkLocalListeningEndPoint.AddressFamily == AddressFamily.InterNetwork &&
                sdkLocalListeningEndPoint.Address == IPAddress.Any)
            {
                sdkLocalListeningEndPoint.Address = IPAddress.Loopback;
            }
            else if (sdkLocalListeningEndPoint.AddressFamily == AddressFamily.InterNetworkV6 &&
                     sdkLocalListeningEndPoint.Address == IPAddress.IPv6Any)
            {
                sdkLocalListeningEndPoint.Address = IPAddress.IPv6Loopback;
            }

            LspInterceptionRequest request = new LspInterceptionRequest(transportType, isBlocking,
                                                                        interceptedEndPoint, sdkLocalListeningEndPoint);

            InternalSend(request.Encode());


            byte[] recvBuf;
            int    recvLen = LspMessage.ReceiveWholeMessage(this.socket, Marshal.SizeOf(typeof(LspInterceptionResponseMsg)), out recvBuf);

            if (recvLen != Marshal.SizeOf(typeof(LspInterceptionResponseMsg)))
            {
                if (isBlocking)
                {
                    throw new InvalidOperationException("BlockTraffic failed");
                }
                else
                {
                    throw new InvalidOperationException("InterceptTraffic failed");
                }
            }

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

            if (response != null && response.Status == 0) //success
            {
                LspSessionInfoCollection sessionInfoCollection = new LspSessionInfoCollection();
                sessionInfoCollection.lspSession = response.Session;
                sessionInfoCollection.lspSession.InterceptedEndPoint = response.InterceptedEndPoint;
                sessionInfoCollection.endPoints = new Dictionary <string, IPEndPoint>();
                this[sdkLocalListeningEndPoint, transportType] = sessionInfoCollection;
            }
            else
            {
                if (isBlocking)
                {
                    throw new InvalidOperationException("BlockTraffic failed");
                }
                else
                {
                    throw new InvalidOperationException("InterceptTraffic failed");
                }
            }
        }