Пример #1
0
 /// <summary>
 /// Raises the <see cref="E:MessageReceived"/> event.
 /// </summary>
 /// <param name="e">The <see cref="WebSocketClient.WebSocketEventArgs"/> instance containing the event data.</param>
 protected void OnMessageReceived(WebSocketEventArgs e)
 {
     if (this.MessageReceived != null)
     {
         this.MessageReceived(this, e);
     }
 }
Пример #2
0
        /// <summary>
        /// Callback invoked when an asynchronous socket read completes
        /// </summary>
        /// <param name="asyncResult">An IAsyncResult that stores any state information and any user defined data for this asynchronous operation.</param>
        private void ReadCallback(IAsyncResult asyncResult)
        {
            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "WebSocket ReadCallback. Thread ID: {0}", Thread.CurrentThread.ManagedThreadId));

            StateObject so = (StateObject)asyncResult.AsyncState;
            Socket      s  = so.Socket;

            byte[] buffer = so.Buffer;

            int length = s.EndReceive(asyncResult);

            if (length > 0)
            {
                int index = 0;

                while (index < length)
                {
                    if (so.IsFinalFragment.HasValue && so.Opcode.HasValue && so.PayloadLengthFinalized && so.IsMasked.HasValue)
                    {
                        if (so.Opcode == Opcode.Continuation)
                        {
                            so.Opcode = so.FirstOpcode;
                        }

                        int leftToGo        = (int)so.PayloadLength - so.Index;
                        int remainingBuffer = length - index;

                        if (so.Opcode == Opcode.Text)
                        {
                            if (leftToGo <= remainingBuffer)
                            {
                                // all buffer read
                                so.Text.Append(Encoding.UTF8.GetString(buffer, index, leftToGo));
                                index += leftToGo;

                                StringBuilder sb          = null;
                                Opcode        firstOpcode = so.Opcode.Value;

                                if (so.IsFinalFragment.Value)
                                {
                                    // final fragment
                                    WebSocketEventArgs args = new WebSocketEventArgs(so.Text.ToString());
                                    this.OnMessageReceived(args);
                                }
                                else
                                {
                                    // more fragments
                                    sb = so.Text;
                                }

                                so             = new StateObject();
                                so.Socket      = s;
                                so.FirstOpcode = firstOpcode;

                                if (sb != null)
                                {
                                    so.Text = sb;
                                }
                            }
                            else
                            {
                                // more unread buffer to go!
                                so.Text.Append(Encoding.UTF8.GetString(buffer, index, remainingBuffer));
                                index     = length;
                                so.Index += remainingBuffer;
                            }
                        }
                        else if (so.Opcode == Opcode.Binary)
                        {
                            // binary
                            Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Client closing due to an unacceptable datatype: Binary"));
                            this.Close(CloseReason.UnacceptableDatatype, false);

                            so        = new StateObject();
                            so.Socket = s;

                            break;
                        }
                        else if (so.Opcode == Opcode.Close)
                        {
                            Debug.WriteLine("WebSocket Close opcode received.");

                            // connection close
                            if (leftToGo <= remainingBuffer)
                            {
                                // all buffer read
                                byte[] temp = new byte[leftToGo];
                                Array.Copy(buffer, index, temp, 0, leftToGo);
                                so.ControlFrameBinaryData.AddRange(temp);
                                index += leftToGo;

                                byte[] closeData = so.ControlFrameBinaryData.ToArray();

                                ushort closeReason = 0;
                                if (closeData.Length == 2)
                                {
                                    closeReason = BitConverter.ToUInt16(closeData, 0);
                                }

                                string closeReasonText = string.Empty;
                                if (closeData.Length > 2)
                                {
                                    closeReasonText = Encoding.UTF8.GetString(closeData, 2, closeData.Length - 2);
                                }

                                if (closeReason == (ushort)CloseReason.Normal)
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing normally: {0}", closeReasonText));
                                }
                                else if (closeReason == (ushort)CloseReason.GoingAway)
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing because server is going away: {0}", closeReasonText));
                                }
                                else if (closeReason == (ushort)CloseReason.FrameTooLarge)
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing because I sent a frame that was too large: {0}", closeReasonText));
                                }
                                else if (closeReason == (ushort)CloseReason.ProtocolError)
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing due to a Protocol Error: {0}", closeReasonText));
                                }
                                else if (closeReason == (ushort)CloseReason.UnacceptableDatatype)
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing because I sent an unacceptable datatype: {0}", closeReasonText));
                                }
                                else
                                {
                                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing for unknown reason: {0} ({1})", closeReasonText, closeReason));
                                }

                                Close(CloseReason.Normal, true);

                                so        = new StateObject();
                                so.Socket = s;

                                break;
                            }
                            else
                            {
                                // more unread buffer to go!
                                byte[] temp = new byte[leftToGo];
                                Array.Copy(buffer, index, temp, 0, remainingBuffer);
                                so.ControlFrameBinaryData.AddRange(temp);
                                index     = length;
                                so.Index += remainingBuffer;
                            }
                        }
                        else if (so.Opcode == Opcode.Ping)
                        {
                            Debug.WriteLine("WebSocket Ping opcode received.");

                            // ping
                            if (leftToGo <= remainingBuffer)
                            {
                                // all buffer read
                                byte[] temp = new byte[leftToGo];
                                Array.Copy(buffer, index, temp, 0, leftToGo);
                                so.ControlFrameBinaryData.AddRange(temp);
                                index += leftToGo;

                                StringBuilder sb          = null;
                                Opcode        firstOpcode = so.Opcode.Value;

                                // Send Pong
                                this.Send(Opcode.Pong, so.ControlFrameBinaryData.ToArray());

                                if (so.Text.Length > 0)
                                {
                                    sb = so.Text;
                                }

                                so             = new StateObject();
                                so.Socket      = s;
                                so.FirstOpcode = firstOpcode;

                                if (sb != null)
                                {
                                    so.Text = sb;
                                }
                            }
                            else
                            {
                                // more unread buffer to go!
                                byte[] temp = new byte[leftToGo];
                                Array.Copy(buffer, index, temp, 0, remainingBuffer);
                                so.ControlFrameBinaryData.AddRange(temp);
                                index     = length;
                                so.Index += remainingBuffer;
                            }
                        }
                        else if (so.Opcode == Opcode.Pong)
                        {
                            // pong
                            throw new NotImplementedException("Pong opcode not implemented");
                        }
                        else
                        {
                            // this shouldn't be set
                            throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Unknown opcode: {0}", so.Opcode));
                        }
                    }
                    else
                    {
                        if (index < length && (!so.IsFinalFragment.HasValue || !so.Opcode.HasValue))
                        {
                            byte first = buffer[index++];
                            so.IsFinalFragment = (first & 0x80) > 0;
                            int opcode = first & 0xF;
                            if (Enum.IsDefined(typeof(Opcode), opcode))
                            {
                                so.Opcode = (Opcode)Enum.ToObject(typeof(Opcode), opcode);
                            }
                            else
                            {
                                throw new NotImplementedException(string.Format(CultureInfo.InvariantCulture, "Unknown opcode: {0}", so.Opcode));
                            }
                        }

                        if (index < length && (!so.IsMasked.HasValue || so.PayloadLength < 0))
                        {
                            byte second = buffer[index++];
                            so.IsMasked      = (second & 0x80) > 0;
                            so.PayloadLength = second & 0x7F;
                        }

                        if (!so.PayloadLengthFinalized && so.PayloadLength > -1)
                        {
                            if (so.PayloadLength <= 125)
                            {
                                so.PayloadLengthFinalized = true;
                            }
                            else if (so.PayloadLength == 126)
                            {
                                while (index < length && so.PayloadLengthIndex < 2)
                                {
                                    so.PayloadLengthByteArray[so.PayloadLengthIndex++] = buffer[index++];
                                }

                                if (so.PayloadLengthIndex == 2)
                                {
                                    so.PayloadLength          = (ushort)IPAddress.HostToNetworkOrder(BitConverter.ToInt16(so.PayloadLengthByteArray, 0));
                                    so.PayloadLengthFinalized = true;
                                }
                            }
                            else if (so.PayloadLength == 127)
                            {
                                while (index < length && so.PayloadLengthIndex < 8)
                                {
                                    so.PayloadLengthByteArray[so.PayloadLengthIndex++] = buffer[index++];
                                }

                                if (so.PayloadLengthIndex == 8)
                                {
                                    so.PayloadLength          = IPAddress.HostToNetworkOrder(BitConverter.ToInt64(so.PayloadLengthByteArray, 0));
                                    so.PayloadLengthFinalized = true;
                                }
                            }
                        }

                        if (so.PayloadLength > int.MaxValue)
                        {
                            Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Client closing because I received a frame that was too large"));
                            Close(CloseReason.FrameTooLarge, false);

                            so        = new StateObject();
                            so.Socket = s;

                            break;
                        }

                        if (so.IsMasked.HasValue && so.IsMasked.Value)
                        {
                            while (index < length && so.MaskIndex < 4)
                            {
                                so.Mask[so.MaskIndex++] = buffer[index++];
                            }
                        }
                    }
                }

                if (so.PayloadLength > -1)
                {
                    if (so.Opcode == Opcode.Close)
                    {
                        Debug.WriteLine("WebSocket Close opcode received.");
                        Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "Server closing for unknown reason"));

                        this.Close(CloseReason.Normal, true);

                        so        = new StateObject();
                        so.Socket = s;
                    }
                }

                try
                {
                    s.BeginReceive(so.Buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(this.ReadCallback), so);
                }
                catch (ObjectDisposedException)
                {
                    // the socket has been closed, so we don't need to read anymore
                }
            }
            else
            {
                if (this.hasSentClose)
                {
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "WebSocket Socket Closed after sending close opcode. Thread ID: {0}. Socket Connected: {1}", Thread.CurrentThread.ManagedThreadId, this.socket.Connected));
                }
                else
                {
                    Debug.WriteLine(string.Format(CultureInfo.InvariantCulture, "WebSocket Socket Closed unexpectedly. Thread ID: {0}. Socket Connected: {1}", Thread.CurrentThread.ManagedThreadId, this.socket.Connected));
                }

                s.Close();
                this.OnClosed();
            }
        }