/// <summary> /// Receive a WebSocket message. /// </summary> /// <returns>A ReceivedMessage object containing Result and Message.</returns> private async Task <WsClientReceivedMessage> ReceiveMessageAsync() { WsClientReceivedMessage receivedMessage = new WsClientReceivedMessage { Message = new MemoryStream() }; do { try { receivedMessage.Result = await _Client.ReceiveAsync(receiveBuffer, CancellationToken.None).ConfigureAwait(false); } catch (WebSocketException e) { receivedMessage.Message.Dispose(); receivedMessage.Message = null; if (_Client != null) { OnStateChange(_Client.State); } else { OnStateChange(WebSocketState.None); } OnErrorState(e, -1); break; } receivedMessage.Message.Write(receiveBuffer.Array, receiveBuffer.Offset, receivedMessage.Result.Count); } while (!receivedMessage.Result.EndOfMessage); return(receivedMessage); }
/// <summary> /// Starts an asynchronous loop for receiving WebSocket messages. /// </summary> public async Task StartMessageReceiveLoop() { // Only one message can be received at a time. Wait until we can control the receive queue. await receiveAsyncSemaphore.WaitAsync().ConfigureAwait(false); // Store the context of our thread so we can release receiveAsyncSemaphore later. SynchronizationContext receiveLoopContext = SynchronizationContext.Current ?? new SynchronizationContext(); // The long-lived while loop should run synchronously. while (_Status == WebSocketState.Open || _Status == WebSocketState.CloseReceived) { try { // There can be a long time between messages received... does it matter which thread picks up the next one? WsClientReceivedMessage receivedMessage = await ReceiveMessageAsync().ConfigureAwait(false); // There should currently only be three MessageType: Close, Text, and Binary. const int expectedMessageTypeCount = 3; Debug.Assert(Enum.GetNames(typeof(WebSocketMessageType)).Length == expectedMessageTypeCount, $"StartMessageReceiveLoop expected {expectedMessageTypeCount} MessageType, there are {Enum.GetNames(typeof(WebSocketMessageType)).Length}"); // Result is null if a connection issue occurred. MessageType==Close if server is closing connection. if (receivedMessage.Result == null || receivedMessage.Result.MessageType == WebSocketMessageType.Close) { break; } // The two other MessageType are Text and Binary. else if (receivedMessage.Result.MessageType == WebSocketMessageType.Text || receivedMessage.Result.MessageType == WebSocketMessageType.Binary) { ParseMessage(receivedMessage); } else { /* Impossible condition unless: * (a) RFC 6455 is updated, * (b) ClientWebSocket includes a new websocket protocol OpCode, * (c) This library hasn't been debugged since (b) made it into a new .NET Core version. * In such a case, the above if/elseif needs updating as GenericClient is potentially no longer fit for purpose. */ WebSocketException e = new WebSocketException( WebSocketError.InvalidMessageType, String.Format(CultureInfo.CurrentCulture, Properties.Resources.exception_unknown_websocket_message_type_format, typeof(GenericClient).Name, receivedMessage.Result.MessageType) ); OnErrorState(e, -1); throw e; } } catch (WebSocketException e) { Debugger.Break(); OnStateChange(_Client.State); OnErrorState(e, -1); } } // No more messages can be received on this connection. Get the receiveLoopContext thread to release the receiveAsyncSemaphore. receiveLoopContext.Send( x => receiveAsyncSemaphore.Release(), null); }
/// <summary> /// Calls correct event invocation method depending on MessageType. /// </summary> /// <param name="receivedMessage">A ReceivedMessage instance.</param> private void ParseMessage(WsClientReceivedMessage receivedMessage) { receivedMessage.Message.Seek(0, SeekOrigin.Begin); if (receivedMessage.Result.MessageType == WebSocketMessageType.Text) { OnReceiveTextMessage(receivedMessage.Message); } else if (receivedMessage.Result.MessageType == WebSocketMessageType.Binary) { OnReceiveBinaryMessage(receivedMessage.Message); } }