public static void Sync(Action action) => TaskUtil.Sync(action);
public static T Sync <T>(Func <T> func) => TaskUtil.Sync(func);
async Task RecvLoopAsync() { try { CancellationToken cancel = this.GrandCancel; LowerStream.ReadTimeout = Options.TimeoutComm; MemoryBuffer <byte> currentRecvingMessage = new MemoryBuffer <byte>(); while (true) { byte b1 = await LowerStream.ReceiveByteAsync(cancel).FlushOtherStreamIfPending(UpperStream); WebSocketOpcode opcode = (WebSocketOpcode)(b1 & 0x0f); byte b2 = await LowerStream.ReceiveByteAsync(cancel); bool isMasked = (b2 & 0b10000000)._ToBool(); int tmp = (b2 & 0b01111111); ulong payloadLen64 = 0; if (tmp <= 125) { payloadLen64 = (ulong)tmp; } else if (tmp == 126) { payloadLen64 = await LowerStream.ReceiveUInt16Async(cancel).FlushOtherStreamIfPending(UpperStream); } else if (tmp == 127) { payloadLen64 = await LowerStream.ReceiveUInt64Async(cancel).FlushOtherStreamIfPending(UpperStream); } if (payloadLen64 > (ulong)Options.RecvMaxPayloadLenPerFrame) { throw new ApplicationException($"payloadLen64 {payloadLen64} > Options.RecvMaxPayloadLenPerFrame {Options.RecvMaxPayloadLenPerFrame}"); } int payloadLen = (int)payloadLen64; Memory <byte> maskKey = default; if (isMasked) { maskKey = await LowerStream.ReceiveAllAsync(4, cancel).FlushOtherStreamIfPending(UpperStream); } Memory <byte> data = await LowerStream.ReceiveAllAsync(payloadLen, cancel).FlushOtherStreamIfPending(UpperStream); if (isMasked) { TaskUtil.Sync(() => { Span <byte> maskKeySpan = maskKey.Span; Span <byte> dataSpan = data.Span; for (int i = 0; i < dataSpan.Length; i++) { dataSpan[i] = (byte)(dataSpan[i] ^ maskKeySpan[i % 4]); } }); } if (opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin, WebSocketOpcode.Continue)) { if (Options.RespectMessageDelimiter == false) { if (data.Length >= 1) { await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); UpperStream.FastSendNonBlock(data, false); } } else { bool isFin = (b1 & 0b10000000)._ToBool(); if (isFin && opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin)) { // Single message if (data.Length >= 1) { await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); UpperStream.FastSendNonBlock(data, false); } } else if (isFin == false && opcode.EqualsAny(WebSocketOpcode.Text, WebSocketOpcode.Bin)) { // First message currentRecvingMessage.Clear(); if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize) { throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize."); } currentRecvingMessage.Write(data); } else if (isFin && opcode == WebSocketOpcode.Continue) { // Final continuous message if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize) { throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize."); } currentRecvingMessage.Write(data); if (currentRecvingMessage.Length >= 1) { await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); UpperStream.FastSendNonBlock(data, false); } currentRecvingMessage.Clear(); } else if (isFin == false && opcode == WebSocketOpcode.Continue) { // Intermediate continuous message if ((currentRecvingMessage.Length + data.Length) >= Options.RecvMaxTotalFragmentSize) { throw new ApplicationException("WebSocket: Exceeding Options.RecvMaxTotalFragmentSize."); } currentRecvingMessage.Write(data); } } } else if (opcode == WebSocketOpcode.Pong) { lock (this.PongQueueLock) { this.PongQueue.Enqueue(data); } this.SendPongEvent.Set(true); } else if (opcode == WebSocketOpcode.Ping) { lock (this.PongQueueLock) { this.PongQueue.Enqueue(data); } this.SendPongEvent.Set(true); } else if (opcode == WebSocketOpcode.Close) { throw new DisconnectedException(); } else { throw new ApplicationException($"WebSocket: Unknown Opcode: {(int)opcode}"); } } } catch (Exception ex) { this.UpperStream.Disconnect(); await this.CancelAsync(ex); } }