/// <summary>
        /// Reads bytes up to and including the passed data paramter, which acts as a separator.
        /// The bytes and the separator are returned by the delegate method.
        /// 
        /// If you pass null or zero-length data as the separator, this method will do nothing.
        /// To read a line from the socket, use the line separator (e.g. CRLF for HTTP) as the data parameter.
        /// Note that this method is not character-set aware, so if a separator can occur naturally
        /// as part of the encoding for a character, the read will prematurely end.
        /// </summary>
        /// <param name="term">
        ///		The separator/delimeter to use.
        /// </param>
        /// <param name="timeout">
        ///		Timeout in milliseconds. Specify negative value for no timeout.
        /// </param>
        /// <param name="tag">
        ///		Tag to identify read request.
        /// </param>
        public void Read(byte[] term, int timeout, long tag)
        {
            if ((term == null) || (term.Length == 0)) return;
            if ((flags & kForbidReadsWrites) > 0) return;

            MutableData buffer = new MutableData(0);

            // readQueue is synchronized
            readQueue.Enqueue(new AsyncReadPacket(buffer, timeout, -1, tag, false, false, term));

            // Queue a call to MaybeDequeueRead
            ThreadPool.QueueUserWorkItem(new WaitCallback(MaybeDequeueRead));
        }
        /// <summary>
        /// Reads a certain number of bytes, and calls the delegate method when those bytes have been read.
        /// If length is 0, this method does nothing and no delgate methods are called.
        /// </summary>
        /// <param name="length">
        ///		The number of bytes to read.
        /// </param>
        /// <param name="timeout">
        ///		Timeout in milliseconds. Specify negative value for no timeout.
        /// </param>
        /// <param name="tag">
        ///		Tag to identify read request.
        ///	</param>
        public void Read(int length, int timeout, long tag)
        {
            if (length <= 0) return;
            if ((flags & kForbidReadsWrites) > 0) return;

            MutableData buffer = new MutableData(length);

            // readQueue is synchronized
            readQueue.Enqueue(new AsyncReadPacket(buffer, timeout, -1, tag, false, true, null));

            // Queue a call to maybeDequeueRead
            ThreadPool.QueueUserWorkItem(new WaitCallback(MaybeDequeueRead));
        }
 public AsyncReadPacket(MutableData buffer,
     int timeout,
     int maxLength,
     long tag,
     bool readAllAvailableData,
     bool fixedLengthRead,
     byte[] term)
 {
     this.buffer = buffer;
     this.bytesDone = 0;
     this.bytesProcessing = 0;
     this.timeout = timeout;
     this.maxLength = maxLength;
     this.tag = tag;
     this.readAllAvailableData = readAllAvailableData;
     this.fixedLengthRead = fixedLengthRead;
     this.term = term;
 }
        /// <summary>
        /// This method extracts any unprocessed data, and makes it available to the client.
        /// 
        /// Called solely from CloseWithException, which is only called from thread safe methods.
        /// </summary>
        private void RecoverUnreadData()
        {
            if (currentRead != null)
            {
                // We never finished the current read.

                int bytesAvailable = currentRead.bytesDone + currentRead.bytesProcessing;

                if (readOverflow == null)
                {
                    readOverflow = new MutableData(currentRead.buffer, 0, bytesAvailable);
                }
                else
                {
                    // We need to move the data into the front of the read overflow
                    readOverflow.InsertData(0, currentRead.buffer, 0, bytesAvailable);
                }
            }
        }
        /// <summary>
        /// This method fills the currentRead buffer with data from the readOverflow variable.
        /// After this is properly completed, DoFinishRead is called to process the bytes.
        /// 
        /// This method is called from MaybeDequeueRead().
        /// 
        /// The above method is thread safe, so this method is inherently thread safe.
        /// It is not explicitly thread safe though, and should not be called outside thread safe methods.
        /// </summary>
        private void DoReadOverflow()
        {
            Debug.Assert(currentRead.bytesDone == 0);
            Debug.Assert(readOverflow.Length > 0);

            try
            {
                if (currentRead.readAllAvailableData)
                {
                    // We're supposed to read what's available.
                    // What we have in the readOverflow is what we have available, so just use it.

                    currentRead.buffer = readOverflow;
                    currentRead.bytesProcessing = readOverflow.Length;

                    readOverflow = null;
                }
                else if (currentRead.fixedLengthRead)
                {
                    // We're reading a certain length of data.

                    if (currentRead.buffer.Length < readOverflow.Length)
                    {
                        byte[] src = readOverflow.ByteArray;
                        byte[] dst = currentRead.buffer.ByteArray;

                        Buffer.BlockCopy(src, 0, dst, 0, dst.Length);

                        currentRead.bytesProcessing = dst.Length;

                        readOverflow.TrimStart(dst.Length);

                        // Note that this is the only case in which the readOverflow isn't emptied.
                        // This is OK because the read is guaranteed to finish in DoFinishRead().
                    }
                    else
                    {
                        byte[] src = readOverflow.ByteArray;
                        byte[] dst = currentRead.buffer.ByteArray;

                        Buffer.BlockCopy(src, 0, dst, 0, src.Length);

                        currentRead.bytesProcessing = src.Length;

                        readOverflow = null;
                    }
                }
                else
                {
                    // We're reading up to a termination sequence
                    // So we can just set the currentRead buffer to the readOverflow
                    // and the DoStartRead method will automatically handle any further overflow.

                    currentRead.buffer = readOverflow;
                    currentRead.bytesProcessing = readOverflow.Length;

                    readOverflow = null;
                }

                // At this point we've filled a currentRead buffer with some data
                // And the currentRead.bytesProcessing is set to the amount of data we filled it with
                // It's now time to process the data.
                DoFinishRead();
            }
            catch (Exception e)
            {
                CloseWithException(e);
            }
        }
        /// <summary>
        /// This method is called when either:
        ///  A) a new read is taken from the read queue
        ///  B) or when data has just been read from the stream.
        /// 
        /// More specifically, it is called from either:
        ///  A) DoReadOverflow()
        ///  B) stream_DidRead()
        /// 
        /// The above methods are thread safe, so this method is inherently thread safe.
        /// It is not explicitly thread safe though, and should not be called outside thread safe methods.
        /// </summary>
        private void DoFinishRead()
        {
            Debug.Assert(currentRead != null);
            Debug.Assert(currentRead.bytesProcessing > 0);

            int totalBytesRead = 0;
            bool done = false;
            bool maxoutError = false;

            if(currentRead.readAllAvailableData)
            {
                // We're done because we read everything that was available (up to a max size).
                currentRead.bytesDone += currentRead.bytesProcessing;
                totalBytesRead = currentRead.bytesProcessing;
                currentRead.bytesProcessing = 0;

                done = true;
            }
            else if (currentRead.fixedLengthRead)
            {
                // We're reading up to a fixed size
                currentRead.bytesDone += currentRead.bytesProcessing;
                totalBytesRead = currentRead.bytesProcessing;
                currentRead.bytesProcessing = 0;

                done = currentRead.buffer.Length == currentRead.bytesDone;
            }
            else
            {
                // We're reading up to a terminator
                // So let's start searching for the termination sequence in the new data

                while (!done && !maxoutError && (currentRead.bytesProcessing > 0))
                {
                    currentRead.bytesDone++;
                    totalBytesRead++;
                    currentRead.bytesProcessing--;

                    bool match = currentRead.bytesDone >= currentRead.term.Length;
                    int offset = currentRead.bytesDone - currentRead.term.Length;

                    for (int i = 0; match && i < currentRead.term.Length; i++)
                    {
                        match = (currentRead.term[i] == currentRead.buffer[offset + i]);
                    }
                    done = match;

                    if (!done && (currentRead.maxLength >= 0) && (currentRead.bytesDone >= currentRead.maxLength))
                    {
                        maxoutError = true;
                    }
                }
            }

            // If there was any overflow data, extract it and save it.
            // This may occur if our read maxed out.
            // Or if we received Y bytes, but only needed X bytes to finish the read (X < Y).
            if (currentRead.bytesProcessing > 0)
            {
                readOverflow = new MutableData(currentRead.buffer, currentRead.bytesDone, currentRead.bytesProcessing);
                currentRead.bytesProcessing = 0;
            }

            if (done)
            {
                // Truncate any excess unused buffer space in the read packet
                currentRead.buffer.SetLength(currentRead.bytesDone);

                CompleteCurrentRead();
                ThreadPool.QueueUserWorkItem(new WaitCallback(MaybeDequeueRead));
            }
            else if (maxoutError)
            {
                CloseWithException(GetReadMaxedOutException());
            }
            else
            {
                // We're not done yet, but we have read in some bytes
                OnSocketDidReadPartial(totalBytesRead, currentRead.tag);

                // It appears that we've read all immediately available data on the socket
                // So begin asynchronously reading data again
                DoStartRead();
            }
        }