async void Read()
        {
            int rx;
            var b = new byte[4096];

            while (true)
            {
                try {
                    rx = await clientStream.ReadAsync(b, 0, b.Length).ConfigureAwait(false);
                } catch {
                    rx = -1;
                }

                if (rx > 0)
                {
                    // Add all the bytes read into our buffer list
                    for (var i = 0; i < rx; i++)
                    {
                        buffer.Add(b [i]);
                    }

                    while (true)
                    {
                        // We need at least 9 bytes to process the frame
                        // 9 octets is the frame header length
                        if (buffer.Count < 9)
                        {
                            break;
                        }

                        // Find out the frame length
                        // which is a 24 bit uint, so we need to convert this as c# uint is 32 bit
                        var flen = new byte[4];
                        flen [0] = 0x0;
                        flen [1] = buffer.ElementAt(0);
                        flen [2] = buffer.ElementAt(1);
                        flen [3] = buffer.ElementAt(2);

                        var frameLength = BitConverter.ToUInt32(flen.EnsureBigEndian(), 0);

                        // If we are expecting a payload that's bigger than what's in our buffer
                        // we should keep reading from the stream
                        if (buffer.Count - 9 < frameLength)
                        {
                            break;
                        }

                        // If we made it this far, the buffer has all the data we need, let's get it out to process
                        var data = buffer.GetRange(0, (int)frameLength + 9).ToArray();
                        // remove the processed info from the buffer
                        buffer.RemoveRange(0, (int)frameLength + 9);

                        // Get the Frame Type so we can instantiate the right subclass
                        var frameType = data [3]; // 4th byte in frame header is TYPE

                        // we need to turn the stream id into a uint
                        var frameStreamIdData = new byte[4];
                        Array.Copy(data, 5, frameStreamIdData, 0, 4);
                        var frameStreamId = Util.ConvertFromUInt31(frameStreamIdData.EnsureBigEndian());

                        // Create a new typed instance of our abstract Frame
                        var frame = Frame.Create((FrameType)frameType);

                        try {
                            // Call the specific subclass implementation to parse
                            frame.Parse(data);
                        } catch (Exception ex) {
                            Log.Error("Parsing Frame Failed: {0}", ex);
                            throw ex;
                        }

                        Log.Debug("<- {0}", frame);

                        // If it's a settings frame, we should note the values and
                        // return the frame with the Ack flag set
                        if (frame.Type == FrameType.Settings)
                        {
                            var settingsFrame = frame as SettingsFrame;

                            // Update our instance of settings with the new data
                            Settings.UpdateFromFrame(settingsFrame, flowControlManager);

                            // See if this was an ack, if not, return an empty
                            // ack'd settings frame
                            if (!settingsFrame.Ack)
                            {
                                await QueueFrame(new SettingsFrame { Ack = true }).ConfigureAwait(false);
                            }
                        }
                        else if (frame.Type == FrameType.Ping)
                        {
                            var pingFrame = frame as PingFrame;
                            // See if we need to respond to the ping request (if it's not-ack'd)
                            if (!pingFrame.Ack)
                            {
                                // Ack and respond
                                pingFrame.Ack = true;
                                await QueueFrame(pingFrame).ConfigureAwait(false);
                            }
                        }
                        else if (frame.Type == FrameType.Data)
                        {
                            // Increment our received data counter
                            Interlocked.Add(ref receivedDataCount, frame.PayloadLength);
                        }

                        // Some other frame type, just pass it along to the stream
                        var stream = await streamManager.Get(frameStreamId).ConfigureAwait(false);

                        stream.ProcessReceivedFrames(frame);
                    }
                }
                else
                {
                    // Stream was closed, break out of reading loop
                    break;
                }
            }

            // Cleanup
            Disconnect();
        }