cocoon/websocket.h
xfy911 2775033198 feat(websocket): 实现 RFC 6455 WebSocket 支持
- 新增 websocket.h / websocket.c:帧解析、编码、握手、连接管理
- server.c: 检测 Upgrade: websocket 请求,执行握手后进入 ws_handle_connection
- 支持文本/二进制帧 echo、ping/pong、close 帧
- 使用 OpenSSL SHA1 计算 Sec-WebSocket-Accept
- 集成测试:新增 2 项 WebSocket 测试(Python 标准库实现)
- 61 项集成测试全部通过,127 项单元测试全部通过
2026-06-05 15:18:01 +08:00

131 lines
3.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifndef WEBSOCKET_H
#define WEBSOCKET_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/**
* @file websocket.h
* @brief WebSocket 协议实现
*
* 支持 RFC 6455 WebSocket 握手、帧解析与编码。
* 服务器端实现不发送掩码mask=0接收客户端掩码帧。
*/
/**
* @brief WebSocket 操作码
*/
typedef enum {
WS_OP_CONT = 0x0, /**< 继续帧 */
WS_OP_TEXT = 0x1, /**< 文本帧 */
WS_OP_BINARY = 0x2, /**< 二进制帧 */
WS_OP_CLOSE = 0x8, /**< 关闭帧 */
WS_OP_PING = 0x9, /**< Ping 帧 */
WS_OP_PONG = 0xA /**< Pong 帧 */
} ws_opcode_t;
/**
* @brief WebSocket 帧结构
*/
typedef struct {
uint8_t opcode; /**< 操作码 */
bool fin; /**< 是否为最后一帧 */
bool masked; /**< 是否掩码 */
uint64_t payload_len;/**< 负载长度 */
uint8_t mask_key[4]; /**< 掩码密钥(仅客户端发送时有效) */
uint8_t *payload; /**< 负载数据(已解掩码) */
} ws_frame_t;
/**
* @brief 执行 WebSocket 握手响应
*
* 根据 RFC 6455对 Sec-WebSocket-Key 计算 SHA1 + Base64 响应。
*
* @param fd 客户端 socket
* @param key 客户端发来的 Sec-WebSocket-Key
* @return 0 成功,-1 失败
*/
int ws_handshake(int fd, const char *key);
/**
* @brief 解析单个 WebSocket 帧
*
* 从数据流中解析一个完整帧。如果数据不完整,返回 -1 且不修改 frame。
*
* @param data 输入数据
* @param len 数据长度
* @param frame 输出帧结构(调用者需初始化)
* @param consumed 输出:消耗的字节数
* @return 0 成功,-1 数据不完整,-2 格式错误
*/
int ws_parse_frame(const uint8_t *data, size_t len, ws_frame_t *frame, size_t *consumed);
/**
* @brief 释放帧占用的负载内存
*
* @param frame 帧指针
*/
void ws_frame_free(ws_frame_t *frame);
/**
* @brief 发送 WebSocket 帧
*
* @param fd 客户端 socket
* @param opcode 操作码
* @param payload 负载数据
* @param len 负载长度
* @return 0 成功,-1 失败
*/
int ws_send_frame(int fd, uint8_t opcode, const uint8_t *payload, size_t len);
/**
* @brief 发送文本帧
*
* @param fd 客户端 socket
* @param text 文本内容UTF-8
* @return 0 成功,-1 失败
*/
int ws_send_text(int fd, const char *text);
/**
* @brief 发送关闭帧
*
* @param fd 客户端 socket
* @param code 关闭码(如 1000
* @param reason 关闭原因(可为 NULL
* @return 0 成功,-1 失败
*/
int ws_send_close(int fd, uint16_t code, const char *reason);
/**
* @brief 发送 Ping 帧
*
* @param fd 客户端 socket
* @return 0 成功,-1 失败
*/
int ws_send_ping(int fd);
/**
* @brief 发送 Pong 帧
*
* @param fd 客户端 socket
* @param payload Ping 的负载(可为 NULL
* @param len 负载长度
* @return 0 成功,-1 失败
*/
int ws_send_pong(int fd, const uint8_t *payload, size_t len);
/**
* @brief 处理 WebSocket 连接(主循环)
*
* 进入 WebSocket 帧循环,处理文本/二进制/ping/pong/close。
* 当前实现为简单 echo 服务器。
*
* @param fd 客户端 socket
* @param timeout_ms 超时毫秒0 表示默认)
*/
void ws_handle_connection(int fd, uint32_t timeout_ms);
#endif /* WEBSOCKET_H */