private void Run() { byte[] RxData = new byte[512]; // Rx buffer used for transferring data from the stream to the ring buffer NetworkStream Stream = _Client.GetStream(); StompRingBuffer<byte> Buffer = new StompRingBuffer<byte>(RxBufferSize); // Run while the client is connected, polling for data rx'd and dispatching frames as necessary // Also handle heartbeats and heartbeat disconnect while (_Client.Connected) { // If we should be heartbeating, and we're connected... if (_Heartbeat > 0 && ConnectionVersion > 0.0f) { // Sleep up to 15ms, or less if we need to heartbeat sooner int SleepAmt = Math.Min(_HeartbeatTxIntervalTimeout, 15); // If we expect to recieve heartbeats... if (_HeartbeatRxInterval > 0) { // ...check that we've recieved data within the rx interval. If not, disconnect. _HeartbeatRxIntervalTimeout -= SleepAmt; if (_HeartbeatRxIntervalTimeout < 0) { lock (_Client) { _Client.Close(); } return; } } // If we need to send heartbeats... if (_HeartbeatTxInterval > 0) { // ... send one if it's been too long since our last transmission _HeartbeatTxIntervalTimeout -= SleepAmt; if (_HeartbeatTxIntervalTimeout < 0 && _HeartbeatTxInterval > 0) { _Client.GetStream().WriteByte((byte)'\n'); _HeartbeatTxIntervalTimeout = _HeartbeatTxInterval; } } Thread.Sleep(SleepAmt); } else // Otherwise just do a standard sleep { Thread.Sleep(15); } // Read in as much data from the stream as we can into the ring buffer while (_Client.Available > 0 && Buffer.AvailableWrite > 0) { // We've received data, so reset the heartbeat rx timeout _HeartbeatRxIntervalTimeout = (int)(_HeartbeatRxInterval * 1.5); // +50% forgiveness for heartbeat loss // Now read in from the networkstream to the ring buffer int AmtRead = Stream.Read(RxData, 0, Buffer.AvailableWrite); Buffer.Write(RxData, AmtRead); } // Advance through any heartbeats rx'd or frame separators while (Buffer.Peek() == '\r' || Buffer.Peek() == '\n' || Buffer.Peek() == '\0') Buffer.Read(1); // Now try to build + dispatch the packet if (!TryBuildPacket(Buffer) && Buffer.AvailableWrite == 0) throw new InvalidOperationException("Ran out of receive ringbuffer space in STOMPClient"); } }
/// <summary> /// Tries to build a packet from the given ringbuffer /// </summary> /// <param name="Buffer"> /// The Ringbuffer to build a packet from /// </param> /// <returns> /// Whether it was able to build a packet or not /// </returns> public bool TryBuildPacket(StompRingBuffer <byte> Buffer) { // See if we have rx'd a packet separator or a \0 in a binary frame body int PacketLength = Buffer.DistanceTo(0); // We have, so what did we find? if (PacketLength > 0) { // This is a really messy block of code. // The goal is that it tries to determine whether it has a full packet or needs to wait for more data // before building the packet and dispatching it byte[] Data = Buffer.Peek(PacketLength); string Header = Encoding.UTF8.GetString(Data); string[] HeaderCheck = Header.Split('\n'); int ContentLength = 0; bool HasContentLength = false; // First, we look to see if our "packet" has a content-length header. Since we scanned out to a null (\0) byte, we're guaranteed to at least have the headers // of whatever packet we're examining for (int i = 0; i < HeaderCheck.Length && HeaderCheck[i] != "" && HeaderCheck[i] != "\r"; i++) { // We found a content-length header? Flag it and store how large in bytes the content should be if (HeaderCheck[i].StartsWith("content-length:")) { HasContentLength = true; ContentLength = int.Parse(HeaderCheck[i].Substring(15)); } } StompFrame Frame = null; if (HasContentLength) { // We have a content-length header. We need to find the start of the frame body, in bytes, // and then make sure we have (ContentLength) bytes available after that // Look for the end of the headers, either 1.0/1.1 or 1.2 (\r\n)-friendly int EndOfHeaders = Header.IndexOf("\r\n\r\n") + 4; if (EndOfHeaders == 3) // (-1) + 4 { EndOfHeaders = Header.IndexOf("\n\n") + 2; } // Get the byte length of the header int Offset = Encoding.UTF8.GetByteCount(Header.Substring(0, EndOfHeaders)); // Now see if we have that many bytes available in the ring buffer (realistically, we should except for obscene frame sizes) if (Offset + ContentLength <= Buffer.AvailableRead) { // If we do, peek the exact packet length we want and assemble Frame = StompFrame.Build(Buffer.Peek(Offset + ContentLength), _FrameTypeMapping); Buffer.Seek(Offset + ContentLength); DispatchFrame(Frame); return(true); } } else // No content-length. We're guaranteed to be a text packet without any overshoot; no special treatment needed { Frame = StompFrame.Build(Data, _FrameTypeMapping); Buffer.Seek(PacketLength); DispatchFrame(Frame); return(true); } } return(false); }
/// <summary> /// Tries to build a packet from the given ringbuffer /// </summary> /// <param name="Buffer"> /// The Ringbuffer to build a packet from /// </param> /// <returns> /// Whether it was able to build a packet or not /// </returns> public bool TryBuildPacket(StompRingBuffer<byte> Buffer) { // See if we have rx'd a packet separator or a \0 in a binary frame body int PacketLength = Buffer.DistanceTo(0); // We have, so what did we find? if (PacketLength > 0) { // This is a really messy block of code. // The goal is that it tries to determine whether it has a full packet or needs to wait for more data // before building the packet and dispatching it byte[] Data = Buffer.Peek(PacketLength); string Header = Encoding.UTF8.GetString(Data); string[] HeaderCheck = Header.Split('\n'); int ContentLength = 0; bool HasContentLength = false; // First, we look to see if our "packet" has a content-length header. Since we scanned out to a null (\0) byte, we're guaranteed to at least have the headers // of whatever packet we're examining for (int i = 0; i < HeaderCheck.Length && HeaderCheck[i] != "" && HeaderCheck[i] != "\r"; i++) { // We found a content-length header? Flag it and store how large in bytes the content should be if (HeaderCheck[i].StartsWith("content-length:")) { HasContentLength = true; ContentLength = int.Parse(HeaderCheck[i].Substring(15)); } } StompFrame Frame = null; if (HasContentLength) { // We have a content-length header. We need to find the start of the frame body, in bytes, // and then make sure we have (ContentLength) bytes available after that // Look for the end of the headers, either 1.0/1.1 or 1.2 (\r\n)-friendly int EndOfHeaders = Header.IndexOf("\r\n\r\n") + 4; if (EndOfHeaders == 3) // (-1) + 4 EndOfHeaders = Header.IndexOf("\n\n") + 2; // Get the byte length of the header int Offset = Encoding.UTF8.GetByteCount(Header.Substring(0, EndOfHeaders)); // Now see if we have that many bytes available in the ring buffer (realistically, we should except for obscene frame sizes) if (Offset + ContentLength <= Buffer.AvailableRead) { // If we do, peek the exact packet length we want and assemble Frame = StompFrame.Build(Buffer.Peek(Offset + ContentLength), _FrameTypeMapping); Buffer.Seek(Offset + ContentLength); DispatchFrame(Frame); return true; } } else // No content-length. We're guaranteed to be a text packet without any overshoot; no special treatment needed { Frame = StompFrame.Build(Data, _FrameTypeMapping); Buffer.Seek(PacketLength); DispatchFrame(Frame); return true; } } return false; }
private void Run() { byte[] RxData = new byte[512]; // Rx buffer used for transferring data from the stream to the ring buffer NetworkStream Stream = _Client.GetStream(); StompRingBuffer <byte> Buffer = new StompRingBuffer <byte>(RxBufferSize); // Run while the client is connected, polling for data rx'd and dispatching frames as necessary // Also handle heartbeats and heartbeat disconnect while (_Client.Connected) { // If we should be heartbeating, and we're connected... if (_Heartbeat > 0 && ConnectionVersion > 0.0f) { // Sleep up to 15ms, or less if we need to heartbeat sooner int SleepAmt = Math.Min(_HeartbeatTxIntervalTimeout, 15); // If we expect to recieve heartbeats... if (_HeartbeatRxInterval > 0) { // ...check that we've recieved data within the rx interval. If not, disconnect. _HeartbeatRxIntervalTimeout -= SleepAmt; if (_HeartbeatRxIntervalTimeout < 0) { lock (_Client) { _Client.Close(); } return; } } // If we need to send heartbeats... if (_HeartbeatTxInterval > 0) { // ... send one if it's been too long since our last transmission _HeartbeatTxIntervalTimeout -= SleepAmt; if (_HeartbeatTxIntervalTimeout < 0 && _HeartbeatTxInterval > 0) { _Client.GetStream().WriteByte((byte)'\n'); _HeartbeatTxIntervalTimeout = _HeartbeatTxInterval; } } Thread.Sleep(SleepAmt); } else // Otherwise just do a standard sleep { Thread.Sleep(15); } // Read in as much data from the stream as we can into the ring buffer while (_Client.Available > 0 && Buffer.AvailableWrite > 0) { // We've received data, so reset the heartbeat rx timeout _HeartbeatRxIntervalTimeout = (int)(_HeartbeatRxInterval * 1.5); // +50% forgiveness for heartbeat loss // Now read in from the networkstream to the ring buffer int AmtRead = Stream.Read(RxData, 0, Buffer.AvailableWrite); Buffer.Write(RxData, AmtRead); } // Advance through any heartbeats rx'd or frame separators while (Buffer.Peek() == '\r' || Buffer.Peek() == '\n' || Buffer.Peek() == '\0') { Buffer.Read(1); } // Now try to build + dispatch the packet if (!TryBuildPacket(Buffer) && Buffer.AvailableWrite == 0) { throw new InvalidOperationException("Ran out of receive ringbuffer space in STOMPClient"); } } }