针对 QUIC 客户端开发对虚幻引擎与 Quiche 库集成进行故障排除

问题描述 投票:0回答:1

我正在就一个涉及虚幻引擎和 Quiche 库的项目寻求帮助。

背景: 我一直致力于将 Quiche 库集成到虚幻引擎中,以开发一个可以连接到 Quic 服务器的客户端。到目前为止,我已经成功地将 Quiche 库集成到虚幻引擎中,并在虚幻引擎中编写了一个简单的 UDP 客户端,以使用虚幻的套接字功能发送和接收消息。此外,我还使用 Quiche 库开发了一个 C 客户端来了解其工作原理,并且这两个客户端都运行良好。

问题: 然而,当我尝试使用 Quiche 库在虚幻引擎中编写客户端时,我在握手过程中遇到了问题。我可以看到客户端向 Quic 服务器发送初始数据包,但之后我无法取得任何进展。建立握手似乎存在障碍。我是虚幻引擎和乳蛋饼库的新手,我已经为此苦苦挣扎了一段时间。

代码:

头文件:

#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
#include <Interfaces/IPv4/IPv4Endpoint.h>
#include <Networking/Public/Common/UdpSocketBuilder.h>
#include <Interfaces/IPv4/IPv4Address.h>
#include <Sockets/Public/IPAddress.h>
#include <SocketSubsystem.h>
#include <Sockets.h>

#include "quiche_api.h"

class RDNCPPTEST_API FRdnCppTestModule : public IModuleInterface
{
public:

    int init();
    int send();
    int recv(quiche_recv_info* recvInfo);
    void driver();

private:

    FSocket* socket;
    ISocketSubsystem* SocketSubsystem;
    FIPv4Address ip;

    quiche_config* config;
    quiche_conn* conn;

    struct sockaddr localAddr;
    struct sockaddr peerAddr;

    const char* host = "172.27.224.121"; // Hard code the IP for now
};

Cpp文件:

#include "RdnCppTest.h"

#define LOCTEXT_NAMESPACE "FRdnCppTestModule"

#define MAX_DATAGRAM_SIZE 1500
#define LOCAL_CONN_ID_LEN 16


int FRdnCppTestModule::init()
{
    /*UNREAL*/
    SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM);
    socket = SocketSubsystem->CreateSocket(NAME_DGram, "mySocket", false);

    if (!socket)
    {
        UE_LOG(LogTemp, Log, TEXT("Error creating socket."));
        return -1;
    }
    UE_LOG(LogTemp, Log, TEXT("Socket successfully created."));

    socket->SetNonBlocking();

    TSharedPtr <FInternetAddr> serveraddr = SocketSubsystem->CreateInternetAddr();
    FIPv4Address::Parse("172.27.224.121", ip);
    serveraddr->SetIp(ip.Value);
    serveraddr->SetPlatformPort(8080);

    bool connected = socket->Connect(*serveraddr);

    if (!connected)
    {
        UE_LOG(LogTemp, Log, TEXT("Error in socket connection"));
        return -1;
    }
    UE_LOG(LogTemp, Log, TEXT("Socket successfully connected to server IP"));

    /* QUICHE */
    quiche_enable_debug_logging([](const char* line, void* argp) {
        UE_LOG(LogTemp, Log, TEXT("Quiche: %s"), UTF8_TO_TCHAR(line));
        },
        nullptr
    );

    config = quiche_config_new(0xbabababa);
    if (config == NULL)
    {
        UE_LOG(LogTemp, Log, TEXT("Failed to create config."));
        return -1;
    }
    UE_LOG(LogTemp, Log, TEXT("Config created."));

    quiche_config_set_application_protos(config,
        (uint8_t*)"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9", 38);
    quiche_config_set_max_idle_timeout(config, 5000);
    quiche_config_set_max_recv_udp_payload_size(config, MAX_DATAGRAM_SIZE);
    quiche_config_set_max_send_udp_payload_size(config, MAX_DATAGRAM_SIZE);
    quiche_config_set_initial_max_data(config, 10000000);
    quiche_config_set_initial_max_stream_data_bidi_local(config, 1000000);
    quiche_config_set_initial_max_stream_data_bidi_remote(config, 1000000);
    quiche_config_set_initial_max_streams_bidi(config, 100);
    quiche_config_set_initial_max_streams_uni(config, 100);
    quiche_config_set_disable_active_migration(config, true);

    uint8_t scid[QUICHE_MAX_CONN_ID_LEN] = {};
    for (size_t i = 0; i < QUICHE_MAX_CONN_ID_LEN; ++i)
        scid[i] = static_cast<unsigned char>(i);
    size_t scid_len = sizeof(scid);

    memset(&localAddr, 0, sizeof(localAddr));
    struct sockaddr_in* localIPv4Addr = (struct sockaddr_in*)&localAddr;
    localIPv4Addr->sin_family = AF_INET;
    localIPv4Addr->sin_addr.s_addr = inet_addr("127.0.0.1");
    unsigned short localPort = socket->GetPortNo();
    localIPv4Addr->sin_port = htons(8080);
    size_t localIPv4Len = sizeof(localIPv4Addr);

    memset(&peerAddr, 0, sizeof(peerAddr));
    struct sockaddr_in* peerIPv4Addr = (struct sockaddr_in*)&peerAddr;
    peerIPv4Addr->sin_family = AF_INET;
    peerIPv4Addr->sin_addr.s_addr = inet_addr(host);
    peerIPv4Addr->sin_port = htons(8080);
    size_t peerIPv4Len = sizeof(peerIPv4Addr);

    conn = quiche_connect(host, (const uint8_t*)scid, scid_len,
        &localAddr, sizeof(localAddr),
        &peerAddr, sizeof(peerAddr),
        config);

    if (conn == nullptr)
    {
        UE_LOG(LogTemp, Log, TEXT("Quiche Connect Error"));
        return -1;
    }
    UE_LOG(LogTemp, Log, TEXT("Quiche Connect Successful"));
    return 0;
}

