Example #1
0
        /// <summary>
        /// add data from owner to buffer, and the sequence information to data sequence.
        /// </summary>
        /// <param name="owner">
        /// an object that specifies the owner of data or event.<para/>
        /// if data, the length must not be null/byte[0].<para/>
        /// if event, it's a TransportEvent object and data must be byte[0].
        /// </param>
        /// <param name="data">
        /// a bytes array that contains the data.<para/>
        /// if event, it can be null.
        /// </param>
        /// <param name="buffer">
        /// a BytesBuffer object that stores the data.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when data is null.
        /// </exception>
        public void Add(object owner, byte[] data, BytesBuffer buffer)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("DataSequence");
            }

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

            lock (this.objectLock)
            {
                // must add data to buffer, then add the sequence item to triggle received event.
                if (data.Length > 0)
                {
                    buffer.Add(data);
                    this.dataLength += data.Length;
                    this.sequence.Add(new SequenceItem(owner, data.Length));
                }
                else
                {
                    this.sequence.Add(new SequenceItem(owner, 0));
                }

                // if the owner in the search history, remove it to make it can be searched by user again.
                if (this.visitedOwners.Contains(owner))
                {
                    this.visitedOwners.Remove(owner);
                }

                this.receivedEvent.Set();
            }
        }
        /// <summary>
        /// expect packet from transport.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="decoder">
        /// a DecodePacketCallback delegate that is used to decode packet from buffer.
        /// </param>
        /// <param name="endpoint">
        /// an object that specifies the endpoint for decoder.<para/>
        /// remember: this endpoint must be the endpoint that is returned to the user,
        /// that is the endpoint return by Connect().
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that indicates the timeout to expect event.
        /// </param>
        /// <param name="packetCache">
        /// a list that contains the stackpackets.
        /// </param>
        /// <returns>
        /// a StackPacket object that specifies the received packet.
        /// </returns>
        public static StackPacket Visit(
            BytesBuffer buffer, DecodePacketCallback decoder, object endpoint,
            TimeSpan timeout, SyncFilterQueue <StackPacket> packetCache)
        {
            // get the packet in packet list.
            if (packetCache.Count > 0)
            {
                return(Utility.GetOne <StackPacket>(packetCache, null));
            }

            // the consumed length of decoder.
            int consumedLength = 0;

            // decode packet, return null or at least one packet.
            StackPacket[] packets = ExpectMultiPacketsVisitor.Visit(
                buffer, decoder, endpoint, timeout, out consumedLength);

            // packet is null, buffer is closed, no packet will come.
            if (packets == null)
            {
                return(null);
            }

            // if packet arrived, add to packet list, and return the first.
            foreach (StackPacket packet in packets)
            {
                packetCache.Enqueue(packet);
            }

            return(Utility.GetOne <StackPacket>(packetCache, null));
        }
Example #3
0
        /// <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 = 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;
            }
        }
Example #4
0
        /// <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>
        /// 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="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public object Connect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("StreamTransport");
            }

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

            this.buffer = new BytesBuffer();

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

            this.localEndPoint = StreamTransport.StreamIdentity;
            this.stream        = this.streamConfig.Stream;

            this.thread = new ThreadManager(StreamReceiveLoop);
            this.thread.Start();

            return(this.localEndPoint);
        }
