--- tags: [] date created: 星期一, 十一月 24日 2025, 4:14:06 下午 date modified: 星期一, 十一月 24日 2025, 4:48:23 下午 --- # 2.4.1 传输层拓扑与套接字模型 (Transport Layer Topology & Socket Model) ## 1. 传输拓扑:UDP 单播直连 (UDP Unicast Direct) - **基线决策**:采用 **UDP 单播 (Unicast)**,由 `DisplayController` 绑定本地网卡,直接向配置的显控终端 IP 发送数据。 - **设计论证**: - **去中心化**:避免了组播(Multicast)对交换机 IGMP Snooping 配置的依赖,降低部署复杂度。 - **无状态**:相比 TCP,UDP 无需维护连接状态(三次握手/四次挥手),消除 `TIME_WAIT` 资源占用,最适合“发后即忘(Fire-and-Forget)”的遥测场景。 - **隔离性**:点对点发送天然隔离了网络风暴,若存在多个显控终端,建议通过应用层复制分发(Application-Layer Fanout),而非网络层组播,以实现对不同终端的差异化流控(例如:给指挥屏发全量数据,给监控屏发降级数据)。 ## 2. 套接字模型:非阻塞 + 边缘触发 (Non-blocking + Epoll ET) 为了支撑高频率(如 100Hz+)的数据刷新而不阻塞业务线程,必须采用全异步 I/O 模型。 - **模式基线**:**Non-blocking Socket** + **Epoll Edge Triggered (ET)**。 - **核心策略:“写优先,等其次” (Write-First, Wait-Later)** - **传统误区**:很多设计是先 `epoll_wait` 等到 `EPOLLOUT` 再写。这在高吞吐场景下是低效的,因为 99% 的时间网络都是可写的,无谓的 `epoll_wait` 增加了系统调用开销。 - **基线逻辑**: 1. **直接发送**:业务线程或发送线程直接调用 `sendto()`。 2. **处理拥塞**:仅当 `sendto()` 返回 `EAGAIN` / `EWOULDBLOCK`(内核缓冲区满)时,才将 Socket 加入 `epoll` 监听 `EPOLLOUT` 事件。 3. **恢复发送**:当 `epoll` 通知可写时,继续发送剩余数据,并立即移除监听,回归“直接发送”模式。 ## 3. 关键配置参数深度解析 (Parameter Tuning) 这里我们不硬定“8MB”,而是给出**基于场景的计算逻辑**。 ### 3.1 发送缓冲区 (`SO_SNDBUF`):突发吸收器 - **设计目标**:吸收 **微突发 (Micro-burst)**。雷达数据通常是脉冲式的,一个 CPI 处理完会瞬间产生大量点迹/航迹数据。Socket 缓冲区必须足够大,能完全容纳“最大可能的单次突发数据量”,让网卡以物理线速慢慢发出去,而不是让应用程序阻塞。 - 计算公式: $$ \text{Target Buffer} = \text{Max\_Burst\_Size} \times \text{Safety\_Factor}$$ - _Max_Burst_Size_:假设最大工况下,一帧(CPI)产生的点迹 + 航迹 + 回波切片总大小。例如:1000 个点迹 (x 64B) + 200 航迹 (x 128B) + 回波切片 (256KB) $\approx$ 350KB。 - _Safety_Factor_:考虑到 OS 调度抖动,可能连续积压 2-3 帧数据。取系数 4。 - _计算结果_:$350\text{KB} \times 4 \approx 1.4\text{MB}$。 - **配置建议**: - **基线值**:动态计算或设置为 **4MB - 8MB**(留有充足余量)。对于现代服务器内存而言,这个开销极低,但收益(零阻塞)巨大。 - **实施**:`setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &size, …)`。注意 Linux 内核会将设置值翻倍以用于元数据,实际可用空间即为设定值。 ### 3.2 服务质量标记 (`IP_TOS` / DSCP):网络特权 - **设计目标**:告诉交换机和路由器,这股数据流是“VIP”,在网络拥塞时优先转发,最后丢弃。 - **参数选择**: - **DSCP (Differentiated Services Code Point)**:推荐使用 **EF (Expedited Forwarding, 0x2E)** 或 **CS6 (0x30)**。这是仅次于网络控制信令(CS7)的最高优先级,通常用于 VoIP 语音或实时控制数据。 - **实施**: ```cpp int tos = 0xB8; // DSCP EF (101110) << 2 setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)); ``` ### 3.3 路径 MTU 发现 (PMTU Discovery):避免分片 - **现状**:虽然采集链路用了 JUMBO Frame (9000),但**显控链路通常经过普通交换机或公网,MTU 往往限制在 1500**。 - **策略**: - **强制禁止分片 (`IP_MTU_DISCOVER` = `IP_PMTUDISC_DO`)**:让内核在数据包超过 MTU 时直接报错 `EMSGSIZE`,而不是默默分片(IP 分片会严重降低 UDP 可靠性,一个分片丢失导致整个包作废)。 - **应用层分包**:`DisplayController` 必须根据实际 MTU(如 1472 字节 Payload)在应用层进行切片(Fragmentation)。这与我们后续 2.4.2 的“Atomic Batch”设计不冲突,一个 Batch 可以被切成多个 UDP 包发送,只要包含相同的 `BatchID` 和 `SequenceID` 即可。 ## 4. 高效线程模型 (The "Pumper" Thread) 为了彻底隔离网络抖动对计算核心的影响,我们确立 **独立发送线程** 模型。 - **架构**: - **生产者**:`SignalProcessor` 计算完毕,将 `DetectionResult` 对象放入一个 **SPSC (单生产单消费) 无锁队列**。 - **消费者 (DisplayController IO Thread)**: - 绑定到非关键 CPU 核心(如 Core 14)。 - 死循环从队列取数据。 - 执行 Protobuf 序列化。 - 执行 `sendto()`。 - 若 `EAGAIN`,则 `epoll_wait()`。 - **收益**: - **序列化卸载**:Protobuf 序列化是 CPU 密集型操作,将其移出信号处理主线程,保护了核心计算资源。 - **故障隔离**:即使网络断开、Socket 缓冲区塞满,影响的也只是这个 IO 线程(队列满后丢弃新数据),绝不会阻塞 FFT/CFAR 运算。 --- ## 总结:2.4.1 基线图谱 |**配置项**|**核心基线**|**设计意图**| |---|---|---| |**拓扑**|**UDP Unicast**|简单、高效、无状态。| |**I/O 模型**|**Non-blocking + Epoll ET**|" 写优先 " 策略,最大化吞吐,最小化系统调用。| |**发送缓冲**|**动态计算 (Max Burst x 4)**|约 4MB-8MB。吸收脉冲式突发,防止应用层阻塞。| |**QoS**|**DSCP EF / CS6**|确保在交换机侧的转发优先级。| |**分片策略**|**禁止 IP 分片 + 应用层切分**|避免 IP 分片重组风险,提升丢包恢复能力。| |**线程模型**|**独立 IO 线程 + SPSC 队列**|隔离网络抖动与序列化开销,保护计算核心。| **提问**:您是否确认 **“非阻塞 +Epoll ET”** 模型以及 **“独立线程序列化与发送”** 的架构基线?确认后我们将进入 **2.4.2 业务数据序列化规范**,讨论数据具体怎么“打包”。