/// <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; }
/// <summary> /// This is our ugly Async OnReceive event handler. /// This chunks the input stream based on the length of the provided buffer and processes out /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer. /// </summary> /// <param name="ar">Our Async State from beginread</param> private void OnReceive(IAsyncResult ar) { WebSocketState _socketState = ar.AsyncState as WebSocketState; try { int bytesRead = _networkContext.Stream.EndRead(ar); if (bytesRead == 0) { // Do Disconnect _networkContext.Stream.Dispose(); _networkContext = null; return; } _bufferPosition += bytesRead; if (_bufferPosition > _bufferLength) { // Message too big for chunksize.. not sure how this happened... //Close(string.Empty); } int offset = 0; bool headerread = true; int headerforwardposition = 0; while (headerread && offset < bytesRead) { if (_socketState.FrameComplete) { WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader; headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader, out headerforwardposition); offset += headerforwardposition; if (headerread) { _socketState.FrameComplete = false; if (pheader.PayloadLen > (ulong)_maxPayloadBytes) { Close("Invalid Payload size"); return; } if (pheader.PayloadLen > 0) { if ((int)pheader.PayloadLen > _bufferPosition - offset) { byte[] writebytes = new byte[_bufferPosition - offset]; Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int)_bufferPosition - offset); _socketState.ExpectedBytes = (int)pheader.PayloadLen; _socketState.ReceivedBytes.AddRange(writebytes); _socketState.Header = pheader; // We need to add the header so that we can unmask it offset += (int)_bufferPosition - offset; } else { byte[] writebytes = new byte[pheader.PayloadLen]; Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int)pheader.PayloadLen); WebSocketReader.Mask(pheader.Mask, writebytes); pheader.IsMasked = false; _socketState.FrameComplete = true; _socketState.ReceivedBytes.AddRange(writebytes); _socketState.Header = pheader; offset += (int)pheader.PayloadLen; } } else { pheader.Mask = 0; _socketState.FrameComplete = true; _socketState.Header = pheader; } if (_socketState.FrameComplete) { ProcessFrame(_socketState); _socketState.Header.SetDefault(); _socketState.ReceivedBytes.Clear(); _socketState.ExpectedBytes = 0; } } } else { WebsocketFrameHeader frameHeader = _socketState.Header; int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count; if (bytesleft > _bufferPosition) { byte[] writebytes = new byte[_bufferPosition]; Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int)_bufferPosition); _socketState.ReceivedBytes.AddRange(writebytes); _socketState.Header = frameHeader; // We need to add the header so that we can unmask it offset += (int)_bufferPosition; } else { byte[] writebytes = new byte[_bufferPosition]; Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int)_bufferPosition); _socketState.FrameComplete = true; _socketState.ReceivedBytes.AddRange(writebytes); _socketState.Header = frameHeader; offset += (int)_bufferPosition; } if (_socketState.FrameComplete) { ProcessFrame(_socketState); _socketState.Header.SetDefault(); _socketState.ReceivedBytes.Clear(); _socketState.ExpectedBytes = 0; // do some processing } } } if (offset > 0) { // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning. if (offset < _buffer.Length) { Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset); } _bufferPosition -= offset; } if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing) { _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive, _socketState); } else { // We can't read the stream anymore... } } catch (IOException) { Close("Undefined Error"); } catch (ObjectDisposedException) { try { Close("Undefined Error"); } catch (Exception) { // we don't care } } catch (NullReferenceException) { // The socket was probably disposed } }