protected void DoSendPongMessage() { WSFrame wsFrame = WSFrame.CreateFrame(WSFrameType.Pong, this.options.MaskingEnabled, "Hello"); this.LogBufferContent("Sending pong frame: ", wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.socketStream.Write(wsFrame.FrameData, 0, wsFrame.FrameData.Length); }
protected void DoSendCloseFrame() { WSFrame wsFrame = WSFrame.CreateFrame(WSFrameType.Close, this.options.MaskingEnabled, ArrayUtil.Concat(this.closeStatus, this.closeReason)); this.LogBufferContent("Sending close frame: ", wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.socketStream.Write(wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.subState = SubState.WaitForCloseResponse; }
/// <summary> /// Removes the WSFrame at the top of the queue, and returns it. /// </summary> /// <returns>The WSFrame that is removed from the top of the queue.</returns> public WSFrame Dequeue() { WSFrame wsFrame = null; lock (this.queue.SyncRoot) { wsFrame = (WSFrame)this.queue.Dequeue(); } return(wsFrame); }
/// <summary> /// Adds a WSFrame to the bottom of the queue. /// </summary> /// <param name="wsFrame">The WSFrame to add to the queue.</param> /// <remarks>If Count == MaxCount, then the WSFrame at the top of the queue is removed and discarded before the new entry is added.</remarks> public void Enqueue(WSFrame wsFrame) { lock (this.queue.SyncRoot) { if (this.queue.Count == this.maxCount) { this.queue.Dequeue(); } this.queue.Enqueue(wsFrame); } }
public static void ApplyMask(WSFrame wsFrame) { if (wsFrame != null && wsFrame.payloadLength > 0) { int maskIndex = 0; for (int i = 0; i < wsFrame.payloadLength; i++) { maskIndex = wsFrame.maskIndex + (i % 4); wsFrame.frameData[wsFrame.payloadIndex + i] = (byte)(wsFrame.frameData[wsFrame.payloadIndex + i] ^ wsFrame.frameData[maskIndex]); } } }
protected void EnqueueMessage(WSFrameType opCode, bool isMasked, byte[] payLoad, bool highPriority = false) { WSFrame wsFrame = WSFrame.CreateFrame(opCode, isMasked, payLoad); if (highPriority) { this.sendQueue.Poke(wsFrame); } else { this.sendQueue.Enqueue(wsFrame); } }
protected bool DequeueAndSendMessages() { bool messagesSent = this.sendQueue.Count > 0; lock (this.sendLock) { while (this.sendQueue.Count > 0) { WSFrame wsFrame = this.sendQueue.Dequeue(); this.LogBufferContent("Sending data frame: ", wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.socketStream.Write(wsFrame.FrameData, 0, wsFrame.FrameData.Length); } } return(messagesSent); }
/// <summary> /// Inserts a WSFrame at the top of the queue. /// </summary> /// <param name="wsFrame">The WSFrame to add to the queue.</param> /// <remarks>May be used to insert high-priority messages at the top of the queue.</remarks> public void Poke(WSFrame wsFrame) { lock (this.queue.SyncRoot) { if (this.queue.Count > this.maxCount) { this.queue.Dequeue(); } int count = this.queue.Count; this.queue.Enqueue(wsFrame); while (count > 0) { this.queue.Enqueue(this.queue.Dequeue()); } } }
protected void DoSendCloseResponse() { byte[] payLoad = null; if (this.lastRcvdFrame.OpCode == WSFrameType.Close && this.lastRcvdFrame.PayloadLength > 0) { // reply with the same status code and reason payLoad = this.lastRcvdFrame.PayloadBytes; } WSFrame wsFrame = WSFrame.CreateFrame(WSFrameType.Close, this.options.MaskingEnabled, payLoad); this.LogBufferContent("Sending close frame: ", wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.socketStream.Write(wsFrame.FrameData, 0, wsFrame.FrameData.Length); this.socketStream.Flush(); this.subState = SubState.CloseTcpConnection; }
protected void DoWaitCloseResponse() { int bytesRead = 0; this.posHeaderEOF = 0; this.bytesInBuffer = 0; if (this.socket.Poll(this.waitCloseMsgTimeout, SelectMode.SelectRead) == true && this.socket.Available > 0) { do { bytesRead = this.socketStream.Read(this.receiveBuffer, this.bytesInBuffer, this.receiveBuffer.Length - this.bytesInBuffer); if (bytesRead > 0) { this.bytesInBuffer += bytesRead; if (WSFrame.TryParse(this.receiveBuffer, 0, this.bytesInBuffer, this.options.MaxReceiveFrameLength, out this.lastRcvdFrame) == true) { // remove data-frame from buffer Array.Copy(this.receiveBuffer, this.lastRcvdFrame.FrameData.Length, this.receiveBuffer, 0, this.bytesInBuffer - this.lastRcvdFrame.FrameData.Length); this.bytesInBuffer = this.bytesInBuffer - this.lastRcvdFrame.FrameData.Length; // check for close frame if (this.lastRcvdFrame.OpCode == WSFrameType.Close) { this.LogBufferContent("Close frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); this.subState = SubState.CloseTcpConnection; return; } this.LogBufferContent("Data frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); } } }while (this.socket.Available > 0 && this.bytesInBuffer < this.receiveBuffer.Length); } Logger.WriteError(this.loggerID, "Close frame not received."); this.subState = SubState.Failed; }
/// <summary> /// Parses a byte-array at startPos to its equivalent RFC 6455 BFP frame. The return value indicates whether the operation succeeded. /// </summary> /// <param name="dataBuffer">A source byte-array containing the bytes to convert.</param> /// <param name="startPos">Start position in the source byte-array.</param> /// <param name="length">Number of bytes to parse.</param> /// <param name="maxFrameLength">Maximum frame size allowed.</param> /// <param name="dataFrame">The resulting BFP frame or null.</param> /// <returns>true if the source byte-array was converted successfully; otherwise, false</returns> public static bool TryParse(byte[] dataBuffer, int startPos, int length, int maxFrameLength, out WSFrame dataFrame) { dataFrame = null; if (dataBuffer == null || dataBuffer.Length == 0 || dataBuffer.Length < (startPos + length)) { return(false); } byte controlByte0 = dataBuffer[startPos]; byte controlByte1 = dataBuffer[startPos + 1]; byte length8 = (byte)(controlByte1 & 0x7F); bool isMasked = ((controlByte1 & 0x80) == 0x80); int maskIndex = 0; byte[] mask = new byte[4]; // get payload index and length Int32 payloadIndex = 0; UInt64 payloadLength64 = 0; if (length8 == 127) { maskIndex = (isMasked ? 10 : 0); payloadIndex = (isMasked ? 14 : 10); payloadLength64 = dataBuffer.ToUInt64(startPos + 2); } else if (length8 == 126) { maskIndex = (isMasked ? 4 : 0); payloadIndex = (isMasked ? 8 : 4); payloadLength64 = dataBuffer.ToUInt16(startPos + 2); } else // must be <= 125 { maskIndex = (isMasked ? 2 : 0); payloadIndex = (isMasked ? 6 : 2); payloadLength64 = length8; } // calculate frame length Int32 frameLength = payloadIndex + (Int32)payloadLength64; // check for max allowed length if (frameLength > maxFrameLength) { throw new NotSupportedException(string.Concat("Maximum frame size of ", maxFrameLength, " bytes has been exceeded.")); } // create WSFrame if full frame has been received if (frameLength <= length) { dataFrame = new WSFrame() { maskIndex = maskIndex, payloadIndex = payloadIndex, payloadLength = (Int32)payloadLength64, frameData = new byte[frameLength] }; Array.Copy(dataBuffer, startPos, dataFrame.frameData, 0, frameLength); if (isMasked) { ApplyMask(dataFrame); } return(true); } return(false); }
/// <summary> /// Creates an RFC 6455 BFP frame with the opcode and payload bytes specified. /// </summary> /// <param name="opCode">BFP opcode.</param> /// <param name="isMasked">Boolean indicating whether to mask the payload.</param> /// <param name="payLoad">Payload bytes.</param> /// <returns></returns> public static WSFrame CreateFrame(WSFrameType opCode, bool isMasked, byte[] payLoad) { // TODO - add code to support continuation frames // - add long maxFrameLength parameter // - return array of WSFrame objects byte controlByte0 = (byte)(0x80 | (byte)opCode); byte controlByte1 = (isMasked && payLoad != null && payLoad.Length > 0 ? (byte)0x80 : (byte)0x00); byte[] mask = (isMasked ? CryptoUtils.GetRandomBytes(4) : new byte[0]); int maskIndex = 0; Int32 payloadIndex = 0; UInt16 length16 = 0; UInt32 length32 = 0; UInt64 length64 = 0; byte[] frameData = null; if (payLoad == null || payLoad.Length == 0) { length32 = 0; payloadIndex = 0; frameData = ArrayUtil.Concat(controlByte0, controlByte1); } else if (payLoad.Length <= 125) { controlByte1 = (byte)(controlByte1 | (byte)payLoad.Length); length32 = (UInt32)payLoad.Length; maskIndex = (isMasked ? 2 : 0); payloadIndex = (isMasked ? 6 : 2); frameData = ArrayUtil.Concat(controlByte0, controlByte1, mask, payLoad); } else if (payLoad.Length <= UInt16.MaxValue) { controlByte1 = (byte)(controlByte1 | (byte)126); length16 = (UInt16)payLoad.Length; length32 = length16; maskIndex = (isMasked ? 4 : 0); payloadIndex = (isMasked ? 8 : 4); frameData = ArrayUtil.Concat(controlByte0, controlByte1, length16, mask, payLoad); } else { controlByte1 = (byte)(controlByte1 | (byte)127); length32 = (UInt32)payLoad.Length; length64 = (UInt64)payLoad.Length; maskIndex = (isMasked ? 10 : 0); payloadIndex = (isMasked ? 14 : 10); frameData = ArrayUtil.Concat(controlByte0, controlByte1, length64, mask, payLoad); } WSFrame dataFrame = new WSFrame() { maskIndex = maskIndex, payloadIndex = payloadIndex, payloadLength = (Int32)length32, frameData = frameData }; if (isMasked) { // Logger.WriteDebug("wsframe", string.Concat("raw: ", frameData.ToHex())); ApplyMask(dataFrame); } return(dataFrame); }
protected void DoReceiving() { // process data in buffer if (this.bytesInBuffer > 0) { if (WSFrame.TryParse(this.receiveBuffer, 0, this.bytesInBuffer, this.options.MaxReceiveFrameLength, out this.lastRcvdFrame) == true) { // restart activity timer if (this.activityTimerEnabled) { this.activityTimer.Restart(ActivityTimerState.WaitingForMessage); } // remove data-frame from buffer Array.Copy(this.receiveBuffer, this.lastRcvdFrame.FrameData.Length, this.receiveBuffer, 0, this.bytesInBuffer - this.lastRcvdFrame.FrameData.Length); this.bytesInBuffer = this.bytesInBuffer - this.lastRcvdFrame.FrameData.Length; // take action based on opcode switch (this.lastRcvdFrame.OpCode) { case WSFrameType.Continuation: // TODO - implement continuation frames break; case WSFrameType.Text: this.LogBufferContent("Text frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); this.OnTextReceived(this.lastRcvdFrame.PayloadText); break; case WSFrameType.Binary: this.LogBufferContent("Data frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); this.OnDataReceived(this.lastRcvdFrame.PayloadBytes); break; case WSFrameType.Close: this.LogBufferContent("Close frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); // based on RFC6455 we must stop sending and receiving messages after Close response is sent this.activityTimer.Stop(); this.state = WebSocketState.Disconnecting; this.subState = SubState.SendCloseResponse; return; case WSFrameType.Ping: this.LogBufferContent("Ping frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); this.DoSendPongMessage(); break; case WSFrameType.Pong: this.LogBufferContent("Pong frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); // no need to do anything break; default: this.LogBufferContent("Unknown frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length); break; } } } // send queued messages if (this.DequeueAndSendMessages() && this.activityTimerEnabled) { this.activityTimer.Restart(ActivityTimerState.MessageSent); } // get more data if (this.socket.Poll(this.waitReceiveTimeout, SelectMode.SelectRead) == true && this.socket.Available > 0) { int bytesRead = this.socketStream.Read(this.receiveBuffer, this.bytesInBuffer, this.receiveBuffer.Length - this.bytesInBuffer); this.bytesInBuffer += bytesRead; if (bytesRead > 0) { this.activityTimer.Restart(ActivityTimerState.WaitingForMessage); } } // check activity timer if (this.activityTimerEnabled && this.activityTimer.HasTimedOut) { switch ((ActivityTimerState)this.activityTimer.State) { case ActivityTimerState.MessageSent: case ActivityTimerState.WaitingForMessage: this.DoSendPingMessage(); activityTimer.Restart(ActivityTimerState.WaitingForPingResponse); break; case ActivityTimerState.WaitingForPingResponse: Logger.WriteError(this.loggerID, string.Concat("Ping response timed out.")); activityTimer.Stop(); this.subState = SubState.Failed; break; } } }