/// <summary>
        /// the received loop for tcp connection.
        /// </summary>
        /// <param name="host">
        /// an IVisitorTcpReceiveLoop interface that specifies the host of visitor.
        /// </param>
        /// <param name="server">
        /// an ITransport object that provides AddEvent, it must be TcpClient or TcpServerConnection.
        /// </param>
        /// <param name="stream">
        /// a Stream object that specifies the underlayer transport stream.<para/>
        /// if DirectTcp, it's the stream of TcpClient.GetStream().<para/>
        /// if Tcp over Ssl, it's SslStream.
        /// </param>
        /// <param name="thread">
        /// a ThreadManager object that specifies the received thread.
        /// </param>
        /// <param name="bufferSize">
        /// an int value that specifies the max buffer size.
        /// </param>
        public static void Visit(
            IVisitorTcpReceiveLoop host, ITransport server,
            Stream stream, ThreadManager thread, int bufferSize)
        {
            byte[] data = new byte[bufferSize];

            while (!thread.ShouldExit)
            {
                int receivedLength = 0;

                try
                {
                    // received data from server.
                    receivedLength = stream.Read(data, 0, data.Length);

                    // if the server close the socket, return.
                    if (receivedLength == 0)
                    {
                        server.AddEvent(host.VisitorCreateTransportEvent(EventType.Disconnected, null));

                        break;
                    }

                    host.VisitorAddReceivedData(ArrayUtility.SubArray<byte>(data, 0, receivedLength));
                }
                catch (IOException ex)
                {
                    SocketException socketException = ex.InnerException as SocketException;

                    // if the server disconnect the socket.
                    if (socketException != null
                        && (socketException.SocketErrorCode == SocketError.ConnectionReset
                        || socketException.SocketErrorCode == SocketError.Interrupted
                        || socketException.SocketErrorCode == SocketError.ConnectionAborted))
                    {
                        // handle the disconnected event, return.
                        server.AddEvent(host.VisitorCreateTransportEvent(EventType.Disconnected, ex));
                    }
                    else
                    {
                        // handle exception event, return.
                        server.AddEvent(host.VisitorCreateTransportEvent(EventType.Exception, ex));
                    }

                    break;
                }
            }
        }
        public static void Visit(
            IVisitorNetbiosReceiveLoop host, ITransport server,
            NetbiosTransport transport, int localEP, int remoteEP, ThreadManager thread)
        {
            byte[] data = null;

            while (!thread.ShouldExit)
            {
                try
                {
                    // received data from server.
                    data = transport.Receive(remoteEP);

                    // if the server close the socket, return.
                    if (data == null)
                    {
                        server.AddEvent(new TransportEvent(EventType.Disconnected, remoteEP, localEP, null));

                        break;
                    }

                    host.VisitorAddReceivedData(ArrayUtility.SubArray<byte>(data, 0));
                }
                // the connection is disconnected.
                catch (InvalidOperationException ex)
                {
                    server.AddEvent(new TransportEvent(EventType.Disconnected, remoteEP, localEP, ex));

                    break;
                }
                catch (Exception ex)
                {
                    // handle exception event, return.
                    server.AddEvent(new TransportEvent(EventType.Exception, remoteEP, localEP, ex));

                    break;
                }
            }
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="tcpListener">
        /// a TcpListener that specifies the listener.
        /// </param>
        /// <param name="tcpServerTransport">
        /// a TcpServerTransport that represents the owner of listener.
        /// </param>
        /// <param name="isLspHooked">
        /// a bool value that indicates whether lsp hooked the transport.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// thrown when tcpListener is null.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when tcpServerTransport is null.
        /// </exception>
        public TcpServerListener(TcpListener tcpListener, TcpServerTransport tcpServerTransport, bool isLspHooked)
        {
            if (tcpListener == null)
            {
                throw new ArgumentNullException("tcpListener");
            }

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

            this.lspHooked = isLspHooked;
            this.listener = tcpListener;
            this.server = tcpServerTransport;
            this.thread = new ThreadManager(AcceptLoop, Unblock);
        }
        public static void Visit(
            IVisitorUdpReceiveLoop host, ITransport server,
            UdpClient udpClient, ThreadManager thread, bool isLspHooked)
        {
            byte[] data = null;

            // get the endpoint of tcp client.
            IPEndPoint localEP = host.LspHookedLocalEP;

            while (!thread.ShouldExit)
            {
                try
                {
                    // received data from server.
                    IPEndPoint remoteEP = null;

                    data = udpClient.Receive(ref remoteEP);

                    if (data == null)
                    {
                        break;
                    }

                    if (isLspHooked)
                    {
                        int numBytesReceived = data.Length;

                        if (numBytesReceived < Marshal.SizeOf(typeof(LspUdpHeader)))
                        {
                            throw new FormatException("Invalid LSP udp packet received");
                        }

                        //Get udp header
                        byte[] udpHeaderBuffer = ArrayUtility.SubArray(data, 0, Marshal.SizeOf(typeof(LspUdpHeader)));

                        IntPtr p = Marshal.AllocHGlobal(udpHeaderBuffer.Length);
                        Marshal.Copy(udpHeaderBuffer, 0, p, udpHeaderBuffer.Length);

                        LspUdpHeader udpHeader = (LspUdpHeader)Marshal.PtrToStructure(p, typeof(LspUdpHeader));
                        Marshal.FreeHGlobal(p);

                        //get address
                        byte[] srcAddressArray = ArrayUtility.SubArray <byte>(data,
                                                                              Marshal.SizeOf(typeof(LspUdpHeader)), udpHeader.HeaderLength -
                                                                              Marshal.SizeOf(typeof(LspUdpHeader)));
                        string srcAddress = Encoding.ASCII.GetString(srcAddressArray);

                        //replacement
                        numBytesReceived = numBytesReceived - udpHeader.HeaderLength;
                        byte[] msgBody = new byte[numBytesReceived];
                        Array.Copy(data, udpHeader.HeaderLength, msgBody, 0, numBytesReceived);

                        //endPoint is real remote client endpoint, remoteEP is LspDll's endpoint
                        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(srcAddress), udpHeader.Port);

                        //map from the real endpoint to the lsp dll endpoint
                        //when sending response to the real endpoint, response will be sent to the lsp dll endpoint
                        //and the lsp dll will relay it to the real endpoint
                        LspConsole.Instance.SetMappedIPEndPoint(
                            (IPEndPoint)udpClient.Client.LocalEndPoint, (IPEndPoint)endPoint,
                            (IPEndPoint)remoteEP, StackTransportType.Udp);

                        host.VisitorAddReceivedData(msgBody, localEP, (IPEndPoint)endPoint);
                    }
                    else
                    {
                        host.VisitorAddReceivedData(data, localEP, remoteEP);
                    }
                }
                catch (Exception ex)
                {
                    // handle exception event, return.
                    server.AddEvent(new TransportEvent(EventType.Exception, localEP, ex));

                    break;
                }
            }
        }
        /// <summary>
        /// Release resources. 
        /// </summary>
        /// <param name = "disposing">
        /// If disposing equals true, Managed and unmanaged resources are disposed. if false, Only unmanaged resources 
        /// can be disposed. 
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed and unmanaged resources.
                if (disposing)
                {
                    // Free managed resources & other reference types:
                    if (this.thread != null)
                    {
                        this.thread.Dispose();
                        this.thread = null;
                    }
                    if (this.transport != null)
                    {
                        // the netbios transport may throw exception, donot arise exception.
                        Utility.SafelyDisconnectNetbiosConnection(this.transport, this.remoteEndPoint);
                        this.transport.Dispose();
                        this.transport = null;
                    }
                    if (this.eventQueue != null)
                    {
                        // the SyncFilterQueue may throw exception, donot arise exception.
                        this.eventQueue.Dispose();
                        this.eventQueue = null;
                    }
                    if (this.packetCache != null)
                    {
                        this.packetCache.Dispose();
                        this.packetCache = null;
                    }
                    if (this.buffer != null)
                    {
                        this.buffer.Dispose();
                        this.buffer = null;
                    }
                }

                // Call the appropriate methods to clean up unmanaged resources.
                // If disposing is false, only the following code is executed:

                this.disposed = true;
            }
        }
        /// <summary>
        /// Release resources. 
        /// </summary>
        /// <param name = "disposing">
        /// If disposing equals true, managed and unmanaged resources are disposed. if false, Only unmanaged resources 
        /// can be disposed. 
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed and unmanaged resources.
                if (disposing)
                {
                    // Free managed resources & other reference types:
                    if (this.thread != null)
                    {
                        this.thread.Dispose();
                        this.thread = null;
                    }
                    if (this.stream != null)
                    {
                        this.stream.Close();
                        this.stream = null;
                    }
                    if (this.buffer != null)
                    {
                        this.buffer.Dispose();
                        this.buffer = null;
                    }

                    if (associatedForwarderChannel != null)
                    {
                        this.associatedForwarderChannel.Dispose();
                    }
                }

                // Call the appropriate methods to clean up unmanaged resources.
                // If disposing is false, only the following code is executed:

                this.disposed = true;
            }
        }
        /// <summary>
        /// Release resources. 
        /// </summary>
        /// <param name = "disposing">
        /// If disposing equals true, managed and unmanaged resources are disposed. if false, Only unmanaged resources 
        /// can be disposed. 
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed and unmanaged resources.
                if (disposing)
                {
                    // Free managed resources & other reference types:
                    if (this.thread != null)
                    {
                        this.thread.Dispose();
                        this.thread = null;
                    }
                    if (this.transport != null)
                    {
                        this.transport.Dispose();
                        this.transport = null;
                    }
                }

                // Call the appropriate methods to clean up unmanaged resources.
                // If disposing is false, only the following code is executed:

                this.disposed = true;
            }
        }
        /// <summary>
        /// start the listener and then start a thread to accept connection from client.
        /// </summary>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the listener on the endpoint has been started.
        /// </exception>
        public void Start()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("NetbiosServerListener");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the listener on the endpoint has been started.");
            }

            if (this.transport == null)
            {
                this.transport = new NetbiosTransport(
                    this.netbiosName, this.server.NetbiosConfig.AdapterIndex,
                    (ushort)this.server.NetbiosConfig.BufferSize, (byte)this.server.NetbiosConfig.MaxSessions,
                    (byte)this.server.NetbiosConfig.MaxNames);
            }

            this.localEndPoint = this.transport.NcbNum;

            this.thread = new ThreadManager(AcceptLoop, Unblock);
            this.thread.Start();
        }
        /// <summary>
        /// Release resources. 
        /// </summary>
        /// <param name = "disposing">
        /// If disposing equals true, managed and unmanaged resources are disposed. if false, Only unmanaged resources 
        /// can be disposed. 
        /// </param>
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed and unmanaged resources.
                if (disposing)
                {
                    // Free managed resources & other reference types:
                    if (this.thread != null)
                    {
                        this.thread.Dispose();
                        this.thread = null;
                    }
                    if (this.udpClient != null)
                    {
                        this.udpClient.Close();
                        this.udpClient = null;
                    }
                    if (this.eventQueue != null)
                    {
                        // the SyncFilterQueue may throw exception, donot arise exception.
                        this.eventQueue.Dispose();
                        this.eventQueue = null;
                    }
                    if (this.packetCache != null)
                    {
                        this.packetCache.Dispose();
                        this.packetCache = null;
                    }
                    if (this.buffer != null)
                    {
                        // the SyncFilterQueue may throw exception, donot arise exception.
                        this.buffer.Dispose();
                        this.buffer = null;
                    }
                }

                // Call the appropriate methods to clean up unmanaged resources.
                // If disposing is false, only the following code is executed:

                this.disposed = true;
            }
        }
        /// <summary>
        /// stop the specified listener.<para/>
        /// the underlayer transport must be TcpServer, UdpServer or NetbiosServer.
        /// </summary>
        /// <param name="localEndPoint">
        /// an object that specifies the listener.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when remoteEndPoint is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// thrown when the specified endpoint is not an IPEndPoint.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the udp client is not connected to server, must invoke Start() first.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when failed to stop the endpoint, it is not started.
        /// </exception>
        public void Stop(object localEndPoint)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("UdpClientTransport");
            }

            if (this.udpClient == null)
            {
                throw new InvalidOperationException(
                    "udp client is not connected to server, must invoke Start() first.");
            }

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

            IPEndPoint localEP = localEndPoint as IPEndPoint;
            if (localEP == null)
            {
                Utility.StopTransportByPort(this, null, this.socketConfig.LocalIpAddress, localEndPoint);
                return;
            }

            if (this.lspHooked)
            {
                localEP = this.udpClient.Client.LocalEndPoint as IPEndPoint;

                LspConsole.Instance.UnblockTraffic(localEP, this.socketConfig.Type);
            }

            if (!localEP.Equals(this.udpClient.Client.LocalEndPoint))
            {
                throw new InvalidOperationException("failed to stop the endpoint, it is not started.");
            }

            this.thread.Stop();
            this.thread = null;

            this.udpClient.Close();
            this.udpClient = null;
        }
        /// <summary>
        /// start at the specified endpoint<para/>
        /// the underlayer transport must be TcpServer, UdpServer or NetbiosServer.
        /// </summary>
        /// <param name="localEndPoint">
        /// an object that specifies the listener.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when localEndPoint is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// thrown when localEndPoint is not an IPEndPoint/port.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the udp client is started.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not cleanup.
        /// </exception>
        public void Start(object localEndPoint)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("UdpClientTransport");
            }

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

            if (this.udpClient != null)
            {
                throw new InvalidOperationException("udp client is started.");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the received thread does not cleanup.");
            }

            IPEndPoint requiredLocalEP = localEndPoint as IPEndPoint;
            if (requiredLocalEP == null)
            {
                if (this.socketConfig.LocalIpAddress == null)
                {
                    this.socketConfig.LocalIpAddress = IPAddress.Any;
                }
                requiredLocalEP = Utility.GetEndPointByPort(this.socketConfig, localEndPoint);
            }

            this.socketConfig.LocalIpAddress = requiredLocalEP.Address;
            this.socketConfig.LocalIpPort = requiredLocalEP.Port;

            this.eventQueue.Clear();
            this.packetCache.Clear();
            this.buffer.Clear();

            bool isLspHooked;
            bool isBlocking;
            StackTransportType type = this.socketConfig.Type;

            // get the replaced endpoint from LSP.
            IPEndPoint actualListenedLocalEP =
                LspConsole.Instance.GetReplacedEndPoint(type, requiredLocalEP, out isLspHooked, out isBlocking);

            // store the lsp state.
            this.lspHooked = isLspHooked;

            this.udpClient = new UdpClient(actualListenedLocalEP);

            if (socketConfig.LocalIpPort == 0)
            {
                //If set local Port to 0, a free port will be selected
                socketConfig.LocalIpPort = (udpClient.Client.LocalEndPoint as IPEndPoint).Port;
            }

            // if LSP is enabled, intercept the traffic
            if (isLspHooked)
            {
                LspConsole.Instance.InterceptTraffic(type, isBlocking, requiredLocalEP, (IPEndPoint)this.udpClient.Client.LocalEndPoint);
            }

            this.thread = new ThreadManager(this.UdpClientReceiveLoop, this.UnblockReceiveThread);
            this.thread.Start();
        }
        public static void Visit(
            IVisitorUdpReceiveLoop host, ITransport server,
            UdpClient udpClient, ThreadManager thread, bool isLspHooked)
        {
            byte[] data = null;

            // get the endpoint of tcp client.
            IPEndPoint localEP = host.LspHookedLocalEP;

            while (!thread.ShouldExit)
            {
                try
                {
                    // received data from server.
                    IPEndPoint remoteEP = null;

                    data = udpClient.Receive(ref remoteEP);

                    if (data == null)
                    {
                        break;
                    }

                    if(isLspHooked)
                    {
                        int numBytesReceived = data.Length;

                        if (numBytesReceived < Marshal.SizeOf(typeof(LspUdpHeader)))
                        {
                            throw new FormatException("Invalid LSP udp packet received");
                        }

                        //Get udp header
                        byte[] udpHeaderBuffer = ArrayUtility.SubArray(data, 0, Marshal.SizeOf(typeof(LspUdpHeader)));

                        IntPtr p = Marshal.AllocHGlobal(udpHeaderBuffer.Length);
                        Marshal.Copy(udpHeaderBuffer, 0, p, udpHeaderBuffer.Length);

                        LspUdpHeader udpHeader = (LspUdpHeader)Marshal.PtrToStructure(p, typeof(LspUdpHeader));
                        Marshal.FreeHGlobal(p);

                        //get address
                        byte[] srcAddressArray = ArrayUtility.SubArray<byte>(data,
                            Marshal.SizeOf(typeof(LspUdpHeader)), udpHeader.HeaderLength -
                            Marshal.SizeOf(typeof(LspUdpHeader)));
                        string srcAddress = Encoding.ASCII.GetString(srcAddressArray);

                        //replacement
                        numBytesReceived = numBytesReceived - udpHeader.HeaderLength;
                        byte[] msgBody = new byte[numBytesReceived];
                        Array.Copy(data, udpHeader.HeaderLength, msgBody, 0, numBytesReceived);

                        //endPoint is real remote client endpoint, remoteEP is LspDll's endpoint
                        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(srcAddress), udpHeader.Port);

                        //map from the real endpoint to the lsp dll endpoint
                        //when sending response to the real endpoint, response will be sent to the lsp dll endpoint
                        //and the lsp dll will relay it to the real endpoint
                        LspConsole.Instance.SetMappedIPEndPoint(
                            (IPEndPoint)udpClient.Client.LocalEndPoint, (IPEndPoint)endPoint,
                            (IPEndPoint)remoteEP, StackTransportType.Udp);

                        host.VisitorAddReceivedData(msgBody, localEP, (IPEndPoint)endPoint);
                    }
                    else
                    {
                        host.VisitorAddReceivedData(data, localEP, remoteEP);
                    }
                }
                catch (Exception ex)
                {
                    // handle exception event, return.
                    server.AddEvent(new TransportEvent(EventType.Exception, localEP, ex));

                    break;
                }
            }
        }
        /// <summary>
        /// disconnect from remote host.<para/>
        /// the underlayer transport must be TcpClient, NetbiosClient, TcpServer or NetbiosServer.<para/>
        /// client side will disconnect the connection to server.<para/>
        /// server side will disconnect all client connection.
        /// </summary>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when tcp client is not connected to server, must invoke Connect() first.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not initialize.
        /// </exception>
        public void Disconnect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("TcpClientTransport");
            }

            if (this.stream == null)
            {
                throw new InvalidOperationException(
                    "tcp client is not connected to server, must invoke Connect() first.");
            }

            if (this.thread == null)
            {
                throw new InvalidOperationException("the received thread does not initialize.");
            }

            this.thread.Dispose();
            this.thread = null;

            this.stream.Close();
            this.stream = null;
        }
        /// <summary>
        /// connect to remote endpoint.<para/>
        /// the underlayer transport must be TcpClient, UdpClient or NetbiosClient.
        /// </summary>
        /// <returns>
        /// the remote endpoint of the connection.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// thrown when tcp client is connected to server.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not cleanup.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the remote ip address is null.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public object Connect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("TcpClientTransport");
            }

            if (this.stream != null)
            {
                throw new InvalidOperationException("tcp client is connected to server.");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the received thread does not cleanup.");
            }

            if (this.socketConfig.RemoteIpAddress == null)
            {
                throw new InvalidOperationException("the remote ip address is null.");
            }

            this.buffer = new BytesBuffer();

            this.eventQueue.Clear();
            this.packetCache.Clear();

            TcpClient tcpClient;

            if (this.socketConfig.LocalIpAddress != null)
            {
                tcpClient = new TcpClient(new IPEndPoint(this.socketConfig.LocalIpAddress, 0));
            }
            else
            {
                tcpClient = new TcpClient(this.socketConfig.RemoteIpAddress.AddressFamily);
            }

            // The Timeout be set to TimeSpan.Zero if it is not initialized.
            if (this.socketConfig.Timeout != TimeSpan.Zero)
            {
                IAsyncResult result = tcpClient.BeginConnect(this.socketConfig.RemoteIpAddress, this.socketConfig.RemoteIpPort, new AsyncCallback(TcpConnectCallback), tcpClient);
                result.AsyncWaitHandle.WaitOne(this.socketConfig.Timeout, true);
                if (!tcpClient.Connected)
                {
                    throw new TimeoutException(string.Format(CultureInfo.InvariantCulture, "Failed to connect server {0}:{1} within {2} milliseconds.", this.socketConfig.RemoteIpAddress,this.socketConfig.RemoteIpPort, this.socketConfig.Timeout.TotalMilliseconds));
                }
            }
            else
            {
                tcpClient.Connect(new IPEndPoint(this.socketConfig.RemoteIpAddress, this.socketConfig.RemoteIpPort));
            }
            this.localEndPoint = tcpClient.Client.LocalEndPoint;
            this.remoteEndPoint = tcpClient.Client.RemoteEndPoint;
            this.stream = tcpClient.GetStream();

            if (this.sslProvider != null)
            {
                SslStream sslStream = new SslStream(this.stream);

                if (this.sslProvider.Certificate == null)
                {
                    sslStream.AuthenticateAsClient(this.sslProvider.TargetHost);
                }
                else
                {
                    sslStream.AuthenticateAsClient(this.sslProvider.TargetHost,
                        new X509CertificateCollection(new X509Certificate[] { this.sslProvider.Certificate }),
                        this.sslProvider.EnabledSslProtocols, false);
                }

                this.stream = sslStream;
            }

            this.thread = new ThreadManager(TcpClientReceiveLoop, UnblockReceiveThread);
            this.thread.Start();

            return this.localEndPoint;
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="localEndPoint">
        /// an int value that specifies the session id of connection.
        /// </param>
        /// <param name="remoteEndPoint">
        /// an int value that specifies the session id of connection.
        /// </param>
        /// <param name="server">
        /// NetbiosServerTransport object that specifies the netbios server transport.
        /// </param>
        /// <param name="transport">
        /// a NetbiosTransport object that specifies the owner of client.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// thrown when the server is null!
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when the transport is null!
        /// </exception>
        public NetbiosServerConnection(
            int localEndPoint, int remoteEndPoint, NetbiosServerTransport server, NetbiosTransport transport)
        {
            if (server == null)
            {
                throw new ArgumentNullException("server");
            }

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

            this.localEndPoint = localEndPoint;
            this.remoteEndPoint = remoteEndPoint;
            this.server = server;
            this.transport = transport;

            this.thread = new ThreadManager(NetbiosServerConnectionReceiveLoop, Unblock);
            this.buffer = new BytesBuffer();
        }
        /// <summary>
        /// connect to remote endpoint.<para/>
        /// the underlayer transport must be TcpClient, UdpClient or NetbiosClient.
        /// </summary>
        /// <returns>
        /// the remote endpoint of the connection.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// thrown when tcp client is connected to server.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not cleanup.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the remote ip address is null.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public object Connect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("TcpClientTransport");
            }

            if (this.stream != null)
            {
                throw new InvalidOperationException("tcp client is connected to server.");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the received thread does not cleanup.");
            }

            if (this.socketConfig.RemoteIpAddress == null)
            {
                throw new InvalidOperationException("the remote ip address is null.");
            }

            this.buffer = new BytesBuffer();

            this.eventQueue.Clear();
            this.packetCache.Clear();

            TcpClient tcpClient;

            if (this.socketConfig.LocalIpAddress != null)
            {
                tcpClient = new TcpClient(new IPEndPoint(this.socketConfig.LocalIpAddress, 0));
            }
            else
            {
                tcpClient = new TcpClient(this.socketConfig.RemoteIpAddress.AddressFamily);
            }

            // The Timeout be set to TimeSpan.Zero if it is not initialized.
            if (this.socketConfig.Timeout != TimeSpan.Zero)
            {
                IAsyncResult result = tcpClient.BeginConnect(this.socketConfig.RemoteIpAddress, this.socketConfig.RemoteIpPort, new AsyncCallback(TcpConnectCallback), tcpClient);
                result.AsyncWaitHandle.WaitOne(this.socketConfig.Timeout, true);
                if (!tcpClient.Connected)
                {
                    throw new TimeoutException(string.Format(CultureInfo.InvariantCulture, "Failed to connect server {0}:{1} within {2} milliseconds.", this.socketConfig.RemoteIpAddress, this.socketConfig.RemoteIpPort, this.socketConfig.Timeout.TotalMilliseconds));
                }
            }
            else
            {
                tcpClient.Connect(new IPEndPoint(this.socketConfig.RemoteIpAddress, this.socketConfig.RemoteIpPort));
            }
            this.localEndPoint  = tcpClient.Client.LocalEndPoint;
            this.remoteEndPoint = tcpClient.Client.RemoteEndPoint;
            this.stream         = tcpClient.GetStream();

            if (this.sslProvider != null)
            {
                SslStream sslStream = new SslStream(this.stream);

                if (this.sslProvider.Certificate == null)
                {
                    sslStream.AuthenticateAsClient(this.sslProvider.TargetHost);
                }
                else
                {
                    sslStream.AuthenticateAsClient(this.sslProvider.TargetHost,
                                                   new X509CertificateCollection(new X509Certificate[] { this.sslProvider.Certificate }),
                                                   this.sslProvider.EnabledSslProtocols, false);
                }

                this.stream = sslStream;
            }

            this.thread = new ThreadManager(TcpClientReceiveLoop, UnblockReceiveThread);
            this.thread.Start();

            return(this.localEndPoint);
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="udpListener">
        /// a UdpClient that specifies the listener.
        /// </param>
        /// <param name="udpServerTransport">
        /// a UdpServerTransport that represents the owner of listener.
        /// </param>
        /// <param name="isLspHooked">
        /// a bool value that indicates whether lsp hooked the transport.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// thrown when tcpListener is null.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when tcpServerTransport is null.
        /// </exception>
        public UdpServerListener(UdpClient udpListener, UdpServerTransport udpServerTransport, bool isLspHooked)
        {
            if (udpListener == null)
            {
                throw new ArgumentNullException("udpListener");
            }

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

            this.lspHooked = isLspHooked;
            this.listener = udpListener;
            this.server = udpServerTransport;
            this.thread = new ThreadManager(UdpServerListenerReceiveLoop, Unblock);
        }
        /// <summary>
        /// start at the specified endpoint<para/>
        /// the underlayer transport must be TcpServer, UdpServer or NetbiosServer.
        /// </summary>
        /// <param name="localEndPoint">
        /// an object that specifies the listener.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when localEndPoint is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// thrown when localEndPoint is not an IPEndPoint/port.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the udp client is started.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not cleanup.
        /// </exception>
        public void Start(object localEndPoint)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("UdpClientTransport");
            }

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

            if (this.udpClient != null)
            {
                throw new InvalidOperationException("udp client is started.");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the received thread does not cleanup.");
            }

            IPEndPoint requiredLocalEP = localEndPoint as IPEndPoint;

            if (requiredLocalEP == null)
            {
                if (this.socketConfig.LocalIpAddress == null)
                {
                    this.socketConfig.LocalIpAddress = IPAddress.Any;
                }
                requiredLocalEP = Utility.GetEndPointByPort(this.socketConfig, localEndPoint);
            }

            this.socketConfig.LocalIpAddress = requiredLocalEP.Address;
            this.socketConfig.LocalIpPort    = requiredLocalEP.Port;

            this.eventQueue.Clear();
            this.packetCache.Clear();
            this.buffer.Clear();

            bool isLspHooked;
            bool isBlocking;
            StackTransportType type = this.socketConfig.Type;

            // get the replaced endpoint from LSP.
            IPEndPoint actualListenedLocalEP =
                LspConsole.Instance.GetReplacedEndPoint(type, requiredLocalEP, out isLspHooked, out isBlocking);

            // store the lsp state.
            this.lspHooked = isLspHooked;

            this.udpClient = new UdpClient(actualListenedLocalEP);

            if (socketConfig.LocalIpPort == 0)
            {
                //If set local Port to 0, a free port will be selected
                socketConfig.LocalIpPort = (udpClient.Client.LocalEndPoint as IPEndPoint).Port;
            }

            // if LSP is enabled, intercept the traffic
            if (isLspHooked)
            {
                LspConsole.Instance.InterceptTraffic(type, isBlocking, requiredLocalEP, (IPEndPoint)this.udpClient.Client.LocalEndPoint);
            }

            this.thread = new ThreadManager(this.UdpClientReceiveLoop, this.UnblockReceiveThread);
            this.thread.Start();
        }
        /// <summary>
        /// stop the listener: stop the listening thread.
        /// </summary>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public void Stop()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("NetbiosServerListener");
            }

            if (this.thread == null)
            {
                return;
            }

            this.thread.Stop();
            this.thread = null;
        }
        /// <summary>
        /// connect to remote endpoint.<para/>
        /// the underlayer transport must be NetbiosTransport, UdpClient or NetbiosClient.
        /// </summary>
        /// <returns>
        /// the remote endpoint of the connection.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// thrown when netbios client is connected to server.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not cleanup.
        /// </exception>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public object Connect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("NetbiosClientTransport");
            }

            if (this.transport != null)
            {
                throw new InvalidOperationException("netbios client is connected to server.");
            }

            if (this.thread != null)
            {
                throw new InvalidOperationException("the received thread does not cleanup.");
            }

            this.buffer = new BytesBuffer();

            this.eventQueue.Clear();
            this.packetCache.Clear();

            this.transport = new NetbiosTransport(
                this.netbiosConfig.LocalNetbiosName, this.netbiosConfig.AdapterIndex, (ushort)this.netbiosConfig.BufferSize,
                (byte)this.netbiosConfig.MaxSessions, (byte)this.netbiosConfig.MaxNames);

            this.remoteEndPoint = this.transport.Connect(this.netbiosConfig.RemoteNetbiosName);
            this.localEndPoint = this.transport.NcbNum;

            this.thread = new ThreadManager(NetbiosClientReceiveLoop, UnblockReceiveThread);
            this.thread.Start();

            return this.remoteEndPoint;
        }
        /// <summary>
        /// disconnect from remote host.<para/>
        /// the underlayer transport must be NetbiosTransport, NetbiosClient, NetbiosServer or NetbiosServer.<para/>
        /// client side will disconnect the connection to server.<para/>
        /// server side will disconnect all client connection.
        /// </summary>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when netbios client is not connected to server, must invoke Connect() first.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// thrown when the received thread does not initialize.
        /// </exception>
        public void Disconnect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("NetbiosClientTransport");
            }

            if (this.transport == null)
            {
                throw new InvalidOperationException(
                    "netbios client is not connected to server, must invoke Connect() first.");
            }

            if (this.thread == null)
            {
                throw new InvalidOperationException("the received thread does not initialize.");
            }

            this.transport.Disconnect(this.remoteEndPoint);

            this.thread.Dispose();
            this.thread = null;

            this.transport.Dispose();
            this.transport = null;
        }
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="tcpClient">
        /// a TcpClient object that specifies the tcp client.
        /// </param>
        /// <param name="tcpServerTransport">
        /// a TcpServerTransport that represents the owner of listener.
        /// </param>
        /// <param name="lspHookedLocalEP">
        /// an IPEndPoint object that specifies the local endpoint.<para/>
        /// if LSP hooked, return the required local endpoint.<para/>
        /// otherwise, return the actual listened local endpoint.
        /// </param>
        /// <param name="isLspHooked">
        /// a bool value that indicates whether lsp hooked the transport.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// thrown when the tcpClient is null!
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when the tcpServerTransport is null!
        /// </exception>
        public TcpServerConnection(
            TcpClient tcpClient, TcpServerTransport tcpServerTransport, IPEndPoint lspHookedLocalEP, bool isLspHooked)
        {
            if (tcpClient == null)
            {
                throw new ArgumentNullException("tcpClient");
            }

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

            this.isAForwarderChannel = false;
            this.lspHooked = isLspHooked;
            this.stream = tcpClient.GetStream();
            this.server = tcpServerTransport;
            this.thread = new ThreadManager(TcpServerConnectionReceiveLoop, Unblock);
            this.buffer = new BytesBuffer();

            if (isLspHooked)
            {
                IPEndPoint destinationEndpoint;
                this.localEndPoint = lspHookedLocalEP;
                this.remoteEndPoint = LspConsole.Instance.RetrieveRemoteEndPoint(tcpClient.Client, out destinationEndpoint);

                //if equals, it means this is a forwarder channel.
                if (remoteEndPoint.Equals(destinationEndpoint))
                {
                    isAForwarderChannel = true;
                }
            }
            else
            {
                this.localEndPoint = tcpClient.Client.LocalEndPoint as IPEndPoint;
                this.remoteEndPoint = tcpClient.Client.RemoteEndPoint as IPEndPoint;
            }
        }