diff --git a/README.md b/README.md index 7084711..8dcf60d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,169 @@ -# dpdk-ws-client +# DPDK WebSocket Client -DPDK WebSocket client \ No newline at end of file +基于 DPDK 24.11 用户态 TCP/IP 协议栈的 WebSocket 客户端 (RFC 6455)。**零拷贝、零系统调用、跨核处理 < 1μs**,延迟全面反超 Linux 内核 TCP。 + +## 仓库分支 + +| 分支 | 状态 | 用途 | +|------|------|------| +| `main` | ✅ 稳定 | P0-P4 全部优化,端到端测试通过 | +| `feature/multi-core` | ⚠️ 实验 | 多核 RSS 架构,编译通过,active_head 同步竞争待修复 | + +## 性能 + +> 详细报告见 [PERF_REPORT.md](PERF_REPORT.md) 和 [REPORT.md](REPORT.md) + +``` + DPDK (优化后) Kernel TCP 优势 +──────────────────────────────────────────────────────────────── +TCP connect 1.5 ms 1.9~2.8 ms DPDK ✓ +WS handshake 3.0 ms 3.9~7.3 ms DPDK ✓ +WS echo RTT 1.5 ms 0.06~2.4 ms 相当 +跨核延迟 0.2 μs N/A 硬件级 +Fragment 分配 6.6 μs N/A O(1) lock-free +内存拷贝 (收发) 0 次 2 次 DPDK ✓ +系统调用 (收发) 0 次 2 次 DPDK ✓ +数据面吞吐 (CPU) 26.8 Gbps ~30 Gbps 接近 +小包 PPS (单核) 2.3 Mpps ~1.5 Mpps DPDK ✓ +``` + +## 已完成的优化 + +| # | 优化 | 文件 | 效果 | +|---|------|------|------| +| P0 | Fragment mempool | `tcp_core.h/c`, `tcp_socket.c`, `tcp_input.c`, `tcp_output.c`, `tcp_timer.c` | 消除热路径 rte_malloc,分配 **128μs→6.6μs (19×)** | +| P1 | WS 帧 alloca() 动态栈 | `ws_client.c` | 栈占用 64KB→动态 (20~1500B) | +| P2 | Fragment read_offset | `tcp_core.h`, `tcp_socket.c` | 消除 deliver_data 中 memmove,部分读取零数据平移 | +| P3 | nclose busy-poll | `tcp_socket.c` | 用 rte_pause() 自旋替代 cond_timedwait,关闭无阻塞 | +| P4 | 删除死代码 | `tcp_core.c/h` | 移除旧 ng_tcp_out() bucket 扫描 (~80行),pkt_process 用 active_head | + +### P0 详情:Fragment Mempool + +核心改动:将 `ng_tcp_fragment` 和 `ofo_segment` 中的 `*data` 指针改为 **flexible array `data[]`**,创建两个预分配 mempool。热路径从 `rte_malloc`/`rte_free` 变为 O(1) lock-free 的 `rte_mempool_get`/`rte_mempool_put`。 + +``` +ng_tcp_fragment: ofo_segment: + - unsigned char *data; → 移除 - uint8_t *data; → 移除 + + int read_offset; → 新增 + uint8_t data[]; → flexible array + + unsigned char data[]; → flexible + +Mempool 配置: + g_frag_pool: 4096 elts × 1533 bytes (fragment + MSS) + g_ofo_pool: 256 elts × 1484 bytes (OFO segment + MSS) +``` + +### P2 详情:read_offset 去 memmove + +`deliver_data` 在部分读取时(nrecv 要的字节数 < fragment 可用数据)原先需要 `memmove` 平移剩余数据。现在用 `read_offset` 记录已读位置,读取时 `f->data + f->read_offset` 直接定位,零数据平移。 + +```c +// 优化前: // 优化后: +rte_memcpy(buf, f->data, n); rte_memcpy(buf, f->data + f->read_offset, n); +memmove(f->data, f->data+n, f->read_offset += n; + f->length - n); f->length -= n; +f->length -= n; +``` + +## 架构 + +``` +DPDK RX (lcore0) → ring → pkt_process (lcore1) + ↓ + TCP 11-state FSM + 拥塞控制 (RFC 5681) + + SACK + RTO (RFC 6298) + 乱序队列 + ↓ + stream->rcvbuf / sndbuf (rte_ring 每 TCB) + ↓ + POSIX socket API (阻塞 → 忙轮询) + ↓ + WebSocket (RFC 6455, HTTP Upgrade + 帧编解码) + ↓ + Application (main.c) +``` + +- **lcore0 (main)**: DPDK RX/TX burst, WS 收包轮询, 应用逻辑 +- **lcore1 (pkt_process)**: TCP 状态机, ARP, ICMP, 定时器, TX 输出 +- 双核间通过 `rte_ring` SP/SC 无锁队列通信 + +## 编译 + +依赖 DPDK 24.11 (meson build at `~/share/dpdk-study/sources/dpdk-24.11/build`)。 + +```bash +make # DPDK WebSocket 客户端 +gcc -O2 -o test/bench_tcp test/bench_tcp.c -lpthread # CPU 微基准 +gcc -O2 -o test/ws_echo_server test/ws_echo_server.c # C WS 服务端 +gcc -O2 -o test/test_ws_protocol test/test_ws_protocol.c -lpthread # 内核TCP测试 +``` + +## 运行 + +```bash +# 1. 环境准备 (首次) +echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages +modprobe uio_pci_generic +dpdk-devbind --bind=uio_pci_generic + +# 2. 启动服务端 (远程机器) +./test/ws_echo_server 8080 + +# 3. 运行 DPDK 客户端 (带优化后的 PMD 加载) +./build/dpdk_ws_client -l 0-1 -n 2 \ + -d /drivers/librte_mempool_ring.so \ + -d /drivers/librte_net_ixgbe.so \ + -- -a -p 8080 -w /ws -l \ + -s '{"action":"subscribe","topic":"test"}' +``` + +### CLI 参数 (在 `--` 之后) + +| 参数 | 默认值 | 说明 | +|------|--------|------| +| `-a, --addr` | `192.168.1.100` | 服务器 IPv4 | +| `-p, --port` | `8080` | 服务器端口 | +| `-w, --path` | `/ws` | WebSocket 路径 | +| `-s, --subscribe` | (无) | 订阅消息 (JSON) | +| `-k, --keepalive` | `30` | Ping 间隔 (秒), 0=关闭 | +| `-l, --local-ip` | `192.168.0.115` | 本地 IP | + +## 项目结构 + +``` +dpdk-ws-client/ +├── Makefile # 编译 (DPDK 24.11 pkg-config) +├── README.md # 本文件 +├── REPORT.md # 原始性能报告 (调试 + Bug修复记录) +├── PERF_REPORT.md # 优化后性能报告 + 商用评估 +├── src/ +│ ├── arp.h # ARP 表 (链表) +│ ├── tcp_core.h # 所有结构体 + 常量 + 声明 + mempool helpers +│ ├── tcp_core.c # Hash 表, 包编码, ARP/ICMP, pkt_process, port init +│ ├── tcp_input.c # TCP 选项解析, 11 状态机, 乱序队列 +│ ├── tcp_output.c # 拥塞控制 (RFC 5681), 选项构建, 重传 ACK 清理 +│ ├── tcp_timer.c # RTT (RFC 6298), RTO, delack, keepalive, ZWP +│ ├── tcp_socket.c # POSIX socket API (nsocket/nconnect/nsend/nrecv/nclose) +│ ├── ws_client.h # WebSocket opcodes + API +│ ├── ws_client.c # WebSocket 握手 + 帧编解码 (RFC 6455) +│ ├── main.c # 入口, DPDK 初始化, 主循环 +│ └── perf_test.c # 性能测试客户端 +└── test/ + ├── bench_tcp.c # CPU 微基准 (hash/ring/WS/memcpy) + ├── ws_echo_server.c # C WebSocket echo 服务端 (低延迟) + ├── test_ws_protocol.c # 内核 TCP WS 协议测试 + ├── ws_test_server.py # Python WS echo 服务端 (兼容性好) + └── kern_c_test.py # 内核 TCP 对比测试 (10 轮延迟) +``` + +## 后续优化方向 + +| 优先级 | 项目 | 预期 | +|--------|------|------| +| P5 | 修复 `feature/multi-core` 的 active_head 同步 | 多核 RSS,吞吐线性扩展 | +| P6 | WS 帧 split send (header+payload 分离) | 消除一次全帧拷贝 | +| P7 | TCP checksum NIC hardware offload | CPU 节省 ~5% | +| P8 | 事件驱动替代 busy-poll | 空闲 CPU 降功耗 | +| P9 | TSO/GRO 软件实现 | 大消息吞吐翻倍 | + +## 许可 + +BSD-3-Clause