第4章:网络协议基础
做低延迟交易,说白了就是跟网络协议打交道。你想想看,行情数据从交易所出来,经过网线、交换机、网卡,最后到你的FPGA逻辑,这一路上每一层协议都在“扒皮”。今天我就带你把这些协议扒个干净。
4.1 以太网帧结构
以太网帧是网络通信的“最小单元”。我刚开始做FPGA网络处理时,第一件事就是对着逻辑分析仪看帧结构。嗯,这东西其实不复杂。
标准以太网帧结构(IEEE 802.3)
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 前导码 | 7 | 同步时钟用,FPGA接收时用来锁定位同步 |
| 帧起始定界符 | 1 | 标志帧开始,值为0xD5 |
| 目的MAC地址 | 6 | 接收方网卡地址 |
| 源MAC地址 | 6 | 发送方网卡地址 |
| 类型/长度 | 2 | 0x0800表示IP,0x0806表示ARP |
| 数据载荷 | 46-1500 | 上层协议数据 |
| 帧校验序列 | 4 | CRC32校验 |
这里有个坑。我曾经在项目中遇到一个奇怪的问题:FPGA收到的帧总是丢包。查了半天,发现是前导码处理不对。很多FPGA的MAC核会自动剥离前导码,但如果你自己写RGMII接口,就得手动处理这7个字节。说白了,前导码就是给接收端“热身”用的,别把它当数据存起来。
4.2 ARP协议
ARP协议解决的是“我知道你IP,但不知道你MAC”的问题。在低延迟交易场景下,ARP其实是个麻烦事——它会产生广播风暴。
我个人习惯的做法是:在FPGA里做一个ARP缓存表,把交易对手的MAC地址硬编码进去。为什么?因为交易链路是固定的,不需要动态学习。你想想看,每次行情来了还要先发个ARP请求,那延迟就上去了。
ARP报文结构
- 硬件类型:0x0001(以太网)
- 协议类型:0x0800(IP)
- 操作码:1=请求,2=应答
- 发送方MAC/IP、目标MAC/IP
我记得有一次调试,发现FPGA发的ARP请求没人理。后来一查,是目标MAC地址填错了。ARP应答里目标MAC是广播地址(FF:FF:FF:FF:FF:FF),但请求里目标MAC得填全0。这种细节,文档上写得很清楚,但实际做起来就容易忘。
4.3 IP协议
IP协议负责把数据包从源地址送到目的地址。在FPGA里处理IP,核心就是解析头部、校验和计算。
IPv4头部(20字节固定部分)
| 字段 | 长度 | 说明 |
|---|---|---|
| 版本/首部长度 | 1字节 | 版本4,长度通常为5(20字节) |
| 服务类型 | 1字节 | 低延迟场景常设高优先级 |
| 总长度 | 2字节 | 包含头部和数据 |
| 标识/标志/片偏移 | 4字节 | 分片用,交易场景很少用 |
| 生存时间 | 1字节 | 防止环路,通常设64 |
| 协议 | 1字节 | 17=UDP,6=TCP |
| 头部校验和 | 2字节 | 只校验头部,不校验数据 |
| 源IP地址 | 4字节 | 32位IP |
| 目的IP地址 | 4字节 | 32位IP |
校验和计算是FPGA的强项。用流水线做,一个时钟周期就能算完。我一般用补码求和再取反的方式,注意每16位累加一次,最后把进位加回去。嗯,这里有个细节:IP校验和只覆盖头部,UDP校验和才覆盖数据。别搞混了。
4.4 UDP协议
UDP是低延迟交易的首选。为什么?因为它简单、无状态、延迟低。说白了就是“发出去就不管了”。
UDP头部(8字节)
| 字段 | 长度 | 说明 |
|---|---|---|
| 源端口 | 2字节 | 发送方端口 |
| 目的端口 | 2字节 | 接收方端口 |
| 长度 | 2字节 | UDP头部+数据的总长度 |
| 校验和 | 2字节 | 可选,0表示不校验 |
在FPGA里解析UDP,我建议直接按字节流处理。收到以太网帧后,先剥掉MAC头(14字节),再剥掉IP头(20字节),剩下的就是UDP头+数据。整个过程用状态机控制,非常简单。
注意:UDP校验和在低延迟场景下经常被设为0。为什么?因为计算校验和需要遍历整个数据包,这会增加延迟。但如果你做的是金融交易,数据完整性很重要,我建议还是算一下。我曾经见过因为UDP校验和错误导致订单数据损坏的案例,那损失可不小。
4.5 TCP协议
TCP协议在低延迟交易里用得少,但也不是完全不用。比如一些管理通道、配置通道会用TCP。TCP的核心是可靠传输,但代价是延迟高。
TCP头部比UDP复杂得多,有20字节固定部分,还有可选字段。关键字段包括:
- 序列号/确认号:保证数据有序到达
- 标志位:SYN、ACK、FIN等,控制连接状态
- 窗口大小:流量控制用
- 校验和:覆盖头部和数据
在FPGA里实现TCP协议栈,说实话挺麻烦的。状态机要处理连接建立、数据传输、连接释放、重传、拥塞控制……我建议除非必要,否则别在FPGA里做完整的TCP。用UDP+应用层确认机制,延迟更低,实现也更简单。
我的建议:交易链路用UDP,管理通道用TCP。这样既保证了低延迟,又保留了管理功能的可靠性。我在一个项目中就是这么做的,效果很好。
4.6 知识体系总览
下面这张图展示了网络协议栈在FPGA处理中的层次关系。从物理层到应用层,每一层都有对应的处理模块。
这张图我画了很多次。每次给团队新人讲网络协议处理,我都会先让他们看这张图。你想想看,从网线进来的数据,先经过物理层恢复时钟和位同步,然后数据链路层做MAC过滤和CRC校验,网络层解析IP头部,传输层处理UDP/TCP,最后应用层才拿到真正的交易数据。每一层都有延迟,每一层也都有优化的空间。
避坑指南:我曾经在项目中遇到一个问题——FPGA收到的UDP包校验和总是错。查了两天,发现是IP头部的总长度字段填错了。UDP校验和计算时要用到IP头部里的总长度,如果这个值不对,校验和肯定算不对。所以,写FPGA代码时,一定要确保每个字段的值都是正确的,别想当然。
好了,网络协议基础就讲到这里。这些协议看起来简单,但实际在FPGA里实现时,每个细节都可能成为性能瓶颈。下一章我们会深入讲如何在FPGA里高效实现这些协议的解析和生成。
公众号:蓝海资料掘金营,微信deep3321