构建测试服务器
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
获取:
/* 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 配置,定义如下参数:
/* 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回调和传输回调,自定义回调函数如下:
/* 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:
/* 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设置:
/* 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
注册套接字事件回调函数。
/* 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等功能。
/* 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数据。
/* 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
向客户端发送响应。
/* 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);