int FRdnCppTestModule::send()
{
    int err = 0;
    int32 bytesSent = 0;
    static uint8_t send_buffer[1350];
    quiche_send_info send_info;

    while (true)
    {
        /*QUICHE PACKET WRITE*/
        ssize_t quichePacketWrite = quiche_conn_send(conn, send_buffer, sizeof(send_buffer), &send_info);
        if (quichePacketWrite == QUICHE_ERR_DONE)
        {
            UE_LOG(LogTemp, Log, TEXT("Nothing to send from quiche."));
            break;
        }
        if (quichePacketWrite < 0)
        {
            UE_LOG(LogTemp, Log, TEXT("Failed to create packet from quiche."));
            err = -1;
            break;
        }
        UE_LOG(LogTemp, Log, TEXT("Packet write from quiche successful. Bytes written: %zd"), quichePacketWrite);

        /*UNREAL SOCKET*/
        bool sent = socket->Send(send_buffer, sizeof(send_buffer), bytesSent);
        if (!sent)
        {
            UE_LOG(LogTemp, Log, TEXT("Failed to send packet over the socket."));
            err = -1;
            break;
        }
        UE_LOG(LogTemp, Log, TEXT("Bytes sent over the socket: %d"), bytesSent);
    }

    return err;
}

int FRdnCppTestModule::recv(quiche_recv_info* recvInfo)
{
    int err = 0;
    int32 bytesRead = 0;
    static uint8_t recvBuffer[65535];

    while (true)
    {
        err = 0;
        bool received = socket->Recv(recvBuffer, sizeof(recvBuffer), bytesRead, ESocketReceiveFlags::None);
        if (!received)
        {
            UE_LOG(LogTemp, Log, TEXT("Read error from socket. Error Value: %d"), bytesRead);
            err = -1;
            break;
        }
        if (!bytesRead)
        {
            UE_LOG(LogTemp, Log, TEXT("Nothing to read from the socket"));
            break;
        }
        UE_LOG(LogTemp, Log, TEXT("Successfully read from the socket. Bytes read: %d"), bytesRead);

        ssize_t quichePacketRead = quiche_conn_recv(conn, recvBuffer, sizeof(recvBuffer), recvInfo);
        if (quichePacketRead < 0)
        {
            UE_LOG(LogTemp, Log, TEXT("Error processing packet in quiche. Error: %zd"), quichePacketRead);
            err = -1;
            break;;
        }
        UE_LOG(LogTemp, Log, TEXT("Packet successfully processed. Bytes processed: %zd"), quichePacketRead);
    }

    return err;
}

