/// <summary> /// Informs the otherside that we accepted their upgrade request /// </summary> /// <param name="pHandshakeResponse">The HTTP 1.1 101 response that says Yay \o/ </param> private void SendUpgradeSuccess(string pHandshakeResponse) { // Create a new websocket state so we can keep track of data in between network reads. WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List <byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true }; byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); try { // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); // Write the upgrade handshake success message _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length); _networkContext.Stream.Flush(); _upgraded = true; UpgradeCompletedDelegate d = OnUpgradeCompleted; if (d != null) { d(this, new UpgradeCompletedEventArgs()); } } catch (IOException) { Close(string.Empty); } catch (ObjectDisposedException) { Close(string.Empty); } }
/// <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 (_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; }
public void SendData(byte[] data) { WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data }; dataMessageFrame.Header.IsEnd = true; dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; SendSocket(dataMessageFrame.ToBytes()); }
/// <summary> /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames. /// </summary> public void SendPingCheck() { WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; pingFrame.Header.IsEnd = true; _pingtime = Util.EnvironmentTickCount(); SendSocket(pingFrame.ToBytes()); }
/// <summary> /// Sends a string to the other side /// </summary> /// <param name="message">the string message that is to be sent</param> public void SendMessage(string message) { byte[] messagedata = Encoding.UTF8.GetBytes(message); WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; textMessageFrame.Header.IsEnd = true; SendSocket(textMessageFrame.ToBytes()); }
public void SendData(byte[] data) { if (_initialMsgTimeout > 0) { _receiveDone.Set(); _initialMsgTimeout = 0; } WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data }; dataMessageFrame.Header.IsEnd = true; dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; SendSocket(dataMessageFrame.ToBytes()); }
/// <summary> /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames. /// </summary> public void SendPingCheck() { if (_initialMsgTimeout > 0) { _receiveDone.Set(); _initialMsgTimeout = 0; } WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; pingFrame.Header.IsEnd = true; _pingtime = Util.EnvironmentTickCount(); SendSocket(pingFrame.ToBytes()); }
/// <summary> /// Sends a string to the other side /// </summary> /// <param name="message">the string message that is to be sent</param> public void SendMessage(string message) { if (_initialMsgTimeout > 0) { _receiveDone.Set(); _initialMsgTimeout = 0; } byte[] messagedata = Encoding.UTF8.GetBytes(message); WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; textMessageFrame.Header.IsEnd = true; SendSocket(textMessageFrame.ToBytes()); }
/// <summary> /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader, /// and an int to move the buffer forward when it reads a header. False when it can't read a header /// </summary> /// <param name="pBuffer">Bytes read from the stream</param> /// <param name="pOffset">Starting place in the stream to begin trying to read from</param> /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the /// buffer's length is larger then the data in it</param> /// <param name="oHeader">Outputs the read WebSocket frame header</param> /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param> /// <returns>True if it got a header, False if it didn't get a header</returns> public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader, out int moveBuffer) { oHeader = WebsocketFrameHeader.ZeroHeader; int minumheadersize = 2; if (length > pBuffer.Length - pOffset) throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied"); if (length < minumheadersize) { moveBuffer = 0; return false; } byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3 byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block oHeader = new WebsocketFrameHeader(); oHeader.SetDefault(); if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit) { oHeader.IsEnd = true; } else { oHeader.IsEnd = false; } //Opcode oHeader.Opcode = (WebSocketReader.OpCode)nibble2; //Mask oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7); // Payload length oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F); int index = 2; // LargerPayload length starts at byte 3 switch (oHeader.PayloadLen) { case 126: minumheadersize += 2; if (length < minumheadersize) { moveBuffer = 0; return false; } Array.Reverse(pBuffer, pOffset + index, 2); // two bytes oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index); index += 2; break; case 127: // we got more this is a bigger frame // 8 bytes - uint64 - most significant bit 0 network byte order minumheadersize += 8; if (length < minumheadersize) { moveBuffer = 0; return false; } Array.Reverse(pBuffer, pOffset + index, 8); oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index); index += 8; break; } //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation if (oHeader.IsMasked) { minumheadersize += 4; if (length < minumheadersize) { moveBuffer = 0; return false; } oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index); index += 4; } moveBuffer = index; return true; }
/// <summary> /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader, /// and an int to move the buffer forward when it reads a header. False when it can't read a header /// </summary> /// <param name="pBuffer">Bytes read from the stream</param> /// <param name="pOffset">Starting place in the stream to begin trying to read from</param> /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the /// buffer's length is larger then the data in it</param> /// <param name="oHeader">Outputs the read WebSocket frame header</param> /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param> /// <returns>True if it got a header, False if it didn't get a header</returns> public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader, out int moveBuffer) { oHeader = WebsocketFrameHeader.ZeroHeader; int minumheadersize = 2; if (length > pBuffer.Length - pOffset) { throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied"); } if (length < minumheadersize) { moveBuffer = 0; return(false); } byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3 byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block oHeader = new WebsocketFrameHeader(); oHeader.SetDefault(); if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit) { oHeader.IsEnd = true; } else { oHeader.IsEnd = false; } //Opcode oHeader.Opcode = (WebSocketReader.OpCode)nibble2; //Mask oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7); // Payload length oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F); int index = 2; // LargerPayload length starts at byte 3 switch (oHeader.PayloadLen) { case 126: minumheadersize += 2; if (length < minumheadersize) { moveBuffer = 0; return(false); } Array.Reverse(pBuffer, pOffset + index, 2); // two bytes oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index); index += 2; break; case 127: // we got more this is a bigger frame // 8 bytes - uint64 - most significant bit 0 network byte order minumheadersize += 8; if (length < minumheadersize) { moveBuffer = 0; return(false); } Array.Reverse(pBuffer, pOffset + index, 8); oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index); index += 8; break; } //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation if (oHeader.IsMasked) { minumheadersize += 4; if (length < minumheadersize) { moveBuffer = 0; return(false); } oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index); index += 4; } moveBuffer = index; return(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); } 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 = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _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); 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(string.Empty); } catch (ObjectDisposedException) { Close(string.Empty); } }