Exemple #1
0
		/// <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);
			}
		}
        /// <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;
        }
        public static WSFrame HarriOlli(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;
        }
 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 smReceive()
        {
            int bytesRead = 0;
            this.lastRcvdFrame = null;

            // start activity timer
            if (this.activityTimerEnabled)
            {
                this.activityTimer.Start(this.waitActivityTimeout, ActivityTimerState.WaitingForMessage);
            }

            while (this.State == WebSocketState.Connected)
            {
                // 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.CloseTcpConnection;
                                this.smSendCloseResponse();
                                return;
                            case WSFrameType.Ping:
                                this.LogBufferContent("Ping frame received: ", this.lastRcvdFrame.FrameData, 0, this.lastRcvdFrame.FrameData.Length);
                                this.smSendPongMessage();
                                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)
                {
                    bytesRead = this.socketStream.Read(this.receiveBuffer, this.bytesInBuffer, this.receiveBuffer.Length - this.bytesInBuffer);
                    this.bytesInBuffer += bytesRead;
                    continue;
                }

                // check activity timer
                if (this.activityTimer.HasTimedOut)
                {
                    switch ((ActivityTimerState)this.activityTimer.State)
                    {
                        case ActivityTimerState.MessageSent:
                        case ActivityTimerState.WaitingForMessage:
                            this.smSendPingMessage();
                            activityTimer.Restart(ActivityTimerState.WaitingForPingResponse);
                            break;
                        case ActivityTimerState.WaitingForPingResponse:
                            Logger.WriteError(this.loggerID, string.Concat("Ping response timed out."));
                            activityTimer.Stop();
                            this.state = WebSocketState.Disconnecting;
                            this.subState = SubState.CloseTcpConnection;
                            break;
                    }
                }
            }
        }
        /// <summary>
        /// Provides a client for connecting to WebSocket services.
        /// </summary>
        /// <param name="logSourceID">Identifier to be used when calling the Logger service.</param>
        /// <param name="options">Collection of WebSocket options.</param>
        public WebSocketClient(string logSourceID, WSOptions options = null)
        {
            this.loggerID = logSourceID;

            this.options = options;
            if (this.options == null)
                this.options = new WSOptions();

            this.origin = this.options.Origin;
            this.subProtocol = this.options.SubProtocol;
            this.extensions = this.options.Extensions;

            this.activityTimerEnabled = this.options.ActivityTimerEnabled;
            this.sendFramesMasked = this.options.MaskingEnabled;

            this.waitHandshakeTimeout = this.options.HandshakeTimeout;
            this.waitCloseMsgTimeout = this.options.CloseMsgTimeout;
            this.waitReceiveTimeout = this.options.ReceiveTimeout;
            this.waitActivityTimeout = this.options.ActivityTimeout;
            this.waitPingRespTimeout = this.options.PingRespTimeout;

            this.activityTimer = new TimerEx();

            this.state = WebSocketState.Initialized;

            this.eventLock = new object();
            this.sendLock = new object();
            this.isDisposed = false;

            this.receiveBuffer = new byte[this.options.MaxReceiveFrameLength];
            this.lastRcvdFrame = null;

            this.sendQueue = new WSFrameQueue(this.options.MaxSendQueueSize);

            // start state machine
            try
            {
                this.runThreadLoop = true;
                this.runStateMachine = new AutoResetEvent(false);
                this.stateMachineThread = new Thread(new ThreadStart(this.WSStateMachine));
                this.stateMachineThread.Start();
            }
            catch (Exception ex)
            {
                Logger.WriteError(this.loggerID, ex.Message, ex.StackTrace);
            }
        }
        protected virtual void Dispose(bool disposing)
        {
            if (this.isDisposed)
                return;

            if (disposing)
            {
                // disconnect
                this.Disconnect();
                DateTime timeoutTime = DateTime.Now.AddSeconds(20);
                while (this.state != WebSocketState.Disconnected && DateTime.Now < timeoutTime)
                    Thread.Sleep(500);

                // kill thread
                this.runThreadLoop = false;
                this.runStateMachine.Set();
                this.stateMachineThread.Join(1000);

                // clear the message queue
                if (this.sendQueue != null)
                    this.sendQueue.Clear();

                // kill timer
                if (this.activityTimer != null)
                    this.activityTimer.Dispose();

                // cleanup streams
                if (this.socketStream != null)
                    this.socketStream.Close();

                // cleanup sockets
                if (this.socket != null)
                    this.socket.Close();
            }

            // set everything to null
            this.eventLock = null;
            this.sendLock = null;
            this.activityTimer = null;
            this.sendQueue = null;
            this.socket = null;
            this.socketStream = null;
            this.receiveBuffer = null;
            this.lastRcvdFrame = null;
            this.serverUri = null;
            this.loggerID = null;
            this.origin = null;
            this.subProtocol = null;
            this.extensions = null;
            this.securityKey = null;
            this.options = null;

            // make sure we don't dispose again
            this.isDisposed = true;
        }
Exemple #8
0
		/// <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());
				}
			}
		}