libwebsockets 库原理详解
本文档深入解析 libwebsockets 库的内部工作原理,基于源码分析和官方文档,详细阐述其架构设计、核心机制和实现细节。
目录
- 架构设计原理
- 核心数据结构
- 事件循环机制
- 角色系统(Roles)
- [WebSocket 协议处理](#WebSocket 协议处理)
- 网络层实现
- [Secure Streams 原理](#Secure Streams 原理)
- 状态机与生命周期
- 内存管理
- 性能优化策略
1. 架构设计原理
1.1 设计哲学
libwebsockets 的设计遵循几个核心原则:
1.1.1 事件驱动架构
libwebsockets 采用事件驱动、单线程、非阻塞的架构模式:
- 单线程操作:所有操作在单个事件循环线程中顺序执行,消除了复杂的锁机制
- 非阻塞 I/O:所有文件描述符设置为非阻塞模式,通过事件通知进行 I/O 操作
- 空闲时休眠:当没有事件时,事件循环进入休眠状态,CPU 使用率为 0%
- 响应式处理:事件到达时立即唤醒并处理,保持低延迟
这种设计使得 libwebsockets 能够:
- 高效处理大量并发连接(数万连接)
- 在单线程中顺序处理所有连接
- 避免线程上下文切换和锁竞争的开销
- 在嵌入式系统中节省资源
1.1.2 模块化设计
libwebsockets 采用高度模块化的设计:
libwebsockets
├── core/ # 核心功能(上下文、内存、日志等)
├── core-net/ # 网络核心(连接管理、事件循环集成)
├── roles/ # 协议角色(HTTP/1, HTTP/2, WebSocket, MQTT, Raw)
├── event-libs/ # 事件循环适配器(libuv, libevent, libev等)
├── tls/ # TLS 支持(OpenSSL, mbedTLS, SChannel)
├── secure-streams/ # Secure Streams API 实现
├── plat/ # 平台适配层
└── misc/ # 辅助功能(JSON解析、缓存等)
每个模块职责清晰,可以独立编译和配置。
1.1.3 角色系统(Role System)
libwebsockets 使用角色系统来抽象不同的协议:
- 每个连接(WSI)可以扮演不同的角色:HTTP/1、HTTP/2、WebSocket、MQTT、Raw Socket
- 角色通过
role_ops结构体定义,包含该角色的所有操作函数 - 连接可以在生命周期中切换角色(如 HTTP 升级到 WebSocket)
- 角色系统使得协议实现与核心网络层解耦
1.2 核心抽象层
1.2.1 Context(上下文)
lws_context 是 libwebsockets 的全局上下文,包含:
- 所有虚拟主机(vhost)的列表
- 事件循环配置和状态
- TLS 配置
- 线程池配置
- 全局选项和设置
Context 是单例的,整个应用程序通常只有一个 context。
1.2.2 VHost(虚拟主机)
lws_vhost 代表一个虚拟主机,可以:
- 绑定到特定的端口和协议
- 拥有独立的 TLS 配置
- 定义支持的协议列表
- 管理绑定到该 vhost 的所有连接
一个 context 可以有多个 vhost,每个 vhost 可以服务不同的域名或端口。
1.2.3 WSI(WebSocket Instance)
struct lws(通常称为 WSI)代表一个连接实例:
- 每个网络连接对应一个 WSI
- WSI 包含连接的所有状态信息
- WSI 可以扮演不同的角色(HTTP、WebSocket、MQTT等)
- WSI 有完整的生命周期管理
WSI 是 libwebsockets 中最核心的数据结构。
2. 核心数据结构
2.1 lws_context(上下文结构)
Context 是 libwebsockets 的全局管理器,主要包含:
c
struct lws_context {
// 虚拟主机列表
struct lws_vhost *vhost_list;
// 线程相关
struct lws_context_per_thread *pt;
int count_threads;
// 事件循环
const struct lws_event_loop_ops *event_loop_ops;
void *event_lib_handle;
// TLS 配置
struct lws_context_tls tls;
// 全局选项
uint64_t options;
// 系统状态管理
struct lws_state_manager mgr_system;
// ... 更多字段
};
关键特性:
- Context 在应用启动时创建,贯穿整个应用生命周期
- 管理所有线程的上下文(per-thread context)
- 提供全局配置和资源管理
2.2 lws(WebSocket Instance)
WSI 是连接的核心表示,结构非常庞大(约1700行),主要包含:
c
struct lws {
// 基础信息
struct lws_a a; // 上下文、vhost、协议等
lws_wsi_state_t wsistate; // 状态(角色+连接状态)
// 角色相关数据
const struct lws_role_ops *role_ops; // 角色操作函数表
struct _lws_websocket_related *ws; // WebSocket 特定数据
struct _lws_http_mode_related http; // HTTP 特定数据
struct _lws_mqtt_related *mqtt; // MQTT 特定数据
// 网络相关
lws_sockaddr46 sa46_local; // 本地地址
lws_sockaddr46 sa46_peer; // 对端地址
lws_sock_file_fd_type desc; // 文件描述符(socket或file)
// 缓冲区
struct lws_buflist *buflist; // 输入缓冲区
struct lws_buflist *buflist_out; // 输出缓冲区
// TLS
struct lws_lws_tls tls;
// 定时器
lws_sorted_usec_list_t sul_timeout;
lws_sorted_usec_list_t sul_connect_timeout;
// 链表节点(用于各种列表)
struct lws_dll2 dll_buflist;
struct lws_dll2 same_vh_protocol;
// 用户数据
void *user_space; // 用户自定义数据
// ... 更多字段(约1700行)
};
关键特性:
- WSI 状态由
wsistate字段编码,包含角色标志和连接状态 - 通过
role_ops指针实现多态,不同角色有不同的操作函数 - 使用链表管理各种 WSI 集合(如等待写入的、等待超时的等)
2.3 lws_wsi_state_t(状态编码)
WSI 的状态使用位字段编码:
c
// 状态编码格式:
// [31:16] 角色标志位
// [15:0] 连接状态
// 角色标志
#define LWSIFR_CLIENT (0x1000 << 16) // 客户端
#define LWSIFR_SERVER (0x2000 << 16) // 服务器端
// 连接状态(部分)
enum lwsi_state {
LRS_UNCONNECTED = 0,
LRS_WAITING_DNS = 1,
LRS_WAITING_CONNECT = 2,
LRS_WAITING_SSL = 4,
LRS_ESTABLISHED = 25,
LRS_FLUSHING_BEFORE_CLOSE = 30,
LRS_DEAD_SOCKET = 32,
// ...
};
这种编码方式使得:
- 可以快速判断 WSI 的角色(客户端/服务器)
- 可以快速判断连接状态
- 状态转换高效(只需位操作)
2.4 lws_role_ops(角色操作表)
每个协议角色都有一个操作函数表:
c
typedef union lws_rops {
lws_rops_handle_POLLIN_t handle_POLLIN;
lws_rops_handle_POLLOUT_t handle_POLLOUT;
lws_rops_write_role_protocol_t write_role_protocol;
lws_rops_close_role_t close_role;
lws_rops_destroy_role_t destroy_role;
// ... 20个操作函数
} lws_rops_t;
设计优势:
- 通过函数指针实现多态,不同角色有不同的实现
- 稀疏表优化:使用 nybble 数组索引,只存储实际存在的函数指针
- 角色可以只实现需要的操作,未实现的返回默认行为
3. 事件循环机制
3.1 事件循环原理
libwebsockets 的事件循环遵循标准的事件驱动模式:
┌─────────────────────────────────┐
│ 事件循环主循环 │
│ │
│ while (running) { │
│ 1. 计算下次唤醒时间 │
│ 2. 等待事件(poll/epoll) │
│ 3. 处理就绪的文件描述符 │
│ 4. 处理定时器事件 │
│ 5. 处理用户回调 │
│ } │
└─────────────────────────────────┘
3.2 事件循环集成
libwebsockets 支持多种事件循环后端:
3.2.1 内置事件循环
默认使用基于 poll() 的简单事件循环:
c
// lib/core-net/service.c
int
lws_service(struct lws_context *context, int timeout_ms)
{
// 1. 处理挂起的定时器
lws_service_do_ripe_rxflow(context);
// 2. 等待事件
n = poll(pt->fds, pt->fds_count, timeout_ms);
// 3. 处理就绪的文件描述符
for (n = 0; n < pt->fds_count; n++) {
if (pt->fds[n].revents & LWS_POLLIN)
lws_service_fd(context, &pt->fds[n]);
if (pt->fds[n].revents & LWS_POLLOUT)
lws_handle_POLLOUT_event(...);
}
// 4. 处理定时器
lws_service_do_ripe_rxflow(context);
}
3.2.2 外部事件循环集成
libwebsockets 可以集成到外部事件循环中:
libuv 集成:
c
// 创建 libuv loop
uv_loop_t *loop = uv_default_loop();
// 配置 lws 使用 libuv
info.event_loop_ops = &event_loop_ops_uv;
// lws 作为 guest 运行在 libuv loop 上
context = lws_create_context(&info);
libevent 集成:
c
// 创建 libevent base
struct event_base *base = event_base_new();
// 配置 lws 使用 libevent
info.event_loop_ops = &event_loop_ops_libevent;
// lws 注册到 libevent base
context = lws_create_context(&info);
3.3 事件处理流程
3.3.1 POLLIN 事件处理
当文件描述符可读时:
c
// lib/core-net/service.c
lws_handle_POLLIN_event(wsi, pollfd) {
// 1. 调用角色的 handle_POLLIN
hr = wsi->role_ops->handle_POLLIN(pt, wsi, pollfd);
// 2. 根据处理结果决定后续动作
switch (hr) {
case LWS_HP_RET_BAIL_OK:
// 正常处理完成
break;
case LWS_HP_RET_BAIL_DIE:
// 需要关闭连接
lws_wsi_close(wsi, ...);
break;
case LWS_HP_RET_USER_SERVICE:
// 需要用户回调处理
user_callback_handle_rxflow(...);
break;
}
}
3.3.2 POLLOUT 事件处理
当文件描述符可写时:
c
// lib/core-net/service.c
lws_handle_POLLOUT_event(wsi, pollfd) {
// 优先级 1: 处理待发送的缓冲数据
if (lws_has_buffered_out(wsi)) {
lws_issue_raw(wsi, NULL, 0);
return;
}
// 优先级 2: 处理压缩数据
if (wsi->http.comp_ctx.buflist_comp) {
lws_rops_func_fidx(...).write_role_protocol(...);
return;
}
// 优先级 3: 调用角色的 handle_POLLOUT
hr = wsi->role_ops->handle_POLLOUT(wsi);
// 优先级 4: 用户可写回调
if (wsi->role_ops->perform_user_POLLOUT)
wsi->role_ops->perform_user_POLLOUT(wsi);
}
3.4 定时器系统
libwebsockets 使用排序的微秒列表(Sorted Usec List)管理定时器:
c
// lib/core-net/sorted-usec-list.c
struct lws_sorted_usec_list {
lws_dll2_t list; // 链表节点
lws_usec_t us; // 触发时间(微秒)
sul_cb_t cb; // 回调函数
void *parent; // 父对象
};
特性:
- 按时间排序的链表,最早的事件在头部
- 事件循环计算到下一个定时器的时间,用于
poll()的超时 - 支持取消定时器
- 高精度(微秒级)
4. 角色系统(Roles)
4.1 角色概念
角色(Role)是 libwebsockets 的核心抽象,代表一个连接可以扮演的协议角色。
支持的角色:
- HTTP/1 (
LWS_ROLE_H1):HTTP/1.0 和 HTTP/1.1 - HTTP/2 (
LWS_ROLE_H2):HTTP/2 协议 - WebSocket (
LWS_ROLE_WS):WebSocket 协议 - MQTT (
LWS_ROLE_MQTT):MQTT 客户端 - Raw Socket (
LWS_ROLE_RAW_SKT):原始套接字 - Raw File (
LWS_ROLE_RAW_FILE):原始文件 - Listen (
LWS_ROLE_LISTEN):监听套接字
4.2 角色操作表(Role Ops)
每个角色通过 lws_role_ops 结构定义其行为:
c
// lib/roles/private-lib-roles.h
typedef union lws_rops {
// 事件处理
lws_rops_handle_POLLIN_t handle_POLLIN;
lws_rops_handle_POLLOUT_t handle_POLLOUT;
// 写入操作
lws_rops_write_role_protocol_t write_role_protocol;
lws_rops_perform_user_POLLOUT_t perform_user_POLLOUT;
// 连接管理
lws_rops_client_bind_t client_bind;
lws_rops_adoption_bind_t adoption_bind;
// 关闭操作
lws_rops_close_role_t close_role;
lws_rops_close_via_role_protocol_t close_via_role_protocol;
lws_rops_destroy_role_t destroy_role;
// 其他操作
lws_rops_check_upgrades_t check_upgrades;
lws_rops_tx_credit_t tx_credit;
// ... 共20个操作
} lws_rops_t;
4.3 WebSocket 角色实现
以 WebSocket 角色为例,展示角色如何工作:
c
// lib/roles/ws/ops-ws.c
static const lws_rops_t rops_table_ws[] = {
.handle_POLLIN = rops_handle_POLLIN_ws,
.handle_POLLOUT = rops_handle_POLLOUT_ws,
.write_role_protocol = rops_write_role_protocol_ws,
.close_via_role_protocol = rops_close_via_role_protocol_ws,
.destroy_role = rops_destroy_role_ws,
// ...
};
// WebSocket 的 POLLIN 处理
static lws_handling_result_t
rops_handle_POLLIN_ws(struct lws_context_per_thread *pt,
struct lws *wsi,
struct lws_pollfd *pollfd)
{
// 1. 读取数据
n = lws_ssl_capable_read(wsi, &buf[LWS_PRE], ...);
// 2. 解析 WebSocket 帧
ret = lws_ws_rx_sm(wsi, 0, c);
// 3. 处理不同类型的帧
switch (wsi->ws->opcode) {
case LWSWSOPC_TEXT_FRAME:
case LWSWSOPC_BINARY_FRAME:
// 调用用户回调
user_callback_handle_rxflow(wsi, LWS_CALLBACK_RECEIVE, ...);
break;
case LWSWSOPC_PING:
// 自动回复 PONG
lws_ws_respond_ping(wsi);
break;
case LWSWSOPC_CLOSE:
// 处理关闭帧
lws_ws_rx_sm_close(wsi);
break;
}
return LWS_HPI_RET_HANDLED;
}
4.4 角色切换
连接可以在生命周期中切换角色,典型的例子是 HTTP 升级到 WebSocket:
c
// lib/roles/http/server/server.c
// HTTP 服务器接收到升级请求
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) &&
!strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), "websocket")) {
// 1. 验证升级请求
if (lws_process_ws_upgrade(wsi) < 0)
return -1;
// 2. 切换角色
lwsi_set_role(wsi, LWSI_ROLE_WS);
wsi->role_ops = &role_ops_ws;
// 3. 分配 WebSocket 特定数据
wsi->ws = lws_zalloc(sizeof(*wsi->ws), ...);
// 4. 发送升级响应
lws_ws_handshake_response(wsi, ...);
// 5. 通知用户连接已建立
wsi->a.protocol->callback(wsi, LWS_CALLBACK_ESTABLISHED, ...);
}
5. WebSocket 协议处理
5.1 WebSocket 帧格式
WebSocket 帧格式(RFC 6455):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/63) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
5.2 WebSocket 帧解析状态机
libwebsockets 使用状态机解析 WebSocket 帧:
c
// lib/roles/ws/ops-ws.c
lws_handling_result_t
lws_ws_rx_sm(struct lws *wsi, char already_processed, unsigned char c)
{
switch (wsi->lws_rx_parse_state) {
case LWS_RXPS_NEW:
// 解析第一个字节:FIN, RSV, Opcode
wsi->ws->opcode = c & 0xf;
wsi->ws->final = !!((c >> 7) & 1);
wsi->lws_rx_parse_state = LWS_RXPS_04_FRAME_HDR_1;
break;
case LWS_RXPS_04_FRAME_HDR_1:
// 解析第二个字节:MASK, Payload Length
wsi->ws->mask = !!(c & 0x80);
wsi->ws->len = c & 0x7f;
if (wsi->ws->len == 126) {
// 扩展长度(16位)
wsi->lws_rx_parse_state = LWS_RXPS_04_PAYLOAD_LEN_EXT_1;
} else if (wsi->ws->len == 127) {
// 扩展长度(64位)
wsi->lws_rx_parse_state = LWS_RXPS_04_PAYLOAD_LEN_EXT_2;
} else {
// 直接长度
goto handle_mask_and_payload;
}
break;
case LWS_RXPS_04_PAYLOAD_LEN_EXT_1:
// 读取16位扩展长度
wsi->ws->len = (c << 8);
wsi->lws_rx_parse_state = LWS_RXPS_04_PAYLOAD_LEN_EXT_1_1;
break;
case LWS_RXPS_04_PAYLOAD_LEN_EXT_1_1:
wsi->ws->len |= c;
goto handle_mask_and_payload;
// ... 更多状态
handle_mask_and_payload:
if (wsi->ws->mask) {
// 客户端发送的帧需要解掩码
wsi->lws_rx_parse_state = LWS_RXPS_04_MASK_1;
} else {
// 服务器发送的帧不需要掩码
wsi->lws_rx_parse_state = LWS_RXPS_04_PAYLOAD;
}
break;
case LWS_RXPS_04_PAYLOAD:
// 处理负载数据
// 如果是文本帧,验证 UTF-8
if (wsi->ws->opcode == LWSWSOPC_TEXT_FRAME) {
if (!lws_check_utf8(&wsi->ws->utf8_state, c))
return LWS_HPI_RET_PLEASE_CLOSE_ME;
}
// 调用用户回调
if (wsi->ws->final || wsi->ws->len == 0) {
user_callback_handle_rxflow(wsi, LWS_CALLBACK_RECEIVE, ...);
}
break;
}
return LWS_HPI_RET_HANDLED;
}
5.3 WebSocket 握手处理
5.3.1 服务器端握手
c
// lib/roles/ws/server-ws.c
int
lws_server_ws_handshake(struct lws *wsi)
{
// 1. 验证必需的 HTTP 头
if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_KEY) ||
!lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
return -1;
// 2. 验证 Upgrade 头
if (strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_UPGRADE), "websocket"))
return -1;
// 3. 验证 WebSocket 版本
if (strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_VERSION), "13"))
return -1;
// 4. 计算 Accept 响应
key = lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY);
lws_SHA1(key, key_len, sha1);
lws_b64_encode_string(sha1, 20, accept, sizeof(accept));
// 5. 发送升级响应
lws_ws_handshake_response(wsi, accept, ...);
// 6. 切换角色
lwsi_set_role(wsi, LWSI_ROLE_WS);
wsi->role_ops = &role_ops_ws;
return 0;
}
5.3.2 客户端握手
c
// lib/roles/ws/client-ws.c
int
lws_client_ws_upgrade(struct lws *wsi)
{
// 1. 生成随机 Key
lws_get_random(context, key, 16);
lws_b64_encode_string(key, 16, key_b64, sizeof(key_b64));
// 2. 发送升级请求
lws_client_connect_2(wsi, ...);
// 3. 等待服务器响应
// 在 HTTP 响应处理中验证 Accept 头
// 4. 切换角色
lwsi_set_role(wsi, LWSI_ROLE_WS);
wsi->role_ops = &role_ops_ws;
return 0;
}
5.4 WebSocket 数据发送
c
// lib/roles/ws/ops-ws.c
int
lws_write(struct lws *wsi, const void *buf, size_t len,
enum lws_write_protocol wp)
{
unsigned char frame[LWS_PRE + 14]; // 最大帧头14字节
unsigned char *p = frame + LWS_PRE;
int n, m, mask_len = 0;
// 1. 构建帧头
*p++ = 0x80 | opcode; // FIN + Opcode
// 2. 编码负载长度
if (len < 126) {
*p++ = len | 0x80; // MASK bit
mask_len = 4;
} else if (len < 65536) {
*p++ = 126 | 0x80;
*p++ = (len >> 8) & 0xff;
*p++ = len & 0xff;
mask_len = 4;
} else {
*p++ = 127 | 0x80;
// 64位长度编码
// ...
mask_len = 4;
}
// 3. 生成掩码(客户端必须掩码)
if (lwsi_role_client(wsi)) {
lws_get_random(context, p, 4);
mask = p;
p += 4;
}
// 4. 掩码负载数据
if (mask) {
for (n = 0; n < len; n++)
((unsigned char *)buf)[n] ^= mask[n % 4];
}
// 5. 发送帧头
m = lws_issue_raw(wsi, frame + LWS_PRE, p - (frame + LWS_PRE));
// 6. 发送负载
if (m >= 0)
m = lws_issue_raw(wsi, buf, len);
return m;
}
5.5 帧分片处理
WebSocket 支持帧分片,libwebsockets 处理分片的逻辑:
c
// 发送分片帧
int
lws_write_fragment(struct lws *wsi, const void *buf, size_t len,
int is_first, int is_final)
{
unsigned char opcode;
if (is_first) {
// 第一帧:使用实际 opcode
opcode = (is_text ? LWSWSOPC_TEXT_FRAME : LWSWSOPC_BINARY_FRAME);
} else {
// 后续帧:使用 CONTINUATION opcode
opcode = LWSWSOPC_CONTINUATION;
}
// FIN 位:只有最后一帧设置
unsigned char fin = is_final ? 0x80 : 0x00;
// 发送帧
return lws_write(wsi, buf, len, fin | opcode);
}
// 接收分片帧
// 在 lws_ws_rx_sm 中:
if (wsi->ws->opcode == LWSWSOPC_CONTINUATION) {
// 这是分片的后续帧
// 累积数据直到 FIN 帧到达
if (wsi->ws->final) {
// 最后一帧,调用用户回调
user_callback_handle_rxflow(wsi, LWS_CALLBACK_RECEIVE, ...);
}
}
6. 网络层实现
6.1 连接建立流程
6.1.1 服务器端连接建立
c
// lib/core-net/vhost.c
// 1. 创建监听套接字
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, ...);
bind(listen_fd, &addr, sizeof(addr));
listen(listen_fd, backlog);
// 2. 注册到事件循环
lws_pollfd_add(context, listen_fd, LWS_POLLIN);
// 3. 接受新连接
// 在 POLLIN 事件中:
new_fd = accept(listen_fd, &addr, &addrlen);
// 4. 创建 WSI
wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_FILE_DESC,
new_fd, NULL, NULL);
// 5. 设置为非阻塞
lws_plat_set_socket_options(new_fd, 1, 1);
// 6. 注册到事件循环
lws_change_pollfd(wsi, 0, LWS_POLLIN);
6.1.2 客户端连接建立
c
// lib/core-net/client/connect.c
int
lws_client_connect_via_info(struct lws_client_connect_info *i)
{
// 1. 创建 WSI
wsi = lws_client_connect_2_create_wsi(i, ...);
// 2. 解析地址
lws_async_dns_getaddrinfo(wsi, host, port, ...);
// 3. 创建套接字
fd = socket(ai->ai_family, SOCK_STREAM, 0);
lws_plat_set_socket_options(fd, 1, 1);
// 4. 异步连接
n = connect(fd, ai->ai_addr, ai->ai_addrlen);
if (n < 0 && errno != EINPROGRESS)
return -1;
// 5. 设置状态
lwsi_set_state(wsi, LRS_WAITING_CONNECT);
// 6. 注册到事件循环
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
// 7. 在 POLLOUT 事件中完成连接
// lws_client_connect_3(wsi, ...);
}
6.2 非阻塞 I/O
所有 I/O 操作都是非阻塞的:
c
// lib/core-net/output.c
int
lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
{
int n;
// 1. 尝试发送
n = send(wsi->desc.sockfd, buf, len, MSG_NOSIGNAL);
if (n < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 2. 缓冲区满,加入输出缓冲区
lws_buflist_append_segment(&wsi->buflist_out, buf, len);
// 3. 请求 POLLOUT 事件
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
return 0; // 部分发送
}
return -1; // 错误
}
if (n < len) {
// 4. 部分发送,剩余数据加入缓冲区
lws_buflist_append_segment(&wsi->buflist_out, buf + n, len - n);
lws_change_pollfd(wsi, 0, LWS_POLLOUT);
}
return n;
}
6.3 缓冲区管理
libwebsockets 使用 lws_buflist 管理输入和输出缓冲区:
c
// lib/core/buflist.c
struct lws_buflist {
uint8_t *buf; // 数据缓冲区
size_t len; // 数据长度
size_t pos; // 已消费位置
struct lws_buflist *next; // 下一个缓冲区段
};
// 追加数据
int
lws_buflist_append_segment(struct lws_buflist **head,
const uint8_t *buf, size_t len)
{
struct lws_buflist *e = lws_malloc(sizeof(*e) + len, ...);
e->buf = (uint8_t *)(e + 1);
memcpy(e->buf, buf, len);
e->len = len;
e->pos = 0;
// 追加到链表尾部
lws_buflist_append(head, e);
}
// 消费数据
size_t
lws_buflist_linear_consume(struct lws_buflist **head,
uint8_t *buf, size_t len)
{
size_t total = 0;
while (*head && total < len) {
size_t to_copy = lws_min((*head)->len - (*head)->pos, len - total);
memcpy(buf + total, (*head)->buf + (*head)->pos, to_copy);
(*head)->pos += to_copy;
total += to_copy;
if ((*head)->pos >= (*head)->len) {
// 这个段已消费完,删除
struct lws_buflist *next = (*head)->next;
lws_free(*head);
*head = next;
}
}
return total;
}
6.4 连接池管理
客户端连接可以复用(连接池):
c
// lib/core-net/client/client.c
struct lws *
lws_client_connect_2(struct lws *wsi, ...)
{
// 1. 查找可复用的连接
wsi_reuse = lws_client_connect_find_existing(wsi, ...);
if (wsi_reuse) {
// 2. 复用现有连接
wsi->parent = wsi_reuse;
wsi->mux_substream = 1;
return wsi;
}
// 3. 创建新连接
return lws_client_connect_3(wsi, ...);
}
7. Secure Streams 原理
7.1 Secure Streams 设计理念
Secure Streams(SS)是 libwebsockets 的高级 API,设计目标:
- 分离关注点:连接策略与业务逻辑分离
- 配置驱动:通过 JSON 策略文件配置连接
- 协议无关:应用代码不依赖具体协议
- 自动重试:内置重试和错误恢复机制
- 简化安全:自动处理 TLS、认证等
7.2 Secure Streams 架构
┌─────────────────────────────────────────┐
│ 应用程序代码 │
│ (协议无关的业务逻辑) │
└──────────────┬──────────────────────────┘
│
│ lws_ss_* API
│
┌──────────────▼──────────────────────────┐
│ Secure Streams 层 │
│ ┌──────────────────────────────────────┐ │
│ │ 状态机管理 │ │
│ │ - 连接状态转换 │ │
│ │ - 重试逻辑 │ │
│ │ - 超时处理 │ │
│ └──────────────────────────────────────┘ │
│ ┌──────────────────────────────────────┐ │
│ │ 策略管理 │ │
│ │ - JSON 策略解析 │ │
│ │ - 端点配置 │ │
│ │ - 协议选择 │ │
│ └──────────────────────────────────────┘ │
└──────────────┬──────────────────────────┘
│
│ 创建 WSI + 角色绑定
│
┌──────────────▼──────────────────────────┐
│ 底层 WSI API │
│ (HTTP/1, HTTP/2, WebSocket, MQTT) │
└──────────────────────────────────────────┘
7.3 Secure Streams 状态机
Secure Streams 使用状态机管理连接生命周期:
c
// lib/secure-streams/secure-streams.c
enum lws_ss_state {
LWSSSCS_CREATING = 1,
LWSSSCS_DISCONNECTED,
LWSSSCS_UNREACHABLE,
LWSSSCS_AUTH_FAILED,
LWSSSCS_CONNECTED,
LWSSSCS_CONNECTING,
LWSSSCS_DESTROYING,
LWSSSCS_POLL,
LWSSSCS_ALL_RETRIES_FAILED,
// ...
};
// 状态转换有效性检查
const uint32_t ss_state_txn_validity[] = {
[LWSSSCS_CREATING] = (1 << LWSSSCS_CONNECTING) |
(1 << LWSSSCS_TIMEOUT) |
(1 << LWSSSCS_DESTROYING),
[LWSSSCS_CONNECTING] = (1 << LWSSSCS_UNREACHABLE) |
(1 << LWSSSCS_CONNECTED) |
(1 << LWSSSCS_TIMEOUT) |
(1 << LWSSSCS_DESTROYING),
[LWSSSCS_CONNECTED] = (1 << LWSSSCS_DISCONNECTED) |
(1 << LWSSSCS_TIMEOUT) |
(1 << LWSSSCS_DESTROYING),
// ...
};
状态转换流程:
CREATING → CONNECTING → CONNECTED
↓ ↓ ↓
POLL UNREACHABLE DISCONNECTED
↓ ↓ ↓
DESTROYING ← TIMEOUT ← ALL_RETRIES_FAILED
7.4 Secure Streams 策略配置
策略通过 JSON 文件定义:
json
{
"retry": [
{"default": {"backoff": [1000, 2000, 3000, 5000]}}
],
"streams": [
{
"streamtype": "myapi",
"endpoint": "wss://api.example.com",
"protocol": "ws",
"retry": "default",
"tls": true,
"metadata": [
{"Authorization": "${AUTH_TOKEN}"}
]
}
]
}
策略解析和加载:
c
// lib/secure-streams/policy-json.c
int
lws_ss_policy_parse(struct lws_context *context, const char *json)
{
// 1. 解析 JSON
lejp_ctx jctx;
lejp_construct(&jctx, ss_policy_parse_cb, context, ...);
lejp_parse(&jctx, json, strlen(json));
// 2. 构建策略结构
struct lws_ss_policy *policy = lws_malloc(...);
// 3. 注册到 context
lws_ss_policy_register(context, policy);
}
7.5 Secure Streams 使用流程
c
// 1. 定义用户对象
struct my_ss {
lws_ss_handle_t *ss;
// 用户数据
};
// 2. 定义回调
static int
my_ss_cb(void *userobj, void *h, int state, void *in, size_t len)
{
struct my_ss *m = (struct my_ss *)userobj;
switch (state) {
case LWSSSCS_CREATING:
// 创建中
break;
case LWSSSCS_CONNECTED:
// 已连接,可以发送数据
lws_ss_request_write(m->ss);
break;
case LWSSSCS_RX:
// 接收到数据
process_data(in, len);
break;
case LWSSSCS_DISCONNECTED:
// 断开连接(会自动重试)
break;
}
return 0;
}
// 3. 创建 Secure Stream
struct lws_ss_info ssi = {
.streamtype = "myapi",
.user_alloc = sizeof(struct my_ss),
.user_cb = my_ss_cb,
};
lws_ss_handle_t *ss;
lws_ss_create(context, 0, &ssi, NULL, NULL, &ss, NULL);
// 4. 发送数据
lws_ss_request_write(ss);
// 在 LWSSSCS_WRITEABLE 回调中:
lws_ss_request_write(ss);
lws_ss_client_write(ss, buf, len);
// 5. 销毁
lws_ss_destroy(&ss);
7.6 Secure Streams 代理模式
Secure Streams 支持代理模式,允许通过串口等非网络连接使用:
┌─────────────┐ 串口/UART ┌─────────────┐ 网络 ┌─────────────┐
│ 客户端设备 │ ←──────────────→ │ SS Proxy │ ←──────────→ │ 远程服务器 │
│ (无网络) │ │ (有网络) │ │ │
└─────────────┘ └─────────────┘ └─────────────┘
实现原理:
- 客户端将 SS 操作序列化为消息
- 通过串口发送到代理
- 代理反序列化并执行操作
- 代理将结果序列化返回客户端
8. 状态机与生命周期
8.1 WSI 生命周期
WSI 的完整生命周期:
┌──────────────┐
│ UNCONNECTED │ ← 初始状态
└──────┬───────┘
│
▼
┌──────────────┐
│ WAITING_DNS │ ← 客户端:等待 DNS 解析
└──────┬───────┘
│
▼
┌──────────────┐
│WAITING_CONNECT│ ← 等待 TCP 连接建立
└──────┬───────┘
│
▼
┌──────────────┐
│ WAITING_SSL │ ← 等待 TLS 握手(如果使用 TLS)
└──────┬───────┘
│
▼
┌──────────────┐
│ ESTABLISHED │ ← 连接已建立,可以通信
└──────┬───────┘
│
│ (正常通信)
│
▼
┌──────────────┐
│FLUSHING_BEFORE│ ← 关闭前刷新缓冲区
│ _CLOSE │
└──────┬───────┘
│
▼
┌──────────────┐
│ DEAD_SOCKET │ ← 连接已关闭
└──────────────┘
8.2 状态转换管理
状态转换通过 lwsi_set_state() 进行:
c
// lib/core-net/wsi.c
void
lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs)
{
lws_wsi_state_t old = wsi->wsistate;
// 更新状态(保留角色标志位)
wsi->wsistate = (old & (~LRS_MASK)) | lrs;
// 调试日志
lwsl_wsi_debug(wsi, "lwsi_set_state 0x%lx -> 0x%lx",
(unsigned long)old, (unsigned long)wsi->wsistate);
// 状态相关的副作用处理
if (lwsi_state_est(wsi) && !lwsi_state_est_PRE_CLOSE(wsi)) {
// 从非建立状态进入建立状态
// 触发相关回调
}
}
8.3 Context 生命周期
c
// 1. 创建 Context
struct lws_context *context = lws_create_context(&info);
// 2. 创建 VHost
struct lws_vhost *vhost = lws_create_vhost(context, &vhost_info);
// 3. 事件循环
while (running) {
lws_service(context, 50); // 50ms 超时
}
// 4. 销毁
lws_context_destroy(context);
8.4 连接关闭流程
c
// lib/core-net/close.c
int
lws_wsi_close(struct lws *wsi, enum lws_close_status reason)
{
// 1. 设置关闭状态
lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
// 2. 刷新输出缓冲区
if (lws_has_buffered_out(wsi)) {
lws_issue_raw(wsi, NULL, 0);
// 等待 POLLOUT 完成刷新
return 0;
}
// 3. 发送关闭帧(WebSocket)
if (wsi->role_ops->close_via_role_protocol) {
wsi->role_ops->close_via_role_protocol(wsi, reason);
}
// 4. 关闭套接字
lws_plat_file_close(wsi->desc.sockfd);
// 5. 清理资源
if (wsi->role_ops->destroy_role)
wsi->role_ops->destroy_role(wsi);
// 6. 从事件循环移除
lws_pollfd_remove(wsi);
// 7. 释放 WSI
__lws_free_wsi(wsi);
return 0;
}
9. 内存管理
9.1 内存分配策略
libwebsockets 使用多种内存分配策略:
9.1.1 标准 malloc/free
用于大块、长期存在的对象:
c
// Context, VHost, WSI 等
struct lws_context *context = lws_malloc(sizeof(*context), ...);
9.1.2 LWSAC(分块分配器)
用于频繁分配的小对象:
c
// lib/misc/lwsac/lws-lwsac.h
struct lwsac {
uint8_t *alloc; // 当前块
size_t alloc_size; // 块大小
size_t ofs; // 当前偏移
struct lwsac *next; // 下一个块
};
// 分配
void *
lwsac_use(struct lwsac **head, size_t len, size_t chunk_size)
{
// 如果当前块空间不足,分配新块
if ((*head)->ofs + len > (*head)->alloc_size) {
struct lwsac *new = lws_malloc(...);
new->next = *head;
*head = new;
}
void *p = (*head)->alloc + (*head)->ofs;
(*head)->ofs += len;
return p;
}
// 一次性释放所有块
void
lwsac_free(struct lwsac **head)
{
while (*head) {
struct lwsac *next = (*head)->next;
lws_free(*head);
*head = next;
}
}
优势:
- 减少 malloc 调用次数
- 提高分配效率
- 适合临时对象(如解析 HTTP 头)
9.1.3 栈分配
用于小的临时缓冲区:
c
char buf[256];
lws_snprintf(buf, sizeof(buf), ...);
9.2 缓冲区管理
9.2.1 输入缓冲区(buflist)
c
// lib/core/buflist.c
// 当接收数据时,如果用户回调未及时处理,数据暂存在 buflist
struct lws_buflist *wsi->buflist;
// 追加数据
lws_buflist_append_segment(&wsi->buflist, buf, len);
// 消费数据
size_t consumed = lws_buflist_linear_consume(&wsi->buflist, user_buf, user_len);
9.2.2 输出缓冲区(buflist_out)
c
// 当发送缓冲区满时,数据暂存在 buflist_out
struct lws_buflist *wsi->buflist_out;
// 在 POLLOUT 事件中发送
if (lws_has_buffered_out(wsi)) {
lws_issue_raw(wsi, NULL, 0); // 发送 buflist_out 中的数据
}
9.3 内存优化选项
libwebsockets 提供多种内存优化选项:
c
// CMake 选项
-DLWS_WITH_MINIMAL_EXAMPLES=OFF // 禁用示例
-DLWS_WITHOUT_EXTENSIONS=ON // 禁用扩展
-DLWS_WITHOUT_SERVER=ON // 仅客户端
-DLWS_WITHOUT_CLIENT=ON // 仅服务器
-DLWS_WITH_HTTP2=OFF // 禁用 HTTP/2
-DLWS_WITH_MQTT=OFF // 禁用 MQTT
10. 性能优化策略
10.1 零拷贝优化
libwebsockets 尽可能避免数据拷贝:
c
// 直接使用用户缓冲区
int
lws_write(struct lws *wsi, const void *buf, size_t len, ...)
{
// 不拷贝数据,直接发送
// 如果发送失败,才加入 buflist_out
}
10.2 预分配缓冲区
c
// LWS_PRE 前缀空间
#define LWS_PRE 4096
// 用户分配缓冲区时预留 LWS_PRE 空间
char buf[LWS_PRE + user_data_len];
// libwebsockets 可以使用 LWS_PRE 空间添加协议头
lws_write(wsi, buf + LWS_PRE, user_data_len, ...);
10.3 事件循环优化
10.3.1 批量处理
c
// 一次事件循环处理多个事件
for (n = 0; n < pt->fds_count; n++) {
if (pt->fds[n].revents)
lws_service_fd(context, &pt->fds[n]);
}
10.3.2 精确超时
c
// 计算到下一个定时器的时间
lws_usec_t next = lws_sul_check(&pt->pt_sul_owner, lws_now_usecs());
// 使用精确超时,避免不必要的唤醒
timeout_ms = lws_usec_to_ms(next - now);
poll(pt->fds, pt->fds_count, timeout_ms);
10.4 连接复用
10.4.1 HTTP/2 多路复用
c
// HTTP/2 可以在一个连接上多路复用多个流
// 每个流是一个独立的 WSI,共享父连接
wsi->mux.parent_wsi = parent;
wsi->mux_substream = 1;
10.4.2 客户端连接池
c
// 复用现有连接而不是创建新连接
wsi_reuse = lws_client_connect_find_existing(wsi, ...);
10.5 编译优化
c
// CMake 选项
-DCMAKE_BUILD_TYPE=Release // 发布模式
-DLWS_WITH_STATIC=ON // 静态链接
-DLWS_WITH_MINIMAL_EXAMPLES=OFF // 减少代码大小
11. 高级特性
11.1 多线程支持
虽然 libwebsockets 是单线程设计,但支持多线程场景:
11.1.1 多线程服务
c
// 每个线程运行独立的事件循环
for (n = 0; n < thread_count; n++) {
pthread_create(&threads[n], NULL, service_thread, &context->pt[n]);
}
void *
service_thread(void *d)
{
struct lws_context_per_thread *pt = d;
while (running) {
lws_service_tsi(context, 50, pt->tsi);
}
}
11.1.2 线程安全 API
c
// 唯一线程安全的 API
lws_cancel_service(context); // 从其他线程唤醒事件循环
11.2 TLS/SSL 集成
11.2.1 多后端支持
libwebsockets 支持多种 TLS 后端:
- OpenSSL:最常用,功能完整
- mbedTLS:轻量级,适合嵌入式
- SChannel:Windows 原生
- AWS-LC:AWS 优化版本
11.2.2 TLS 握手流程
c
// lib/tls/tls-client.c
int
lws_tls_client_connect(struct lws *wsi)
{
// 1. 创建 SSL 上下文
wsi->tls.ssl = SSL_new(wsi->a.vhost->tls.ssl_ctx);
// 2. 绑定套接字
SSL_set_fd(wsi->tls.ssl, wsi->desc.sockfd);
// 3. 设置 SNI
SSL_set_tlsext_host_name(wsi->tls.ssl, hostname);
// 4. 异步握手
n = SSL_connect(wsi->tls.ssl);
if (n < 0) {
int err = SSL_get_error(wsi->tls.ssl, n);
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
// 需要更多 I/O,等待事件
lwsi_set_state(wsi, LRS_WAITING_SSL);
return 0;
}
}
// 5. 握手完成
lwsi_set_state(wsi, LRS_ESTABLISHED);
return 0;
}
11.3 插件系统
libwebsockets 支持动态插件:
c
// lib/plugins/README.md
// 插件可以动态加载或静态链接
// 动态加载
struct lws_plugin *plugin = lws_plugin_load("myplugin.so");
// 静态链接
// 在 CMakeLists.txt 中:
target_link_libraries(myapp libwebsockets_plugin_myplugin)
12. 调试与故障排除
12.1 日志系统
c
// 设置日志级别
lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE |
LLL_INFO | LLL_DEBUG | LLL_PARSER | LLL_HEADER |
LLL_EXT | LLL_CLIENT | LLL_LATENCY | LLL_THREAD,
NULL);
// 自定义日志回调
void
my_log_cb(int level, const char *line)
{
fprintf(stderr, "%s\n", line);
}
lws_set_log_level(..., my_log_cb);
12.2 状态检查
c
// 检查 WSI 状态
lwsi_state(wsi); // 当前状态
lwsi_role(wsi); // 角色
lwsi_role_client(wsi); // 是否客户端
lwsi_role_server(wsi); // 是否服务器
// 检查连接状态
lwsi_state_est(wsi); // 是否已建立
12.3 常见问题
12.3.1 连接超时
c
// 检查超时设置
wsi->sul_connect_timeout.us = lws_now_usecs() + 30 * LWS_US_PER_SEC;
// 在超时回调中:
if (lwsi_state(wsi) == LRS_WAITING_CONNECT) {
lwsl_wsi_warn(wsi, "Connection timeout");
lws_wsi_close(wsi, LWS_CLOSE_STATUS_ABNORMAL_CLOSE);
}
12.3.2 内存泄漏
c
// 使用工具检查
valgrind --leak-check=full ./myapp
// 检查 buflist 是否未释放
assert(!wsi->buflist);
assert(!wsi->buflist_out);
11. HTTP 协议处理
11.1 HTTP/1 解析
libwebsockets 使用基于状态机的 HTTP 解析器:
11.1.1 词法分析表
libwebsockets 使用预编译的词法分析表来快速识别 HTTP 头:
c
// lib/roles/http/minilex.c
// 生成词法分析表,将常见 HTTP 头名称映射到 token ID
static const unsigned char lextable_h1[] = {
#include "lextable.h"
};
// 解析过程
int
lws_http_parse(struct lws *wsi, unsigned char *buf, int len)
{
// 使用词法表快速识别头名称
token = lextable_lookup(lextable_h1, header_name);
// 根据 token ID 存储头值
switch (token) {
case WSI_TOKEN_HOST:
lws_hdr_copy(wsi, buf, len, WSI_TOKEN_HOST);
break;
case WSI_TOKEN_UPGRADE:
// 检查是否是 WebSocket 升级
if (strcmp(buf, "websocket") == 0)
upgrade_to_ws = 1;
break;
// ...
}
}
11.1.2 HTTP 头解析状态机
c
// lib/roles/http/parsers.c
enum http_parser_state {
WSI_TOKEN_NAME_PART, // 解析头名称
WSI_TOKEN_SKIPPING_SAW_CR, // 跳过 CR
WSI_TOKEN_SKIPPING, // 跳过未知头
WSI_TOKEN_VALUE_PART, // 解析头值
WSI_TOKEN_LEADING_WS, // 前导空白
};
int
lws_http_parse(struct lws *wsi, unsigned char c)
{
switch (wsi->http.ah->parser_state) {
case WSI_TOKEN_NAME_PART:
// 累积头名称
if (c == ':') {
// 名称结束,查找 token ID
token = lws_tokenize(wsi->http.ah->lextable_pos, ...);
wsi->http.ah->parser_state = WSI_TOKEN_VALUE_PART;
} else {
wsi->http.ah->lextable_pos = lextable_lookup_next(...);
}
break;
case WSI_TOKEN_VALUE_PART:
// 累积头值
if (c == '\r' || c == '\n') {
// 头解析完成
lws_hdr_store(wsi, token, value_buf, value_len);
wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART;
}
break;
}
}
11.2 HTTP/2 实现
11.2.1 HTTP/2 帧格式
HTTP/2 使用二进制帧格式:
+-----------------------------------------------+
| Length (24) |
+---------------+---------------+---------------+
| Type (8) | Flags (8) |
+-+-------------+---------------+-------------------------------+
|R| Stream Identifier (31) |
+=+=============================================================+
| Frame Payload (0...) ...
+---------------------------------------------------------------+
11.2.2 HTTP/2 状态机
c
// lib/roles/h2/http2.c
enum lws_h2_states {
LWS_H2S_IDLE, // 空闲
LWS_H2S_RESERVED_LOCAL, // 本地保留
LWS_H2S_RESERVED_REMOTE, // 远程保留
LWS_H2S_OPEN, // 打开
LWS_H2S_HALF_CLOSED_REMOTE, // 远程半关闭
LWS_H2S_HALF_CLOSED_LOCAL, // 本地半关闭
LWS_H2S_CLOSED, // 关闭
};
// 状态转换有效性检查
static const uint16_t http2_rx_validity[] = {
[LWS_H2S_IDLE] = (1 << LWS_H2_FRAME_TYPE_SETTINGS) |
(1 << LWS_H2_FRAME_TYPE_HEADERS) |
(1 << LWS_H2_FRAME_TYPE_PRIORITY),
[LWS_H2S_OPEN] = (1 << LWS_H2_FRAME_TYPE_DATA) |
(1 << LWS_H2_FRAME_TYPE_HEADERS) |
(1 << LWS_H2_FRAME_TYPE_RST_STREAM) |
(1 << LWS_H2_FRAME_TYPE_PING),
// ...
};
11.2.3 HTTP/2 多路复用
c
// lib/roles/h2/http2.c
// 一个 HTTP/2 连接可以有多个流(stream)
// 每个流是一个独立的 WSI,共享父连接
struct lws *parent_wsi; // HTTP/2 连接
struct lws *stream_wsi; // HTTP/2 流
// 创建新流
stream_wsi = lws_h2_stream_create(parent_wsi, stream_id);
// 流共享父连接的套接字
stream_wsi->mux.parent_wsi = parent_wsi;
stream_wsi->mux_substream = 1;
stream_wsi->desc.sockfd = parent_wsi->desc.sockfd; // 共享 fd
11.2.4 HPACK 头压缩
HTTP/2 使用 HPACK 压缩 HTTP 头:
c
// lib/roles/h2/hpack.c
// HPACK 使用静态表和动态表压缩头
// 静态表(预定义的头)
static const struct hpack_hf_static hpack_static[] = {
{":method", "GET"},
{":method", "POST"},
{":path", "/"},
{":scheme", "http"},
{":scheme", "https"},
// ... 61个预定义项
};
// 动态表(运行时添加)
struct hpack_dynamic_table {
struct hpack_dte *entries;
size_t size;
size_t max_size;
};
// 编码头
int
lws_h2_hpack_encode(struct lws *wsi, const char *name, const char *value,
unsigned char **p, unsigned char *end)
{
// 1. 查找静态表
idx = hpack_find_static(name, value);
if (idx >= 0) {
// 使用索引编码
hpack_encode_int(*p, end, idx, 7, 0x80);
return 0;
}
// 2. 查找动态表
idx = hpack_find_dynamic(wsi->h2.hpack, name, value);
if (idx >= 0) {
hpack_encode_int(*p, end, idx + 61, 6, 0x40);
return 0;
}
// 3. 字面量编码
hpack_encode_literal(*p, end, name, value, ...);
// 4. 添加到动态表
hpack_add_dynamic(wsi->h2.hpack, name, value);
}
11.3 HTTP 服务器功能
11.3.1 静态文件服务
c
// lib/roles/http/server/server.c
int
lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
const char *other_headers)
{
// 1. 打开文件
fd = lws_plat_file_open(file, LWS_O_RDONLY);
// 2. 获取文件大小
struct stat s;
fstat(fd, &s);
// 3. 发送 HTTP 响应头
lws_write(wsi, http_headers, header_len, LWS_WRITE_HTTP_HEADERS);
// 4. 发送文件内容
while (remaining > 0) {
n = read(fd, buf, sizeof(buf));
lws_write(wsi, buf, n, LWS_WRITE_HTTP);
remaining -= n;
}
close(fd);
return 0;
}
11.3.2 动态内容生成
c
// lib/roles/http/server/server.c
int
lws_http_transaction_completed(struct lws *wsi)
{
// 1. 重置 HTTP 头表
lws_header_table_reset(wsi, 0);
// 2. 准备下一个事务
lwsi_set_state(wsi, LRS_ESTABLISHED);
// 3. 如果还有待处理数据,继续处理
if (lws_buflist_next_segment_len(&wsi->buflist, NULL))
lws_callback_on_writable(wsi);
return 0;
}
12. MQTT 协议处理
12.1 MQTT 协议概述
libwebsockets 实现了 MQTT 3.1.1 客户端,支持 QoS 0 和 QoS 1。
12.2 MQTT 消息格式
MQTT 消息由固定头和可变头、负载组成:
Fixed Header:
+-----------------+-----------------+
| Message Type | Flags |
+-----------------+-----------------+
| Remaining Length (1-4 bytes) |
+-----------------------------------+
Variable Header: (根据消息类型)
+-----------------------------------+
| Protocol-specific fields |
+-----------------------------------+
Payload: (可选)
+-----------------------------------+
| Application data |
+-----------------------------------+
12.3 MQTT 连接流程
12.3.1 CONNECT 消息
c
// lib/roles/mqtt/client/client-mqtt-handshake.c
int
lws_mqtt_client_send_connect(struct lws *wsi)
{
unsigned char *p, *start;
int len, n;
// 1. 构建 CONNECT 消息
p = start = lws_malloc(LWS_PRE + 256, ...);
p += LWS_PRE;
// 固定头
*p++ = LWS_MQTT_MSG_CONNECT | 0x02; // Message Type + Flags
// 可变头
// Protocol Name
*p++ = 0x00; *p++ = 0x04; // Length
memcpy(p, "MQTT", 4); p += 4;
// Protocol Level
*p++ = 0x04; // MQTT 3.1.1
// Connect Flags
*p++ = 0x02; // Clean Session
// Keep Alive
*p++ = (keepalive >> 8) & 0xff;
*p++ = keepalive & 0xff;
// Client Identifier
*p++ = (client_id_len >> 8) & 0xff;
*p++ = client_id_len & 0xff;
memcpy(p, client_id, client_id_len); p += client_id_len;
// 2. 计算剩余长度
len = p - start - LWS_PRE - 1; // 减去固定头第一字节
start[LWS_PRE] = len; // 简化:实际需要变长编码
// 3. 发送
return lws_write(wsi, start + LWS_PRE, p - start - LWS_PRE,
LWS_WRITE_HTTP);
}
12.3.2 CONNACK 处理
c
// lib/roles/mqtt/client/client-mqtt.c
int
lws_mqtt_client_rx_sm(struct lws *wsi, unsigned char c)
{
switch (wsi->mqtt->rx_state) {
case LMQCRS_AWAIT_CONNACK:
// 解析 CONNACK
if (wsi->mqtt->rx_pos == 0) {
// 固定头
wsi->mqtt->rx_msg_type = (c >> 4) & 0x0f;
wsi->mqtt->rx_remaining = c & 0x0f;
wsi->mqtt->rx_pos = 1;
} else if (wsi->mqtt->rx_pos == 1) {
// 剩余长度(简化)
wsi->mqtt->rx_remaining = c;
wsi->mqtt->rx_pos = 2;
} else if (wsi->mqtt->rx_pos == 2) {
// 可变头:Connect Acknowledge Flags
wsi->mqtt->rx_pos = 3;
} else if (wsi->mqpt->rx_pos == 3) {
// 可变头:Return Code
if (c == 0) {
// 连接成功
lwsi_set_state(wsi, LRS_ESTABLISHED);
wsi->a.protocol->callback(wsi, LWS_CALLBACK_MQTT_CLIENT_CONNECTED,
wsi->user_space, NULL, 0);
} else {
// 连接失败
lws_wsi_close(wsi, LWS_CLOSE_STATUS_ABNORMAL_CLOSE);
}
wsi->mqtt->rx_state = LMQCRS_IDLE;
}
break;
}
}
12.4 MQTT 发布/订阅
12.4.1 PUBLISH 消息
c
// lib/roles/mqtt/client/client-mqtt.c
int
lws_mqtt_client_send_publish(struct lws *wsi, const char *topic,
const uint8_t *payload, size_t payload_len,
int qos, int retain)
{
unsigned char *p, *start;
int len;
p = start = lws_malloc(LWS_PRE + 256 + payload_len, ...);
p += LWS_PRE;
// 固定头
*p++ = LWS_MQTT_MSG_PUBLISH | (qos << 1) | (retain ? 1 : 0);
// 可变头
// Topic Name
*p++ = (topic_len >> 8) & 0xff;
*p++ = topic_len & 0xff;
memcpy(p, topic, topic_len); p += topic_len;
// Packet Identifier (QoS > 0)
if (qos > 0) {
uint16_t packet_id = lws_mqtt_get_next_packet_id(wsi);
*p++ = (packet_id >> 8) & 0xff;
*p++ = packet_id & 0xff;
}
// Payload
memcpy(p, payload, payload_len); p += payload_len;
// 发送
len = p - start - LWS_PRE;
return lws_write(wsi, start + LWS_PRE, len, LWS_WRITE_HTTP);
}
12.4.2 SUBSCRIBE 消息
c
int
lws_mqtt_client_send_subscribe(struct lws *wsi, const char *topic, int qos)
{
unsigned char *p, *start;
uint16_t packet_id;
p = start = lws_malloc(LWS_PRE + 256, ...);
p += LWS_PRE;
// 固定头
*p++ = LWS_MQTT_MSG_SUBSCRIBE | 0x02; // QoS 1
// 可变头:Packet Identifier
packet_id = lws_mqtt_get_next_packet_id(wsi);
*p++ = (packet_id >> 8) & 0xff;
*p++ = packet_id & 0xff;
// Payload:Topic Filter + QoS
*p++ = (topic_len >> 8) & 0xff;
*p++ = topic_len & 0xff;
memcpy(p, topic, topic_len); p += topic_len;
*p++ = qos;
// 发送
return lws_write(wsi, start + LWS_PRE, p - start - LWS_PRE,
LWS_WRITE_HTTP);
}
12.5 MQTT QoS 处理
12.5.1 QoS 1 确认机制
c
// lib/roles/mqtt/client/client-mqtt.c
// 发送 QoS 1 消息后,等待 PUBACK
// 1. 发送 PUBLISH (QoS 1)
lws_mqtt_client_send_publish(wsi, topic, payload, len, 1, 0);
// 2. 等待 PUBACK
// 在 lws_mqtt_client_rx_sm 中:
if (wsi->mqtt->rx_msg_type == LWS_MQTT_MSG_PUBACK) {
// 解析 Packet Identifier
uint16_t ack_packet_id = (p[0] << 8) | p[1];
// 查找对应的发送消息
struct lws_mqtt_pending *pending =
lws_mqtt_find_pending(wsi, ack_packet_id);
if (pending) {
// 确认收到,可以删除
lws_mqtt_remove_pending(wsi, pending);
// 通知用户
wsi->a.protocol->callback(wsi, LWS_CALLBACK_MQTT_ACK,
wsi->user_space, NULL, ack_packet_id);
}
}
12.5.2 重传机制
c
// lib/roles/mqtt/client/client-mqtt.c
// 如果 PUBACK 超时,重传消息
void
lws_mqtt_resend_timeout_cb(lws_sorted_usec_list_t *sul)
{
struct lws_mqtt_pending *pending =
lws_container_of(sul, struct lws_mqtt_pending, sul);
// 重传
lws_mqtt_client_send_publish(pending->wsi,
pending->topic,
pending->payload,
pending->payload_len,
pending->qos,
pending->retain);
// 重新设置超时
lws_sul_schedule(context, 0, &pending->sul,
lws_mqtt_resend_timeout_cb,
pending->retry_us);
}
13. 总结
libwebsockets 是一个设计精良的网络库,其核心优势:
- 事件驱动架构:单线程、非阻塞,高效处理大量并发
- 模块化设计:清晰的模块划分,易于理解和扩展
- 角色系统:灵活的协议抽象,支持多种协议
- 内存优化:多种分配策略,适合嵌入式系统
- 跨平台支持:支持多种操作系统和事件循环
13.1 设计模式总结
- 策略模式:角色系统通过函数指针表实现多态
- 状态模式:WSI 状态机管理连接生命周期
- 观察者模式:事件循环 + 回调机制
- 工厂模式:Context 创建和管理 WSI
- 适配器模式:事件循环适配器集成外部库
13.2 性能特点
- 高并发:单线程可处理数万连接
- 低延迟:事件驱动,快速响应
- 低资源:适合嵌入式系统
- 可扩展:支持多线程和 SMP
13.3 适用场景
- WebSocket 服务器/客户端
- HTTP/1、HTTP/2 服务器
- MQTT 客户端
- **嵌入式网络应用
- 高并发网络服务
参考资料
- libwebsockets 源码:
doc/websocket_learn/libwebsockets/ - 官方文档:
doc/websocket_learn/libwebsockets_documentation.md - README 文件:
doc/websocket_learn/libwebsockets/READMEs/ - 最小示例:
doc/websocket_learn/libwebsockets/minimal-examples/
本文档基于 libwebsockets 源码分析编写,旨在深入理解库的内部工作原理。