/// <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); } }
/// <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(); } }