/// <summary> /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so. /// </summary> /// <param name="message"></param> public void Close(string message) { if (_initialMsgTimeout > 0) { _receiveDone.Set(); _initialMsgTimeout = 0; } if (_networkContext == null) { return; } if (_networkContext.Stream != null) { if (_networkContext.Stream.CanWrite) { byte[] messagedata = Encoding.UTF8.GetBytes(message); WebSocketFrame closeResponseFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close; closeResponseFrame.Header.PayloadLen = (ulong)messagedata.Length; closeResponseFrame.Header.IsEnd = true; SendSocket(closeResponseFrame.ToBytes()); } } CloseDelegate closeD = OnClose; if (closeD != null) { closeD(this, new CloseEventArgs()); } _closing = true; }
/// <summary> /// Processes a websocket frame and triggers consumer events /// </summary> /// <param name="psocketState">We need to modify the websocket state here depending on the frame</param> private void ProcessFrame(WebSocketState psocketState) { if (psocketState.Header.IsMasked) { byte[] unmask = psocketState.ReceivedBytes.ToArray(); WebSocketReader.Mask(psocketState.Header.Mask, unmask); psocketState.ReceivedBytes = new List <byte>(unmask); } if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0) { _receiveDone.Set(); _initialMsgTimeout = 0; } switch (psocketState.Header.Opcode) { case WebSocketReader.OpCode.Ping: PingDelegate pingD = OnPing; if (pingD != null) { pingD(this, new PingEventArgs()); } WebSocketFrame pongFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong; pongFrame.Header.IsEnd = true; SendSocket(pongFrame.ToBytes()); break; case WebSocketReader.OpCode.Pong: PongDelegate pongD = OnPong; if (pongD != null) { pongD(this, new PongEventArgs() { PingResponseMS = System.Environment.TickCount - _pingtime }); } break; case WebSocketReader.OpCode.Binary: if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. { psocketState.ContinuationFrame = new WebSocketFrame { Header = psocketState.Header, WebSocketPayload = psocketState.ReceivedBytes.ToArray() }; } else { // Send Done Event! DataDelegate dataD = OnData; if (dataD != null) { dataD(this, new WebsocketDataEventArgs() { Data = psocketState.ReceivedBytes.ToArray() }); } } break; case WebSocketReader.OpCode.Text: if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. { psocketState.ContinuationFrame = new WebSocketFrame { Header = psocketState.Header, WebSocketPayload = psocketState.ReceivedBytes.ToArray() }; } else { TextDelegate textD = OnText; if (textD != null) { textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) }); } // Send Done Event! } break; case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes //Console.WriteLine("currhead " + psocketState.Header.IsEnd); //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd); if (psocketState.ContinuationFrame == null && psocketState.Header.IsEnd) { // This must be the first and last frame. Why didn't we just send a BinaryData? DataDelegate dataD = OnData; if (dataD != null) { dataD(this, new WebsocketDataEventArgs() { Data = psocketState.ReceivedBytes.ToArray() }); } break; } byte[] combineddata = new byte[psocketState.ReceivedBytes.Count + psocketState.ContinuationFrame.WebSocketPayload.Length]; byte[] newdata = psocketState.ReceivedBytes.ToArray(); Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length); Buffer.BlockCopy(newdata, 0, combineddata, psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length); psocketState.ContinuationFrame.WebSocketPayload = combineddata; psocketState.Header.PayloadLen = (ulong)combineddata.Length; if (psocketState.Header.IsEnd) { if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text) { // Send Done event TextDelegate textD = OnText; if (textD != null) { textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) }); } } else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary) { // Send Done event DataDelegate dataD = OnData; if (dataD != null) { dataD(this, new WebsocketDataEventArgs() { Data = combineddata }); } } else { // protocol violation } psocketState.ContinuationFrame = null; } break; case WebSocketReader.OpCode.Close: Close(string.Empty); break; } psocketState.Header.SetDefault(); psocketState.ReceivedBytes.Clear(); psocketState.ExpectedBytes = 0; }