libwebsockets库原理详解

libwebsockets 库原理详解

本文档深入解析 libwebsockets 库的内部工作原理,基于源码分析和官方文档,详细阐述其架构设计、核心机制和实现细节。


目录

  1. 架构设计原理
  2. 核心数据结构
  3. 事件循环机制
  4. 角色系统(Roles)
  5. [WebSocket 协议处理](#WebSocket 协议处理)
  6. 网络层实现
  7. [Secure Streams 原理](#Secure Streams 原理)
  8. 状态机与生命周期
  9. 内存管理
  10. 性能优化策略

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,设计目标:

  1. 分离关注点:连接策略与业务逻辑分离
  2. 配置驱动:通过 JSON 策略文件配置连接
  3. 协议无关:应用代码不依赖具体协议
  4. 自动重试:内置重试和错误恢复机制
  5. 简化安全:自动处理 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   │ ←──────────→ │  远程服务器  │
│  (无网络)   │                   │  (有网络)   │              │             │
└─────────────┘                   └─────────────┘              └─────────────┘

实现原理:

  1. 客户端将 SS 操作序列化为消息
  2. 通过串口发送到代理
  3. 代理反序列化并执行操作
  4. 代理将结果序列化返回客户端

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 是一个设计精良的网络库,其核心优势:

  1. 事件驱动架构:单线程、非阻塞,高效处理大量并发
  2. 模块化设计:清晰的模块划分,易于理解和扩展
  3. 角色系统:灵活的协议抽象,支持多种协议
  4. 内存优化:多种分配策略,适合嵌入式系统
  5. 跨平台支持:支持多种操作系统和事件循环

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 源码分析编写,旨在深入理解库的内部工作原理。

相关推荐
天上飞的粉红小猪2 小时前
c++的IO流
开发语言·c++
ygklwyf2 小时前
JPRS编程竞赛2026#1(AtCoder初学者竞赛442)
c++·算法·模拟
王老师青少年编程2 小时前
信奥赛C++提高组csp-s之倍增算法思想及应用(3)
c++·noip·csp·信奥赛·csp-s·提高组·倍增算法
源代码•宸2 小时前
Leetcode—47. 全排列 II【中等】
经验分享·后端·算法·leetcode·面试·golang·深度优先
Wyy_9527*2 小时前
行为型设计模式——状态模式
java·spring boot·后端
万象.3 小时前
redis客户端安装与实现C++版本
数据库·c++·redis
梅梅绵绵冰3 小时前
springboot初步2
java·spring boot·后端
这儿有一堆花3 小时前
实战:FastAPI与WebSocket的高并发足球数据API开发指南
websocket·网络协议·fastapi
321.。3 小时前
深入理解 Linux 线程封装:从 pthread 到 C++ 面向对象实现
linux·开发语言·c++