Example #6
0
        public QuicServerConnection(
            QuicConnection clientConnection,
            QuicServerTransport quicServerTransport,
            IPEndPoint lspHookedLocalEP,
            bool isLspHooked)
        {
            if (clientConnection == null)
            {
                throw new ArgumentNullException(nameof(clientConnection));
            }

            if (quicServerTransport == null)
            {
                throw new ArgumentNullException(nameof(quicServerTransport));
            }

            if (!clientConnection.Connected)
            {
                throw new ArgumentException($"Client has already disconnected");
            }

            this.server = quicServerTransport;

            this.connection = clientConnection;

            this.stream = clientConnection.OpenBidirectionalStream();

            this.thread = new ThreadManager(QuicServerConnectionReceiveLoop, Unblock);

            this.buffer = new BytesBuffer();

            this.localEndPoint = clientConnection.LocalEndPoint as IPEndPoint;

            this.remoteEndPoint = clientConnection.RemoteEndPoint as IPEndPoint;
        }
        /// <summary>
        /// to receive bytes from connection.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that specifies the timeout for this operation.
        /// </param>
        /// <param name="maxCount">
        /// an int value that specifies the maximum count of expect bytes.
        /// </param>
        /// <returns>
        /// a bytes array that contains the received bytes.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// thrown when the connection is closed, there is no data anymore.
        /// </exception>
        public static byte[] Visit(BytesBuffer buffer, TimeSpan timeout, int maxCount)
        {
            bool bufferClosed;
            // get specified length data in buffer, at-least one byte.
            byte[] data = buffer.Read(timeout, maxCount, 0, out bufferClosed);

            if (data.Length == 0)
            {
                throw new InvalidOperationException("the connection is closed, there is no data anymore.");
            }

            buffer.Remove(data.Length);

            return data;
        }
        /// <summary>
        /// to receive bytes from connection.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that specifies the timeout for this operation.
        /// </param>
        /// <param name="maxCount">
        /// an int value that specifies the maximum count of expect bytes.
        /// </param>
        /// <returns>
        /// a bytes array that contains the received bytes.
        /// </returns>
        /// <exception cref="InvalidOperationException">
        /// thrown when the connection is closed, there is no data anymore.
        /// </exception>
        public static byte[] Visit(BytesBuffer buffer, TimeSpan timeout, int maxCount)
        {
            bool bufferClosed;

            // get specified length data in buffer, at-least one byte.
            byte[] data = buffer.Read(timeout, maxCount, 0, out bufferClosed);

            if (data.Length == 0)
            {
                throw new InvalidOperationException("the connection is closed, there is no data anymore.");
            }

            buffer.Remove(data.Length);

            return(data);
        }
        /// <summary>
        /// expect packet from transport.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="decoder">
        /// a DecodePacketCallback delegate that is used to decode packet from buffer.
        /// </param>
        /// <param name="endpoint">
        /// an object that specifies the endpoint for decoder.<para/>
        /// remember: this endpoint is the identity endpoint of tcp/netbios connection,
        /// that is the remote endpoint of connection.
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that indicates the timeout to expect event.
        /// </param>
        /// <param name="consumedLength">
        /// return an int value that specifies the consumed length.
        /// </param>
        /// <returns>
        /// a StackPacket object that specifies the received packet.
        /// </returns>
        public static StackPacket[] Visit(
            BytesBuffer buffer, DecodePacketCallback decoder, object endpoint,
            TimeSpan timeout, out int consumedLength)
        {
            bool bufferClosed;

            // get all data in buffer, at-least one byte.
            byte[] data = buffer.Read(timeout, BytesBuffer.MaxCount, 0, out bufferClosed);

            // the end time for operation.
            DateTime endTime = DateTime.Now + timeout;

            while (true)
            {
                // decode packets using data in buffer.
                int expectedLength = 0;

                // decode data.
                if (data.Length > 0)
                {
                    StackPacket[] packets = decoder(endpoint, data, out consumedLength, out expectedLength);

                    buffer.Remove(consumedLength);

                    // if packet arrived, add to packet list, and return the first.
                    if (packets != null && packets.Length > 0)
                    {
                        return(packets);
                    }
                }

                // if buffer is closed, no data will come, return null.
                if (bufferClosed)
                {
                    consumedLength = 0;
                    return(null);
                }

                // wait for the next data coming.
                data = buffer.Read(endTime - DateTime.Now, BytesBuffer.MaxCount, data.Length, out bufferClosed);
            }
        }
        /// <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.Dispose();
                        this.stream = 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>
        /// consturctor.
        /// </summary>
        /// <param name="transportConfig">
        /// a TransportConfig object that contains the config.
        /// </param>
        /// <param name="decodePacketCallback">
        /// a DecodePacketCallback delegate that is used to decode the packet from bytes.
        /// </param>
        /// <exception cref="ArgumentException">
        /// thrown when transportConfig is not StreamConfig
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when transportConfig is null.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when decodePacketCallback is null.
        /// </exception>
        public StreamTransport(TransportConfig transportConfig, DecodePacketCallback decodePacketCallback)
        {
            if (transportConfig == null)
            {
                throw new ArgumentNullException("transportConfig");
            }

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

            this.UpdateConfig(transportConfig);

            this.buffer      = new BytesBuffer();
            this.eventQueue  = new SyncFilterQueue <TransportEvent>();
            this.packetCache = new SyncFilterQueue <StackPacket>();

            this.decoder = decodePacketCallback;
        }
