async Task RecvLoopAsync() { try { CancellationToken cancel = this.GrandCancel; while (true) { // データサイズを受信 int dataSize = await LowerStream.ReceiveSInt32Async(cancel).FlushOtherStreamIfPending(UpperStream); if (dataSize >= Consts.WideTunnelConsts.SpecialOpCode_Min && dataSize < Consts.WideTunnelConsts.SpecialOpCode_Max) { // 特殊コード受信 int code = dataSize; if (code == Consts.WideTunnelConsts.SpecialOpCode_S2C_SwitchToWebSocket_Ack) { // WebSocket 切替え完了応答 this.S2C_SwitchToWebSocket_Completed = true; this.S2C_SoecialOp_Completed_Event.Set(true); } // ループの最初に戻る continue; } else if (dataSize < 0 || dataSize > Consts.WideTunnelConsts.MaxBlockSize) { // 不正なデータサイズを受信。通信エラーか throw new CoresLibException($"dataSize < 0 || dataSize > Consts.WideTunnelConsts.MaxBlockSize ({dataSize})"); } // データ本体を受信 var data = await LowerStream.ReceiveAllAsync(dataSize, cancel); // データが 1 バイト以上あるか (0 バイトの場合は Keep Alive であるので無視する) if (data.IsEmpty == false) { // 上位レイヤに渡す await UpperStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); UpperStream.FastSendNonBlock(data, false); } } } catch (Exception ex) { // ex._Debug(); this.UpperStream.Disconnect(); await this.CleanupAsync(ex); } }
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(); this.Cancel(ex); } }
async Task SendLoopAsync() { try { CancellationToken cancel = this.GrandCancel; LocalTimer timer = new LocalTimer(); long nextPingTick = timer.AddTimeout(0); while (true) { await LowerStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); await UpperStream.WaitReadyToReceiveAsync(cancel, timer.GetNextInterval(), noTimeoutException : true, cancelEvent : this.SendPongEvent); MemoryBuffer <byte> sendBuffer = new MemoryBuffer <byte>(); IReadOnlyList <ReadOnlyMemory <byte> > userDataList = UpperStream.FastReceiveNonBlock(out int totalRecvSize, maxSize: Options.MaxBufferSize); if (totalRecvSize >= 1) { // Send data if (Options.RespectMessageDelimiter == false) { userDataList = Util.DefragmentMemoryArrays(userDataList, Options.SendSingleFragmentSize); } foreach (ReadOnlyMemory <byte> userData in userDataList) { if (userData.Length >= 1) { BuildAndAppendFrame(sendBuffer, true, WebSocketOpcode.Bin, userData); } } } if (timer.Now >= nextPingTick) { // Send ping nextPingTick = timer.AddTimeout(Util.GenRandInterval(Options.SendPingInterval)); BuildAndAppendFrame(sendBuffer, true, WebSocketOpcode.Ping, "[WebSocketPing]"._GetBytes_Ascii()); } lock (this.PongQueueLock) { // Send pong while (true) { if (this.PongQueue.TryDequeue(out ReadOnlyMemory <byte> data) == false) { break; } BuildAndAppendFrame(sendBuffer, true, WebSocketOpcode.Pong, data); } } if (sendBuffer.Length >= 1) { LowerStream.FastSendNonBlock(sendBuffer.Memory); } } } catch (Exception ex) { this.UpperStream.Disconnect(); this.Cancel(ex); } }
async Task SendLoopAsync(int keepaliveInterval) { try { CancellationToken cancel = this.GrandCancel; LocalTimer timer = new LocalTimer(); long nextPingTick = timer.AddTimeout(0); bool initialFourZeroSent = false; bool localFlag_C2S_SwitchToWebSocket_Requested = false; while (true) { MemoryBuffer <byte> sendBuffer = new MemoryBuffer <byte>(); if (initialFourZeroSent == false) { // 最初の 0x00000000 (4 バイト) を送信 initialFourZeroSent = true; sendBuffer.WriteSInt32(4); sendBuffer.WriteSInt32(0x00000000); } // 上位ストリームからのデータを送信 IReadOnlyList <ReadOnlyMemory <byte> > userDataList = UpperStream.FastReceiveNonBlock(out int totalSendSize, maxSize: Consts.WideTunnelConsts.MaxBlockSize); if (totalSendSize >= 1) { // Send data foreach (var mem in userDataList) { //$"Send: {mem.Length}"._Debug(); //$"SendData: {mem._GetHexString()}"._Debug(); sendBuffer.WriteSInt32(mem.Length); sendBuffer.Write(mem); } } if (timer.Now >= nextPingTick) { // Send ping sendBuffer.WriteSInt32(0); nextPingTick = timer.AddTimeout(Util.GenRandInterval(keepaliveInterval)); } if (localFlag_C2S_SwitchToWebSocket_Requested == false && this.C2S_SwitchToWebSocket_Requested) { // Web socket switch request invoked (only once) $"Web socket switch request invoked"._Debug(); localFlag_C2S_SwitchToWebSocket_Requested = true; sendBuffer.WriteSInt32(Consts.WideTunnelConsts.SpecialOpCode_C2S_SwitchToWebSocket_Request_Guacd); } if (sendBuffer.IsThisEmpty() == false) { //$"RawSendData: {sendBuffer.Span._GetHexString()}"._Debug(); LowerStream.FastSendNonBlock(sendBuffer); } await LowerStream.WaitReadyToSendAsync(cancel, Timeout.Infinite); await UpperStream.WaitReadyToReceiveAsync(cancel, timer.GetNextInterval(), noTimeoutException : true, cancelEvent : C2S_SpecialOp_Event); } } catch (Exception ex) { // ex._Debug(); this.UpperStream.Disconnect(); await this.CleanupAsync(ex); } }