Skip to content

构建测试服务器

XQUIC 作为传输层的中间件,引入了 QUIC 功能到套接字应用中,并可选地支持 HTTP(或 HTTP/3)功能。

为了帮助你快速熟悉如何在套接字应用中利用 XQUIC,我们提供了一个带有 XQUIC 接口和回调操作的最小客户端和服务器。

请注意,mini_client 和 mini_server 只展示了部分 XQUIC 接口,仅适用于学习和测试目的。

mini_server 基本上包括以下几个模块:

  • Engine: Engine是 XQUIC 主进程的核心模块,必须在第一阶段进行初始化。

  • Connection: Connection模块是Engine的一个子模块,负责建立和管理Connection。

  • Request: Request模块是Connection的一个子模块,负责数据传输。

Engine

Engine管理 XQUIC 的核心逻辑,包括Connection管理、通用回调管理、入站和出站数据包处理等。为了确保数据包处理的正确性,客户端和服务器都需要用Engine进行初始化。

下面是构建服务器端 XQUIC Engine的基本步骤:

创建Engine

在构建 XQUIC Engine之前,确保使用以下配置进行初始化:

  • xqc_engine_type_t engine_type: Engine类型,0 表示服务器,1 表示客户端

  • xqc_config_t * engine_config: Engine配置

默认配置可通过 xqc_engine_get_default_config 获取:

c
/* Psudo */ 
xqc_engine_get_default_config(&engine_config, engine_type);
  • xqc_engine_ssl_config_t * ssl_config: SSL 配置

mini_server 使用 xqc_mini_svr_init_engine_ssl_config 初始化 SSL 配置,定义如下参数:

c
/* Psudo */
ssl_cfg->private_key_file = args->env_cfg.private_key_file; //"server.key"
ssl_cfg->cert_file = args->env_cfg.cert_file;   // "server.crt"
ssl_config->ciphers = args->quic_cfg.ciphers;   // "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256"
ssl_config->groups = args->quic_cfg.groups;     // "P-256:X25519:P-384:P-521"
  • xqc_engine_callback_t * engine_callbacks 和 xqc_transport_callbacks_t * transport_callbacks: Engine基础回调和传输回调。

mini_server 使用 xqc_mini_svr_init_callback 初始化Engine回调和传输回调,自定义回调函数如下:

c
/* Psudo */ 
static xqc_engine_callback_t callback = {
    .set_event_timer = xqc_mini_svr_set_event_timer,
    .log_callbacks = {
        .xqc_log_write_err = xqc_mini_svr_write_log_file,
        .xqc_log_write_stat = xqc_mini_svr_write_log_file,
        .xqc_qlog_event_write = xqc_mini_svr_write_qlog_file
    },
    .keylog_cb = xqc_mini_svr_keylog_cb,
};

static xqc_transport_callbacks_t transport_cbs = {
    .server_accept = xqc_mini_svr_accept,
    .write_socket = xqc_mini_svr_write_socket,
    .write_socket_ex = xqc_mini_svr_write_socket_ex,
    .conn_update_cid_notify = xqc_mini_svr_conn_update_cid_notify,
};
  • void * user_data: 应用上下文,将在回调函数中传递以便于使用。

之后,可以使用 xqc_engine_create 创建Engine:

c
/* Psudo */ 
xqc_engine_t *engine = xqc_engine_create(0, &engine_config, &ssl_config,
                                         &callback, &transport_cbs, user_data);

初始化Engine上下文

Engine初始化完成后,还需要完成一些上下文设置:

初始化Connection设置

服务器通常接受客户端的Request,并被动地与客户端建立Connection。因此,在接受客户端Request之前应设置Connection设置:

c
/* Psudo */
xqc_conn_settings_t conn_settings = {
    .cong_ctrl_callback = ccc,
    .cc_params = {
        .customize_on = 1,
        .init_cwnd = 32,
        .bbr_enable_lt_bw = 1,
    },
    .spurious_loss_detect_on = 1,
    .init_idle_time_out = 60000,
    .enable_multipath = args->quic_cfg.multipath,
    .scheduler_callback = sched,
    .standby_path_probe_timeout = 1000,
    .adaptive_ack_frequency = 1,
    .anti_amplification_limit = 4,
};

/* set customized connection settings to engine ctx */
xqc_server_set_conn_settings(engine, &conn_settings);

注册应用程序回调

详情参见mini_client 部分

Connection

基于Connection模块,XQUIC 支持在 UDP 传输层之上实现可靠传输。

不同于 TCP,QUIC Connection更像是套接字状态管理的一种抽象形式,并且基于套接字传输进行协商和维护。

初始化套接字和套接字回调

服务器通常接受客户端的Request,并被动地与客户端建立Connection。

为了让服务器能够接收和处理客户端发来的数据包,需要创建并初始化套接字。所有套接字逻辑都在 xqc_mini_svr_create_user_conn 中实现。

mini_server 使用 xqc_mini_svr_create_socket 创建特定地址、端口及几个参数的套接字,并使用 xqc_mini_svr_socket_event_callback 注册套接字事件回调函数。

c
/* Psudo */
xqc_mini_svr_create_socket(user_conn, &args->net_cfg);

/* bind socket event callback to fd event */
user_conn->ev_socket = event_new(ctx->eb, user_conn->fd, EV_READ | EV_PERSIST,
                                 xqc_mini_svr_socket_event_callback, user_conn);
event_add(user_conn->ev_socket, NULL);

创建Connection(被动)

XQUIC 的主要功能体现在处理 READ 事件时,允许服务器使用 XQUIC 接口处理客户端的原始数据包,从而实现解析 QUIC Request和建立 QUIC Connection等功能。

c
/* Psudo: socket read handler, receive raw data and process it in xquic engine */
ssize_t recv_size = recvfrom(fd, packet_buf, sizeof(packet_buf), 0,
                             (struct sockaddr *) &peer_addr, &peer_addrlen);

/* process quic packet with xquic engine */
xqc_engine_packet_process(engine, packet_buf, recv_size,
                          (struct sockaddr *)(user_conn->local_addr), user_conn->local_addrlen,
                          (struct sockaddr *)(user_conn->peer_addr), user_conn->peer_addrlen,
                          (xqc_usec_t)recv_time, user_conn);

xqc_engine_finish_recv(engine);

Request

服务器使用已注册的应用程序回调函数处理和响应Request,详见注册应用程序回调

mini_server 通过以下步骤启用Request的发送和接收:

接收Request

mini_server 使用 xqc_mini_svr_h3_request_read_notify 接口定制处理已被 XQUIC 主逻辑解析的客户端Request数据。

c
/* Psudo */
// Step 1: send headers
xqc_http_headers_t *headers = xqc_h3_request_recv_headers(h3_request, &fin);

// Step 2: send body
ssize_t read = xqc_h3_request_recv_body(h3_request, recv_buff, recv_buff_size, &fin);

发送Request

当服务器触发写通知时,mini_server 使用 xqc_mini_svr_h3_request_write_notify 处理发送Request。

在接受到来自客户端的Request后,mini_server 使用 xqc_mini_cli_handle_h3_request 向客户端发送响应。

c
/* Psudo */
// Step 1: send headers
xqc_h3_request_send_headers(h3_request, &headers, 0);

// Step 2: send body
xqc_h3_request_send_body(user_stream->h3_request, send_buf, send_buf_size, fin);