Example #12
0
        /// <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>
        /// 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;
            }
        }
        /// <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>
        /// expect packet from transport.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="decoder">
        /// a DecodePacketCallback delegate that is used to decode packet from buffer.
        /// </param>
        /// <param name="endpoint">
        /// an object that specifies the endpoint for decoder.<para/>
        /// remember: this endpoint must be the endpoint that is returned to the user,
        /// that is the endpoint return by Connect().
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that indicates the timeout to expect event.
        /// </param>
        /// <param name="packetCache">
        /// a list that contains the stackpackets.
        /// </param>
        /// <returns>
        /// a StackPacket object that specifies the received packet.
        /// </returns>
        public static StackPacket Visit(
            BytesBuffer buffer, DecodePacketCallback decoder, object endpoint,
            TimeSpan timeout, SyncFilterQueue<StackPacket> packetCache)
        {
            // get the packet in packet list.
            if (packetCache.Count > 0)
            {
                return Utility.GetOne<StackPacket>(packetCache, null);
            }

            // the consumed length of decoder.
            int consumedLength = 0;

            // decode packet, return null or at least one packet.
            StackPacket[] packets = ExpectMultiPacketsVisitor.Visit(
                buffer, decoder, endpoint, timeout, out consumedLength);

            // packet is null, buffer is closed, no packet will come.
            if (packets == null)
            {
                return null;
            }

            // if packet arrived, add to packet list, and return the first.
            foreach (StackPacket packet in packets)
            {
                packetCache.Enqueue(packet);
            }

            return Utility.GetOne<StackPacket>(packetCache, 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;
            }
        }
        /// <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>
        /// add data from owner to buffer, and the sequence information to data sequence.
        /// </summary>
        /// <param name="owner">
        /// an object that specifies the owner of data or event.<para/>
        /// if data, the length must not be null/byte[0].<para/>
        /// if event, it's a TransportEvent object and data must be byte[0].
        /// </param>
        /// <param name="data">
        /// a bytes array that contains the data.<para/>
        /// if event, it can be null.
        /// </param>
        /// <param name="buffer">
        /// a BytesBuffer object that stores the data.
        /// </param>
        /// <exception cref="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when data is null.
        /// </exception>
        public void Add(object owner, byte[] data, BytesBuffer buffer)
        {
            if (disposed)
            {
                throw new ObjectDisposedException("DataSequence");
            }

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

            lock (this.objectLock)
            {
                // must add data to buffer, then add the sequence item to triggle received event.
                if (data.Length > 0)
                {
                    buffer.Add(data);
                    this.dataLength += data.Length;
                    this.sequence.Add(new SequenceItem(owner, data.Length));
                }
                else
                {
                    this.sequence.Add(new SequenceItem(owner, 0));
                }

                // if the owner in the search history, remove it to make it can be searched by user again.
                if (this.visitedOwners.Contains(owner))
                {
                    this.visitedOwners.Remove(owner);
                }

                this.receivedEvent.Set();
            }
        }
        /// <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>
        /// 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="ObjectDisposedException">
        /// thrown when this object is disposed.
        /// </exception>
        public object Connect()
        {
            if (disposed)
            {
                throw new ObjectDisposedException("StreamTransport");
            }

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

            this.buffer = new BytesBuffer();

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

            this.localEndPoint = StreamTransport.StreamIdentity;
            this.stream = this.streamConfig.Stream;

            this.thread = new ThreadManager(StreamReceiveLoop);
            this.thread.Start();

            return this.localEndPoint;
        }
        /// <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.Dispose();
                        this.stream = 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>
        /// constructor
        /// </summary>
        /// <param name="transportConfig">
        /// a TransportConfig object that contains the config.
        /// </param>
        /// <param name="decodePacketCallback">
        /// a DecodePacketCallback delegate that is used to decode the packet from bytes.
        /// </param>
        /// <exception cref="ArgumentException">
        /// thrown when transportConfig is not NetbiosTransportConfig
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when transportConfig is null.
        /// </exception>
        /// <exception cref="ArgumentNullException">
        /// thrown when decodePacketCallback is null.
        /// </exception>
        public NetbiosClientTransport(TransportConfig transportConfig, DecodePacketCallback decodePacketCallback)
        {
            if (transportConfig == null)
            {
                throw new ArgumentNullException("transportConfig");
            }

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

            this.UpdateConfig(transportConfig);

            this.buffer = new BytesBuffer();
            this.eventQueue = new SyncFilterQueue<TransportEvent>();
            this.packetCache = new SyncFilterQueue<StackPacket>();

            this.decoder = decodePacketCallback;
        }
        /// <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>
        /// 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>
        /// expect packet from transport.
        /// </summary>
        /// <param name="buffer">
        /// a BytesBuffer object that contains the received data from endpoint.
        /// </param>
        /// <param name="decoder">
        /// a DecodePacketCallback delegate that is used to decode packet from buffer.
        /// </param>
        /// <param name="endpoint">
        /// an object that specifies the endpoint for decoder.<para/>
        /// remember: this endpoint is the identity endpoint of tcp/netbios connection,
        /// that is the remote endpoint of connection.
        /// </param>
        /// <param name="timeout">
        /// a TimeSpan object that indicates the timeout to expect event.
        /// </param>
        /// <param name="consumedLength">
        /// return an int value that specifies the consumed length.
        /// </param>
        /// <returns>
        /// a StackPacket object that specifies the received packet.
        /// </returns>
        public static StackPacket[] Visit(
            BytesBuffer buffer, DecodePacketCallback decoder, object endpoint,
            TimeSpan timeout, out int consumedLength)
        {
            bool bufferClosed;
            // get all data in buffer, at-least one byte.
            byte[] data = buffer.Read(timeout, BytesBuffer.MaxCount, 0, out bufferClosed);

            // the end time for operation.
            DateTime endTime = DateTime.Now + timeout;

            while (true)
            {
                // decode packets using data in buffer.
                int expectedLength = 0;

                // decode data.
                if (data.Length > 0)
                {
                    StackPacket[] packets = decoder(endpoint, data, out consumedLength, out expectedLength);

                    buffer.Remove(consumedLength);

                    // if packet arrived, add to packet list, and return the first.
                    if (packets != null && packets.Length > 0)
                    {
                        return packets;
                    }
                }

                // if buffer is closed, no data will come, return null.
                if (bufferClosed)
                {
                    consumedLength = 0;
                    return null;
                }

                // wait for the next data coming.
                data = buffer.Read(endTime - DateTime.Now, BytesBuffer.MaxCount, data.Length, out bufferClosed);
            }
        }
        /// <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;
            }
        }