/// <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; } } } }
/// <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; } } } } }