public async Task SendAsync(byte[] buffer, int offset, int size, WebSocketMessageType webSocketMessageType, TimeSpan timeout)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, webSocketMessageType, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(SendAsync)}");
            }

            Fx.AssertAndThrow(State == WebSocketState.Open, ClientWebSocketNotInOpenStateDuringSend);
            _tcpClient.Client.SendTimeout = TimeoutHelper.ToMilliseconds(timeout);
            bool succeeded = false;

            try
            {
                byte[] webSocketHeader = PrepareWebSocketHeader(size, webSocketMessageType);
                await _webSocketStream.WriteAsync(webSocketHeader, 0, webSocketHeader.Length).ConfigureAwait(false);

                MaskWebSocketData(buffer, offset, size);
                await _webSocketStream.WriteAsync(buffer, offset, size).ConfigureAwait(false);

                succeeded = true;
            }
            finally
            {
                if (!succeeded)
                {
                    Fault();
                }

                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, webSocketMessageType, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(SendAsync)}");
                }
            }
        }
        public async Task SendAsync(byte[] buffer, int offset, int size, WebSocketMessageType webSocketMessageType, TimeSpan timeout)
        {
            Fx.AssertAndThrow(this.State == WebSocketState.Open, ClientWebSocketNotInOpenStateDuringSend);
            this.TcpClient.Client.SendTimeout = TimeoutHelper.ToMilliseconds(timeout);
            bool succeeded = false;

            try
            {
                var webSocketHeader = PrepareWebSocketHeader(size, webSocketMessageType);
                await this.WebSocketStream.WriteAsync(webSocketHeader, 0, webSocketHeader.Length);

                MaskWebSocketData(buffer, offset, size);
                await this.WebSocketStream.WriteAsync(buffer, offset, size);

                succeeded = true;
            }
            finally
            {
                if (!succeeded)
                {
                    this.Fault();
                }
            }
        }
        public async Task <int> ReceiveAsync(byte[] buffer, int offset, TimeSpan timeout)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(ReceiveAsync)}");
            }

            byte[] header = new byte[2];

            Fx.AssertAndThrow(State == WebSocketState.Open, ClientWebSocketNotInOpenStateDuringReceive);
            _tcpClient.ReceiveTimeout = TimeoutHelper.ToMilliseconds(timeout);

            bool succeeded = false;

            try
            {
                byte payloadLength;
                bool pongFrame;

                // TODO: rewrite this section to handle all control frames (including ping)
                int totalBytesRead;
                int bytesRead;
                do
                {
                    // Ignore pong frame and start over
                    totalBytesRead = 0;
                    do
                    {
                        bytesRead = await _webSocketStream.ReadAsync(header, totalBytesRead, header.Length - totalBytesRead).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                        }

                        totalBytesRead += bytesRead;
                    }while (totalBytesRead < header.Length);

                    if (!ParseWebSocketFrameHeader(header, out payloadLength, out pongFrame))
                    {
                        // Encountered a close frame or error in parsing frame from server. Close connection
                        byte[] closeHeader = PrepareWebSocketHeader(0, WebSocketMessageType.Close);

                        await _webSocketStream.WriteAsync(closeHeader, 0, closeHeader.Length).ConfigureAwait(false);

                        State = WebSocketState.Closed;
                        _webSocketStream?.Close();
                        _tcpClient?.Close();
                        return(0);  // TODO: throw exception?
                    }

                    if (pongFrame && payloadLength > 0)
                    {
                        totalBytesRead = 0;
                        byte[] tempBuffer = new byte[payloadLength];
                        while (totalBytesRead < payloadLength)
                        {
                            bytesRead = await _webSocketStream.ReadAsync(tempBuffer, totalBytesRead, payloadLength - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }
                    }
                } while (pongFrame);

                totalBytesRead = 0;

                if (buffer.Length < payloadLength)
                {
                    throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                }

                if (payloadLength < MediumSizeFrame)
                {
                    while (totalBytesRead < payloadLength)
                    {
                        bytesRead = await _webSocketStream.ReadAsync(buffer, offset + totalBytesRead, payloadLength - totalBytesRead).ConfigureAwait(false);

                        if (bytesRead == 0)
                        {
                            throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                        }

                        totalBytesRead += bytesRead;
                    }
                }
                else
                {
                    switch (payloadLength)
                    {
                    case MediumSizeFrame:
                        // read payload length (< 64K)
                        do
                        {
                            bytesRead = await _webSocketStream.ReadAsync(header, totalBytesRead, header.Length - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }while (totalBytesRead < header.Length);

                        totalBytesRead = 0;
                        ushort extendedPayloadLength = (ushort)((header[0] << 8) | header[1]);

                        // read payload
                        if (buffer.Length >= extendedPayloadLength)
                        {
                            while (totalBytesRead < extendedPayloadLength)
                            {
                                bytesRead = await _webSocketStream.ReadAsync(buffer, offset + totalBytesRead, extendedPayloadLength - totalBytesRead).ConfigureAwait(false);

                                if (bytesRead == 0)
                                {
                                    throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                                }

                                totalBytesRead += bytesRead;
                            }
                        }
                        else
                        {
                            throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                        }

                        break;

                    case LargeSizeFrame:
                        // read payload length (>= 64K)
                        byte[] payloadLengthBuffer = new byte[8];
                        do
                        {
                            bytesRead = await _webSocketStream.ReadAsync(payloadLengthBuffer, totalBytesRead, payloadLengthBuffer.Length - totalBytesRead).ConfigureAwait(false);

                            if (bytesRead == 0)
                            {
                                throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                            }

                            totalBytesRead += bytesRead;
                        }while (totalBytesRead < payloadLengthBuffer.Length);

                        totalBytesRead = 0;

                        // ignore bytes 0-3 - length cannot be larger than a 32-bit number
                        uint superExtendedPayloadLength = (uint)((payloadLengthBuffer[4] << 24) | (payloadLengthBuffer[5] << 16) | (payloadLengthBuffer[6] << 8) | payloadLengthBuffer[7]);

                        // read payload
                        if (buffer.Length >= superExtendedPayloadLength)
                        {
                            while (totalBytesRead < superExtendedPayloadLength)
                            {
                                bytesRead = await _webSocketStream.ReadAsync(buffer, offset + totalBytesRead, (int)(superExtendedPayloadLength - totalBytesRead)).ConfigureAwait(false);

                                if (bytesRead == 0)
                                {
                                    throw new IOException(FramingPrematureEOF, new InvalidDataException("IotHubClientWebSocket was expecting more bytes"));
                                }

                                totalBytesRead += bytesRead;
                            }
                        }
                        else
                        {
                            throw Fx.Exception.AsError(new InvalidOperationException(Resources.SizeExceedsRemainingBufferSpace));
                        }

                        break;
                    }
                }

                succeeded = true;
                return(totalBytesRead);
            }
            finally
            {
                if (!succeeded)
                {
                    Fault();
                }

                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, timeout, $"{nameof(IotHubClientWebSocket)}.{nameof(ReceiveAsync)}");
                }
            }
        }