void FRdnCppTestModule::driver()
{
    int err = 0;
    bool protos_sent = false;

    /* Initializing Local Addr and Peer Addr */
    /*struct sockaddr localAddr;
    struct sockaddr peerAddr;
    socklen_t localAddrLength = sizeof(localAddr);
    memset(&localAddr, 0, localAddrLength);
    socklen_t peerAddrLength = sizeof(peerAddr);
    memset(&peerAddr, 0, peerAddrLength);*/

    if (init() < 0) // Socket construction and quiche initialization
    {
        UE_LOG(LogTemp, Log, TEXT("Error in the init function."));
        return;
    }

    quiche_recv_info recvInfo =
    {
            (struct sockaddr*)&peerAddr,
            sizeof(peerAddr),
            (struct sockaddr*)&localAddr,
            sizeof(localAddr),
    };

    /* Main Loop */
    while (true)
    {
        err = send();
        if (err)
        {
            UE_LOG(LogTemp, Log, TEXT("Error in the send function!"));
            break;
        }
        err = recv(&recvInfo);
        if (err)
        {
            UE_LOG(LogTemp, Log, TEXT("Error in the receive function!"));
            break;
        }
        if (quiche_conn_is_established(conn) && !protos_sent)
        {
            const uint8_t* app_proto;
            size_t app_proto_len;
            quiche_conn_application_proto(conn, &app_proto, &app_proto_len); // Returns the negotiated ALPN
            UE_LOG(LogTemp, Log, TEXT("Connection Established and ALPN negotiated."));
            const static uint8_t r[] = "GET /index.html\r\n";
            if (quiche_conn_stream_send(conn, 4, r, sizeof(r), true) < 0)
            {
                UE_LOG(LogTemp, Log, TEXT("Failed to send request."));
                break;
            }
            UE_LOG(LogTemp, Log, TEXT("Request sent after ALPN negototiation."));
            protos_sent = true;
        }
        if (quiche_conn_is_established(conn))
        {
            UE_LOG(LogTemp, Log, TEXT("Connection established successfully!"));
            // TODO: Send data over a stream
        }
        if (quiche_conn_is_closed(conn))
        {
            UE_LOG(LogTemp, Log, TEXT("Connection Closed."));
            break;
        }
    }

    quiche_conn_free(conn);
    quiche_config_free(config);
}

#undef LOCTEXT_NAMESPACE

IMPLEMENT_MODULE(FRdnCppTestModule, RdnCppTest)

虚幻客户端输出:

LogTemp: Socket successfully created.
LogTemp: Socket successfully connected to server IP
LogTemp: Config created.
LogTemp: Quiche Connect Successful
LogTemp: Quiche: quiche::tls: 000102030405060708090a0b0c0d0e0f10111213 write message lvl=Initial len=277
LogTemp: Quiche: quiche: 000102030405060708090a0b0c0d0e0f10111213 tx pkt Initial version=babababa dcid=f30a9f1d527d83397b803de36f65c9ac scid=000102030405060708090a0b0c0d0e0f10111213 len=281 pn=0 src:127.0.0.1:8080 dst:172.27.224.121:8080
LogTemp: Quiche: quiche: 000102030405060708090a0b0c0d0e0f10111213 tx frm CRYPTO off=0 len=277
LogTemp: Quiche: quiche::recovery: 000102030405060708090a0b0c0d0e0f10111213 timer=998.2987ms latest_rtt=0ns srtt=None min_rtt=0ns rttvar=166.5ms loss_time=[None, None, None] loss_probes=[0, 0, 0] cwnd=15000 ssthresh=18446744073709551615 bytes_in_flight=344 app_limited=true congestion_recovery_start_time=None Rate { delivered: 0, delivered_time: Instant { t: 563857.6140141s }, first_sent_time: Instant { t: 563857.6140141s }, end_of_app_limited: 1, last_sent_packet: 0, largest_acked: 0, rate_sample: RateSample { delivery_rate: 0, is_app_limited: false, interval: 0ns, delivered: 0, prior_delivere
d: 0, prior_time: None, send_elapsed: 0ns, ack_elapsed: 0ns, rtt: 0ns } } pacer=Pacer { enabled: true, capacity: 15000, used: 0, rate: 0, last_update: Instant { t: 563857.6140141s }, next_time: Instant { t: 563857.6140141s }, max_datagram_size: 1500, last_packet_size: None, iv: 0ns, max_pacing_rate: None } hystart=window_end=None last_round_min_rtt=18446744073709551615.999999999s current_round_min_rtt=18446744073709551615.999999999s css_baseline_min_rtt=18446744073709551615.999999999s rtt_sample_count=0 css_start_time=None css_round_count=0 cubic={ k=0 w_max=0 } 
LogTemp: Packet write from quiche successful. Bytes written: 1200
LogTemp: Bytes sent over the socket: 1350
LogTemp: Nothing to send from quiche.
LogTemp: Read error from socket. Error Value: 0
LogTemp: Error in the receive function!

服务器端输出:

172.27.224.1
version negotiation
sent 47 bytes
Trying again, recv would block

请注意,我使用的服务器是乳蛋饼存储库中的示例服务器: https://github.com/cloudflare/quiche/blob/master/quiche/examples/server.c

c++ sockets network-programming unreal-engine4 quic
1个回答
0
投票

您完全缺少进行握手的事件循环。如果可能的话,尝试运行和调试示例服务器和客户端程序。

© www.soinside.com 2019 - 2024. All rights reserved.