/// <summary> /// Creates a new WebSockGram suitable for the specified WebSocketReceiveResult (text or binary). /// Appends the received data to it /// </summary> public static WebSockGram StartReceivingFromReceiveResult(byte[] receiveBuffer, WebSocketReceiveResult receiveResult) { WebSockGram result; if (receiveResult.MessageType == WebSocketMessageType.Text) { result = new TextWebSockGram(); } else if (receiveResult.MessageType == WebSocketMessageType.Binary) { result = new BinaryWebSockGram(); } else { throw new WebSocketException("Invalid type of a message. Text or Binary websocket message expected"); } result.AppendFromReceiveResult(receiveBuffer, receiveResult); return(result); }
/// <summary> /// An asynchroneous method for a WebSocket request processing /// </summary> /// <param name="listenerContext">Listener context</param> private async void ProcessWebSocketRequestAsync(HttpListenerContext listenerContext) { // Accepting WebSocket connection from the context WebSocketContext webSocketContext; try { webSocketContext = await listenerContext.AcceptWebSocketAsync(subProtocol : null); } catch (Exception e) { // The upgrade process failed somehow. For simplicity lets assume it was a failure on the part of the server and indicate this using 500. listenerContext.Response.StatusCode = 500; listenerContext.Response.Close(); Logger.LogError(String.Format("Exception: {0}", e)); return; } // This is our new WebSocket object WebSocket webSocket = webSocketContext.WebSocket; lock (socketsAndMessagesLock) { // Adding the new client to the list connectedSockets.Add(webSocket); // Sending all the previous messages to the new client Logger.Log("Sending " + cabinetController.Cards.Count + " old cards to the new client"); foreach (var oldCard in cabinetController.Cards) { // Making an add_card command for the selected card var addCommand = new Model.Commands.UpdateCardCommand(oldCard.Key, oldCard.Value); var addCommandGram = new TextWebSockGram(serializer.SerializeCommand(addCommand)); // Sending the command to the new client addCommandGram.Send(webSocket).Wait(); } } // We are expecting a command to be sent from the client Expecting expecting = Expecting.Command; // Here the received command will be held if we will be waiting for an appended binary data Model.Commands.Command receivedCommand = null; // The received WebSocket message collector WebSockGram receivedGram = null; // Communicating try { byte[] receiveBuffer = new byte[16384]; // While the WebSocket connection remains open run a simple loop that receives data and sends it back. while (webSocket.State == WebSocketState.Open) { WebSocketReceiveResult receiveResult = await webSocket.ReceiveAsync(new ArraySegment <byte>(receiveBuffer), CancellationToken.None); if (receiveResult.MessageType == WebSocketMessageType.Close) { // We've got a Close request. Sending it back await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); } else { // We have received a message. Text or binary. We are going to collect it to the WebSockGram object if (receivedGram == null) { // Start building of a new message receivedGram = WebSockGram.StartReceivingFromReceiveResult(receiveBuffer, receiveResult); } else { // Continue building of the message receivedGram.AppendFromReceiveResult(receiveBuffer, receiveResult); } // If the message has just ended, let's interpret it if (receivedGram.Completed) { // Logging the message if (receivedGram is TextWebSockGram) { Logger.Log("Received text: \"" + receivedGram.ToString() + "\""); } else if (receivedGram is BinaryWebSockGram) { Logger.Log("Received binary: " + receivedGram.Length + " bytes"); } else { throw new Exception("Invalid case"); } // Checking what we have received (depending on what we have been expecting) if (expecting == Expecting.Command) { // We are expecting a command. So receivedGram has to be a text if (receivedGram is TextWebSockGram) { var command = serializer.DeserializeCommand(receivedGram.ToString()); // Executing the received command if (command is Model.Commands.UpdateCardCommand) { var updateCardCommand = command as Model.Commands.UpdateCardCommand; lock (socketsAndMessagesLock) { // Giving the received message to the controller cabinetController.UpdateCard(updateCardCommand.Id, updateCardCommand.Value); // Broadcasting the message Logger.Log("Broadcasting the new card " + updateCardCommand.Value + " to the " + connectedSockets.Count + " connected clients"); receivedGram.Broadcast(connectedSockets).Wait(); } } else if (command is Model.Commands.UploadImageCardCommand) { // After this command a binary should be appended (containing the image data). // So we are saving the command itself... receivedCommand = command; // ...and setting the state machine to expect binary appended to the command. expecting = Expecting.AppendedBinary; } else if (command is Model.Commands.ListCardIDsCommand) { // Sending all the cards ids IList <string> ids = cabinetController.ListCardIDs(); Model.Commands.ListCardIDsCommand response = new Model.Commands.ListCardIDsCommand(ids); var ser = serializer.SerializeCommand(response); WebSockGram listids = new TextWebSockGram(ser); await listids.Send(webSocket); } } else { throw new Exception("We are expecting a command, but the received WebSocket message isn't a text"); } } else if (expecting == Expecting.AppendedBinary) { // We are expecting an appended binary if (receivedGram is BinaryWebSockGram) { // We've got an appended binary if (receivedCommand is Model.Commands.UploadImageCardCommand) { // Our current command is upload_image_card var uploadImageMessageCommand = receivedCommand as Model.Commands.UploadImageCardCommand; // Saving the received image byte[] imageData = (receivedGram as BinaryWebSockGram).Data.ToArray(); var savedImageFileName = imagesController.Add(uploadImageMessageCommand.Value.Filename, new MemoryStream(imageData)); lock (socketsAndMessagesLock) { // Setting up the link to the saved file. Adding it to the received Card data var imageFileCard = uploadImageMessageCommand.Value; imageFileCard.Link = new Uri(listenerContext.Request.Url, "data/" + savedImageFileName); // Creating a new card in the controller, containing the received image cabinetController.UpdateCard(uploadImageMessageCommand.Id, imageFileCard); // Broadcasting the add_card command to all the connected clients Logger.Log("Broadcasting the new image card " + imageFileCard.Link + " to the " + connectedSockets.Count + " connected clients"); var addCommand = new Model.Commands.UpdateCardCommand(uploadImageMessageCommand.Id, imageFileCard); TextWebSockGram addCardGram = new TextWebSockGram(serializer.SerializeCommand(addCommand)); addCardGram.Broadcast(connectedSockets).Wait(); } } else { throw new Exception("Strange case"); } } else { // Resetting the received command (we have a client error here) throw new Exception("We are expecting a binary appended to a command, but the received WebSocket message isn't a binary"); } // After we received the appended binary, resetting the state machine to expect a command again expecting = Expecting.Command; receivedCommand = null; } receivedGram = null; } } } } catch (Exception e) { // Just log any exceptions to the console. // Pretty much any exception that occurs when calling `SendAsync`/`ReceiveAsync`/`CloseAsync` // is unrecoverable in that it will abort the connection and leave the `WebSocket` instance in an unusable state. Logger.Log(String.Format("Exception: {0}", e)); } finally { // Disposing the WebSocket and removing it from the list try { // Clean up by disposing the WebSocket once it is closed/aborted. if (webSocket != null) { lock (socketsAndMessagesLock) { connectedSockets.Remove(webSocket); } webSocket.Dispose(); } } catch (Exception e) { Logger.Log(String.Format("Exception during WebSocket disposure: {0}", e)); } } }