예제 #1
0
    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);
        }
    }
예제 #2
0
        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);
            }
        }
예제 #3
0
        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);
            }
        }
예제 #4
0
    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);
        }
    }