限时福利领取


WebSocket协议核心解析

RFC6455定义的WebSocket协议包含两个关键阶段:

  1. 握手阶段:客户端发送包含Upgrade: websocket头的HTTP请求,服务端返回101 Switching Protocols响应完成协议切换。密钥交换过程如下:

    // 客户端生成Base64编码的随机密钥
    char key[24];
    snprintf(key, sizeof(key), "%d", rand());
    base64_encode(key, &client_key);
  2. 数据帧格式

    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/64)           |
    |N|V|V|V|       |S|             |   (if payload len==126/127)   |
    | |1|2|3|       |K|             |                               |
    +-+-+-+-+-------+-+-------------+-------------------------------+
    FIN标志位指示帧结束,opcode区分文本(0x1)/二进制(0x2)数据类型。

libwebsockets实战模块

TLS加密握手实现

TLS握手流程

struct lws_context_creation_info info;
memset(&info, 0, sizeof info);
info.port = CONTEXT_PORT_NO_LISTEN;
info.protocols = protocols;
info.gid = -1;
info.uid = -1;
info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;

// 加载证书链(PEM格式)
info.ssl_cert_filepath = "./client-cert.pem";
info.ssl_private_key_filepath = "./client-key.pem";

context = lws_create_context(&info);

帧处理与环形缓冲区

#define BUF_SIZE 8192
typedef struct {
    uint8_t data[BUF_SIZE];
    size_t head, tail;
} ring_buffer;

// 写入数据时处理回绕
int rb_write(ring_buffer *rb, const void *data, size_t len) {
    if (BUF_SIZE - (rb->tail - rb->head) < len) 
        return -1; // 缓冲区满

    size_t first_part = min(len, BUF_SIZE - (rb->tail % BUF_SIZE));
    memcpy(rb->data + (rb->tail % BUF_SIZE), data, first_part);
    if (first_part < len)
        memcpy(rb->data, data + first_part, len - first_part);
    rb->tail += len;
    return 0;
}

性能优化三要素

内存泄漏检测

使用Valgrind定位常见问题:

valgrind --leak-check=full --show-leak-kinds=all ./ws_client

典型问题包括: - 未释放的SSL上下文 - 连接关闭时未清理的环形缓冲区

线程安全Connection Map

连接管理架构

pthread_mutex_t conn_lock = PTHREAD_MUTEX_INITIALIZER;
struct hashmap *conn_map;

void add_connection(int fd, struct ws_conn *conn) {
    pthread_mutex_lock(&conn_lock);
    hashmap_put(conn_map, &fd, sizeof(fd), conn);
    pthread_mutex_unlock(&conn_lock);
}

压测数据(AWS c5.xlarge)

| 并发数 | 平均延迟 | 吞吐量 | |--------|----------|--------| | 100 | 12ms | 8.3k/s | | 500 | 47ms | 6.1k/s |

避坑指南

  1. FIN/RST帧处理
  2. 收到FIN帧需等待后续帧的continuation帧
  3. RST帧应立即终止当前消息处理

  4. OpenSSL线程安全

    OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | 
                    OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
    OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS |
                       OPENSSL_INIT_ADD_ALL_DIGESTS, NULL);
  5. epoll惊群预防

    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLEXCLUSIVE; // 关键参数

百万连接架构思考

扩展方向提示: 1. 使用io_uring替代epoll减少系统调用 2. 基于一致性哈希的连接分片 3. 用户态协议栈(如DPDK)

关键问题: - 文件描述符限制(需调整/proc/sys/fs/nr_open) - TCP端口复用(SO_REUSEPORT) - 零拷贝传输技术

Logo

音视频技术社区,一个全球开发者共同探讨、分享、学习音视频技术的平台,加入我们,与全球开发者一起创造更加优秀的音视频产品!

更多推荐