/// <summary>
        /// Notifies the framer instance that incoming data has been received from the stream. This method will invoke <see cref="MessageArrived"/> as necessary.
        /// </summary>
        /// <remarks>
        /// <para>This method may invoke <see cref="MessageArrived"/> zero or more times.</para>
        /// <para>Zero-length receives are ignored. May streams use a 0-length read to indicate the end of a stream, but the framer takes no action in this case.</para>
        /// </remarks>
        /// <param name="data">The data received from the stream. Cannot be null. May be a slice of the read buffer for the stream.</param>
        /// <exception cref="System.Net.ProtocolViolationException">If the data received is not a properly-formed message.</exception>
        public void DataReceived(IList <byte> data)
        {
            int i = 0;

            while (i != data.Count)
            {
                int bytesAvailable = data.Count - i;
                if (this.beginDelimiterIndex != this.beginDelimiter.Length)
                {
                    // We are searching for the begin delimiter
                    int bytesRequested = this.beginDelimiter.Length - this.beginDelimiterIndex;
                    int bytesUsed      = Math.Min(bytesRequested, bytesAvailable);

                    // Test if we have a (continuing) match
                    if (this.beginDelimiter.Slice(this.beginDelimiterIndex, bytesUsed).SequenceEqual(data.Slice(i, bytesUsed)))
                    {
                        this.beginDelimiterIndex += bytesUsed;
                        i += bytesUsed;
                    }
                    else
                    {
                        trace.TraceEvent(TraceEventType.Error, 0, "Message does not begin with start delimieter " + this.beginDelimiter.PrettyDump() + " in data " + data.Slice(i, bytesAvailable).PrettyDump());
                        throw new System.Net.ProtocolViolationException("Message does not begin with a start delimiter");
                    }
                }
                else
                {
                    // The begin delimiter has been found; we are reading data while searching for the end delimiter
                    if (this.endDelimiterIndex != 0)
                    {
                        // A partial end delimiter was found at the end of a previous chunk of incoming data; see if this data finishes the end delimiter
#if DEBUG
                        Debug.Assert(i == 0, "This branch should only be taken when a new chunk of incoming data has arrived.");
#endif
                        int bytesToMatch = Math.Min(this.endDelimiter.Length - this.endDelimiterIndex, bytesAvailable);
                        if (this.endDelimiter.Slice(this.endDelimiterIndex, bytesToMatch).SequenceEqual(data.Slice(0, bytesToMatch)))
                        {
                            // A partial or complete match was found
                            if (bytesToMatch == this.endDelimiter.Length - this.endDelimiterIndex)
                            {
                                // A complete match has been found: report the message and continue processing
                                if (this.MessageArrived != null)
                                {
                                    this.MessageArrived(this.dataBuffer);
                                }

                                i += bytesToMatch;
                                this.Reset();
                            }
                            else
                            {
                                // The partial match has been extended into a longer partial match (at the end of the data)
                                this.endDelimiterIndex += bytesAvailable;
                                return;
                            }
                        }
                        else
                        {
                            // This was a false partial end delimiter; prepend it to the incoming data and continue searching
                            List <byte> newData = new List <byte>(data.Count + this.endDelimiterIndex);
                            newData.AddRange(this.endDelimiter.Take(this.endDelimiterIndex));
                            newData.AddRange(data);
                            data = newData;

                            this.endDelimiterIndex = 0;
                        }
                    }
                    else
                    {
                        // The data buffer does not have an implicit partial end delimiter on it, so we can just search the incoming data
                        // Search for a complete end delimiter, or a partial end delimiter at the end of the incoming data
                        int endDelimiterFoundIndex;
                        for (endDelimiterFoundIndex = 0; endDelimiterFoundIndex != bytesAvailable; ++endDelimiterFoundIndex)
                        {
                            int bytesToMatch = Math.Min(this.endDelimiter.Length, bytesAvailable - endDelimiterFoundIndex);
                            if (this.endDelimiter.Slice(0, bytesToMatch).SequenceEqual(data.Slice(i + endDelimiterFoundIndex, bytesToMatch)))
                            {
                                // A partial or complete match was found
                                if (bytesToMatch == this.endDelimiter.Length)
                                {
                                    // A complete match was found: report the message and continue processing
                                    this.CheckMaxMessageSize(this.dataBuffer.Count, endDelimiterFoundIndex);
                                    IList <byte> message;
                                    if (this.dataBuffer.Count == 0)
                                    {
                                        message = data.Slice(i, endDelimiterFoundIndex);
                                    }
                                    else
                                    {
                                        message = ListExtensions.Concat(this.dataBuffer, data.Slice(i, endDelimiterFoundIndex));
                                    }

                                    if (this.MessageArrived != null)
                                    {
                                        this.MessageArrived(message);
                                    }

                                    i += endDelimiterFoundIndex + bytesToMatch;
                                    this.dataBuffer.Clear();
                                    this.beginDelimiterIndex = 0;
#if DEBUG
                                    Debug.Assert(this.endDelimiterIndex == 0, "endDelimiterIndex should already be 0");
#endif
                                    break;
                                }
                                else
                                {
                                    // A partial match was found at the end of the available data
                                    this.AppendDataToDataBuffer(data, i, endDelimiterFoundIndex);
                                    this.endDelimiterIndex = bytesToMatch;
                                    return;
                                }
                            }
                        }

                        if (endDelimiterFoundIndex == bytesAvailable)
                        {
                            // No end delimiter match was found (not even a partial at the end of the buffer)
                            this.AppendDataToDataBuffer(data, i, bytesAvailable);
                            return;
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Notifies the framer instance that incoming data has been received from the stream. This method will invoke <see cref="MessageArrived"/> as necessary.
        /// </summary>
        /// <remarks>
        /// <para>This method may invoke <see cref="MessageArrived"/> zero or more times.</para>
        /// <para>Zero-length receives are ignored. May streams use a 0-length read to indicate the end of a stream, but the framer takes no action in this case.</para>
        /// </remarks>
        /// <param name="data">The data received from the stream. Cannot be null. May be a slice of the read buffer for the stream.</param>
        /// <exception cref="System.Net.ProtocolViolationException">If the data received is not a properly-formed message.</exception>
        public void DataReceived(IList <byte> data)
        {
            int i = 0;

            while (i != data.Count)
            {
                int bytesAvailable = data.Count - i;
                if (!this.sawBeginDelimiter)
                {
                    // Search for a begin delimiter
                    if (data[i] != this.beginDelimiter)
                    {
                        trace.TraceEvent(TraceEventType.Error, 0, "Message does not begin with start delimieter 0x" + this.beginDelimiter.ToString("X2", NumberFormatInfo.InvariantInfo) + " in data " + data.Slice(i, bytesAvailable).PrettyDump());
                        throw new System.Net.ProtocolViolationException("Message does not begin with a start delimiter");
                    }

                    // The begin delimiter was found, so continue processing
                    this.sawBeginDelimiter = true;
                    ++i;
                }
                else
                {
                    IList <byte> availableData = data.Slice(i, bytesAvailable);

                    // Search for an end delimiter
                    int endIndex = availableData.IndexOf(this.endDelimiter);
                    if (endIndex == -1)
                    {
                        // The end delimiter wasn't found, so place the data in the data buffer and return
                        this.AppendDataToDataBuffer(availableData);
                        return;
                    }
                    else
                    {
                        // The end delimiter was found

                        // Concatenate our existing data buffer with the rest of this message
                        this.CheckMaxMessageSize(this.dataBuffer.Count, endIndex);
                        IList <byte> message;
                        if (this.dataBuffer.Count == 0)
                        {
                            message = data.Slice(i, endIndex);
                        }
                        else
                        {
                            message = ListExtensions.Concat(this.dataBuffer, data.Slice(i, endIndex));
                        }

                        // Invoke the callback
                        if (this.MessageArrived != null)
                        {
                            this.MessageArrived(message);
                        }

                        // Clear the data buffer and continue processing
                        this.sawBeginDelimiter = false;
                        this.dataBuffer.Clear();
                        i += endIndex + 1;
                    }
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Notifies the framer instance that incoming data has been received from the stream. This method will invoke <see cref="MessageArrived"/> as necessary.
        /// </summary>
        /// <remarks>
        /// <para>This method may invoke <see cref="MessageArrived"/> zero or more times.</para>
        /// <para>Zero-length receives are ignored. May streams use a 0-length read to indicate the end of a stream, but the framer takes no action in this case.</para>
        /// </remarks>
        /// <param name="data">The data received from the stream. Cannot be null. May be a slice of the read buffer for the stream.</param>
        /// <exception cref="System.Net.ProtocolViolationException">If the data received is not a properly-formed message.</exception>
        public void DataReceived(IList <byte> data)
        {
            int i = 0;

            while (i != data.Count)
            {
                int bytesAvailable = data.Count - i;
                if (this.delimiterIndex == -1)
                {
                    // We are searching for the begin delimiter
                    bool waiting = false;
                    for (int index = 0; index != this.beginDelimiters.Length; ++index)
                    {
                        byte[] beginDelimiter = this.beginDelimiters[index];
                        int    bytesRequested = beginDelimiter.Length - this.dataBuffer.Count;
                        if (bytesRequested > bytesAvailable)
                        {
                            // Not enough data has arrived for a complete match
                            waiting = true;
                            continue;
                        }

                        // Test if this begin delimiter already has been rejected
                        if (bytesRequested <= 0)
                        {
                            continue;
                        }

                        // Test if we have a complete match
                        IList <byte> lastOfBeginDelimiter = data.Slice(i, bytesRequested);
                        if (beginDelimiter.SequenceEqual(this.dataBuffer.Concat(lastOfBeginDelimiter)))
                        {
                            this.AppendDataToDataBuffer(lastOfBeginDelimiter);
                            i += bytesRequested;
                            this.delimiterIndex = index;
                            break;
                        }
                    }

                    if (this.delimiterIndex == -1)
                    {
                        if (waiting)
                        {
                            // The data is not long enough to match at least one of the begin delimiters, and it doesn't match any that it is long enough to match.
                            this.AppendDataToDataBuffer(data.Slice(i, bytesAvailable));
                            return;
                        }
                        else
                        {
                            trace.TraceEvent(TraceEventType.Error, 0, "Message does not begin with a defined start delimieter in data " + data.Slice(i, bytesAvailable).PrettyDump());
                            throw new System.Net.ProtocolViolationException("Message does not begin with a start delimiter");
                        }
                    }
                }
                else
                {
                    // The begin delimiter has been found; we are reading data while searching for the end delimiter
                    byte[] endDelimiter = this.endDelimiters[this.delimiterIndex];
                    if (this.endDelimiterIndex != 0)
                    {
                        // A partial end delimiter was found at the end of a previous chunk of incoming data; see if this data finishes the end delimiter
#if DEBUG
                        Debug.Assert(i == 0, "This branch should only be taken when a new chunk of incoming data has arrived.");
#endif
                        int bytesToMatch = Math.Min(endDelimiter.Length - this.endDelimiterIndex, bytesAvailable);
                        if (endDelimiter.Slice(this.endDelimiterIndex, bytesToMatch).SequenceEqual(data.Slice(0, bytesToMatch)))
                        {
                            // A partial or complete match was found
                            if (bytesToMatch == endDelimiter.Length - this.endDelimiterIndex)
                            {
                                // A complete match has been found: report the message and continue processing
                                if (this.MessageArrived != null)
                                {
                                    this.MessageArrived(this.delimiterIndex, this.dataBuffer.Skip(this.beginDelimiters[this.delimiterIndex].Length));
                                }

                                i += bytesToMatch;
                                this.Reset();
                            }
                            else
                            {
                                // The partial match has been extended into a longer partial match (at the end of the data)
                                this.endDelimiterIndex += bytesAvailable;
                                return;
                            }
                        }
                        else
                        {
                            // This was a false partial end delimiter; prepend it to the incoming data and continue searching
                            List <byte> newData = new List <byte>(data.Count + this.endDelimiterIndex);
                            newData.AddRange(endDelimiter.Take(this.endDelimiterIndex));
                            newData.AddRange(data);
                            data = newData;

                            this.endDelimiterIndex = 0;
                        }
                    }
                    else
                    {
                        // The data buffer does not have an implicit partial end delimiter on it, so we can just search the incoming data
                        // Search for a complete end delimiter, or a partial end delimiter at the end of the incoming data
                        int endDelimiterFoundIndex;
                        for (endDelimiterFoundIndex = 0; endDelimiterFoundIndex != bytesAvailable; ++endDelimiterFoundIndex)
                        {
                            int bytesToMatch = Math.Min(endDelimiter.Length, bytesAvailable - endDelimiterFoundIndex);
                            if (endDelimiter.Slice(0, bytesToMatch).SequenceEqual(data.Slice(i + endDelimiterFoundIndex, bytesToMatch)))
                            {
                                // A partial or complete match was found
                                IList <byte> match = data.Slice(i, endDelimiterFoundIndex);
                                if (bytesToMatch == endDelimiter.Length)
                                {
                                    // A complete match was found: report the message and continue processing
                                    this.CheckMaxMessageSize(this.dataBuffer.Count, endDelimiterFoundIndex);
                                    IList <byte> message = ListExtensions.Concat(this.dataBuffer, match);

                                    if (this.MessageArrived != null)
                                    {
                                        this.MessageArrived(this.delimiterIndex, message.Skip(this.beginDelimiters[this.delimiterIndex].Length));
                                    }

                                    i += endDelimiterFoundIndex + bytesToMatch;
                                    this.dataBuffer.Clear();
                                    this.delimiterIndex = -1;
#if DEBUG
                                    Debug.Assert(this.endDelimiterIndex == 0, "endDelimiterIndex should already be 0");
#endif
                                    break;
                                }
                                else
                                {
                                    // A partial match was found at the end of the available data
                                    this.AppendDataToDataBuffer(match);
                                    this.endDelimiterIndex = bytesToMatch;
                                    return;
                                }
                            }
                        }

                        if (endDelimiterFoundIndex == bytesAvailable)
                        {
                            // No end delimiter match was found (not even a partial at the end of the buffer)
                            this.AppendDataToDataBuffer(data.Slice(i, bytesAvailable));
                            return;
                        }
                    }
                }
            }
        }