/// <summary> /// Called when a Pong frame is received /// </summary> /// <param name="e"></param> protected virtual void OnPong(PongEventArgs e) { Pong?.Invoke(this, e); }
/// <summary> /// Receive web socket result /// </summary> /// <param name="buffer">The buffer to copy data into</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>The web socket result details</returns> public override async Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken) { try { // we may receive control frames so reading needs to happen in an infinite loop while (true) { // allow this operation to be cancelled from iniside OR outside this instance using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_internalReadCts.Token, cancellationToken)) { WebSocketFrame frame; try { if (_readCursor.NumBytesLeftToRead > 0) { // If the buffer used to read the frame was too small to fit the whole frame then we need to "remember" this frame // and return what we have. Subsequent calls to the read function will simply continue reading off the stream without // decoding the first few bytes as a websocket header. _readCursor = await WebSocketFrameReader.ReadFromCursorAsync(_stream, buffer, _readCursor, linkedCts.Token); frame = _readCursor.WebSocketFrame; } else { _readCursor = await WebSocketFrameReader.ReadAsync(_stream, buffer, linkedCts.Token); frame = _readCursor.WebSocketFrame; } } catch (InternalBufferOverflowException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.MessageTooBig, "Frame too large to fit in buffer. Use message fragmentation", ex); throw; } catch (ArgumentOutOfRangeException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, "Payload length out of range", ex); throw; } catch (EndOfStreamException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InvalidPayloadData, "Unexpected end of stream encountered", ex); throw; } catch (OperationCanceledException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.EndpointUnavailable, "Operation cancelled", ex); throw; } catch (Exception ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Error reading WebSocket frame", ex); throw; } var endOfMessage = frame.IsFinBitSet && _readCursor.NumBytesLeftToRead == 0; switch (frame.OpCode) { case WebSocketOpCode.ConnectionClose: return(await RespondToCloseFrame(frame, buffer, linkedCts.Token)); case WebSocketOpCode.Ping: ArraySegment <byte> pingPayload = new ArraySegment <byte>(buffer.Array, buffer.Offset, _readCursor.NumBytesRead); await SendPongAsync(pingPayload, linkedCts.Token); break; case WebSocketOpCode.Pong: ArraySegment <byte> pongBuffer = new ArraySegment <byte>(buffer.Array, _readCursor.NumBytesRead, buffer.Offset); Pong?.Invoke(this, new PongEventArgs(pongBuffer)); break; case WebSocketOpCode.TextFrame: if (!frame.IsFinBitSet) { // continuation frames will follow, record the message type Text _continuationFrameMessageType = WebSocketMessageType.Text; } return(new WebSocketReceiveResult(_readCursor.NumBytesRead, WebSocketMessageType.Text, endOfMessage)); case WebSocketOpCode.BinaryFrame: if (!frame.IsFinBitSet) { // continuation frames will follow, record the message type Binary _continuationFrameMessageType = WebSocketMessageType.Binary; } return(new WebSocketReceiveResult(_readCursor.NumBytesRead, WebSocketMessageType.Binary, endOfMessage)); case WebSocketOpCode.ContinuationFrame: return(new WebSocketReceiveResult(_readCursor.NumBytesRead, _continuationFrameMessageType, endOfMessage)); default: Exception ex = new NotSupportedException($"Unknown WebSocket opcode {frame.OpCode}"); await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, ex.Message, ex); throw ex; } } } } catch (Exception catchAll) { // Most exceptions will be caught closer to their source to send an appropriate close message (and set the WebSocketState) // However, if an unhandled exception is encountered and a close message not sent then send one here if (_state == WebSocketState.Open) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Unexpected error reading from WebSocket", catchAll); } throw; } }
/// <summary> /// Receive web socket result /// </summary> /// <param name="buffer">The buffer to copy data into</param> /// <param name="cancellationToken">The cancellation token</param> /// <returns>The web socket result details</returns> public override async Task <WebSocketReceiveResult> ReceiveAsync(ArraySegment <byte> buffer, CancellationToken cancellationToken) { try { // we may receive control frames so reading needs to happen in an infinite loop while (true) { // allow this operation to be cancelled from iniside OR outside this instance using (CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_internalReadCts.Token, cancellationToken)) { WebSocketFrame frame = null; try { frame = await WebSocketFrameReader.ReadAsync(_stream, buffer, linkedCts.Token); Events.Log.ReceivedFrame(_guid, frame.OpCode, frame.IsFinBitSet, frame.Count); } catch (InternalBufferOverflowException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.MessageTooBig, "Frame too large to fit in buffer. Use message fragmentation", ex); throw; } catch (ArgumentOutOfRangeException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, "Payload length out of range", ex); throw; } catch (EndOfStreamException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InvalidPayloadData, "Unexpected end of stream encountered", ex); throw; } catch (OperationCanceledException ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.EndpointUnavailable, "Operation cancelled", ex); throw; } catch (Exception ex) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Error reading WebSocket frame", ex); throw; } switch (frame.OpCode) { case WebSocketOpCode.ConnectionClose: return(await RespondToCloseFrame(frame, buffer, linkedCts.Token)); case WebSocketOpCode.Ping: ArraySegment <byte> pingPayload = new ArraySegment <byte>(buffer.Array, buffer.Offset, frame.Count); await SendPongAsync(pingPayload, linkedCts.Token); break; case WebSocketOpCode.Pong: ArraySegment <byte> pongBuffer = new ArraySegment <byte>(buffer.Array, frame.Count, buffer.Offset); Pong?.Invoke(this, new PongEventArgs(pongBuffer)); break; case WebSocketOpCode.TextFrame: if (!frame.IsFinBitSet) { // continuation frames will follow, record the message type Text _continuationFrameMessageType = WebSocketMessageType.Text; } return(new WebSocketReceiveResult(frame.Count, WebSocketMessageType.Text, frame.IsFinBitSet)); case WebSocketOpCode.BinaryFrame: if (!frame.IsFinBitSet) { // continuation frames will follow, record the message type Binary _continuationFrameMessageType = WebSocketMessageType.Binary; } return(new WebSocketReceiveResult(frame.Count, WebSocketMessageType.Binary, frame.IsFinBitSet)); case WebSocketOpCode.ContinuationFrame: return(new WebSocketReceiveResult(frame.Count, _continuationFrameMessageType, frame.IsFinBitSet)); default: Exception ex = new NotSupportedException($"Unknown WebSocket opcode {frame.OpCode}"); await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.ProtocolError, ex.Message, ex); throw ex; } } } } catch (Exception catchAll) { // Most exceptions will be caught closer to their source to send an appropriate close message (and set the WebSocketState) // However, if an unhandled exception is encountered and a close message not sent then send one here if (_state == WebSocketState.Open) { await CloseOutputAutoTimeoutAsync(WebSocketCloseStatus.InternalServerError, "Unexpected error reading from WebSocket", catchAll); } throw; } }
private void ProcessReceive(SocketAsyncEventArgs ioEventArgs) { var dataToken = (WSDataToken)ioEventArgs.UserToken; var socket = (WebSocket)dataToken.Socket; if (ioEventArgs.BytesTransferred == 0) { //对方主动关闭socket Closing(ioEventArgs, Opcode.Empty); return; } if (ioEventArgs.SocketError != SocketError.Success) { //Socket错误 Closing(ioEventArgs); return; } bool needPostAnother = TryReceiveMessage(ioEventArgs, out List <DataMessage> messages, out bool hasHandshaked); if (hasHandshaked) { Connected?.Invoke(this, new WebSocketEventArgs(socket)); } if (messages != null) { foreach (var message in messages) { try { switch (message.Opcode) { case Opcode.Close: Closing(ioEventArgs); needPostAnother = false; break; case Opcode.Ping: Ping?.Invoke(this, new WebSocketEventArgs(socket, message)); break; case Opcode.Pong: Pong?.Invoke(this, new WebSocketEventArgs(socket, message)); break; default: DataReceived?.Invoke(this, new WebSocketEventArgs(socket, message)); break; } } catch (Exception ex) { logger.Error("OnDataReceived error:{0}", ex); } } } if (needPostAnother) { if (socket.IsClosed) { ResetSAEAObject(ioEventArgs); } else { PostReceive(ioEventArgs); } } }