4、TCP协议深度解析:从HFT视角看TCP的“罪与罚”
大家好,我是你们的讲师。今天我们来聊聊TCP协议。很多做高频交易的朋友一听到TCP就皱眉,觉得它“慢”、“不可控”。但说实话,TCP本身的设计初衷就不是为了纳秒级交易,而是为了可靠传输。不过,既然我们要在HFT场景下用它,就必须把它扒个精光,看看哪些地方能优化,哪些地方必须禁用。
我个人习惯是,先看协议头,再看握手流程,最后看流量控制。这样一层层剥开,你才能理解为什么HFT要“阉割”TCP的某些特性。
4.1 TCP头部结构:20字节的“包袱”
TCP头部固定20字节,不算大,但在高频场景下,每一个字节都意味着额外的处理时间。我见过有人为了省这20字节,硬是把数据塞进UDP里,结果丢包了还得自己实现重传,得不偿失。
来看看TCP头部里哪些字段对HFT最关键:
| 字段 | 长度 | 对HFT的影响 |
|---|---|---|
| 源端口/目的端口 | 各2字节 | 必须的,用于多路复用 |
| 序列号 | 4字节 | 用于排序和重传,HFT中常被忽略 |
| 确认号 | 4字节 | 滑动窗口的基础,但延迟确认会害死人 |
| 标志位 | 9位 | SYN、FIN、ACK等,握手挥手全靠它 |
| 窗口大小 | 2字节 | 流量控制的核心,HFT中要尽量放大 |
嗯,这里要注意的是选项字段。TCP头部可以扩展,比如时间戳、窗口缩放因子等。这些选项虽然能提升性能,但也会增加头部长度。我在项目中遇到过,有些网卡对TCP选项处理很慢,导致延迟飙升。所以,HFT场景下,尽量保持TCP头部最小化。
4.2 三次握手:建立连接的“代价”
三次握手,说白了就是“你准备好了吗?”、“我准备好了!”、“好的,开始发数据”。这个过程需要1.5个RTT(往返时间)。在HFT中,1.5个RTT意味着什么?意味着你可能已经错过了一波行情。
为什么会这样?因为三次握手是TCP可靠性的基石。客户端发SYN,服务端回SYN+ACK,客户端再回ACK。每一步都确认了对方的接收能力。但问题是,这个确认过程太慢了。
我建议,在HFT中,尽量复用连接,而不是频繁建立新连接。如果你必须频繁建立连接,可以考虑使用TCP快速打开(TFO),它能在SYN包中携带数据,减少一次RTT。不过,TFO的普及率不高,很多中间设备会丢弃它。
4.3 四次挥手:关闭连接的“拖沓”
四次挥手比三次握手更复杂。主动关闭方发FIN,被动方回ACK,被动方再发FIN,主动方回ACK。这个过程需要2个RTT。而且,主动关闭方还会进入TIME_WAIT状态,等待2MSL(最大报文生存时间)才能彻底关闭。
在HFT中,TIME_WAIT是个大问题。为什么?因为端口资源是有限的。如果你频繁关闭连接,端口会被占用,导致无法建立新连接。我记得有一次,一个交易系统的端口被耗尽,就是因为TIME_WAIT太多。
怎么解决?尽量使用长连接,或者调整系统参数,缩短TIME_WAIT时间。但要注意,缩短TIME_WAIT可能导致旧数据干扰新连接。嗯,这里要权衡。
4.4 滑动窗口与拥塞控制:HFT的“紧箍咒”
滑动窗口是TCP流量控制的机制。接收方告诉发送方:“我还能收多少数据”。发送方根据这个窗口大小发送数据。拥塞控制则是网络层面的,发送方根据网络拥塞程度调整发送速率。
这两个机制,在HFT中都是“紧箍咒”。为什么?因为它们会主动降低发送速率。比如,拥塞控制中的慢启动,一开始只能发一个报文,然后指数增长。这在HFT中是不可接受的,因为我们需要第一时间把数据发出去。
我个人的经验是,在HFT中,尽量禁用拥塞控制。怎么做?可以设置TCP_CONGESTION为“bbr”或“htcp”,这些算法对延迟更友好。或者,干脆使用UDP,自己实现简单的流量控制。
4.5 Nagle算法与延迟确认:HFT的“头号公敌”
Nagle算法,说白了就是“攒够了再发”。它会把多个小数据包合并成一个大数据包,减少网络开销。但问题是,HFT需要的是低延迟,而不是高吞吐。你攒数据的时间,可能已经错过了交易机会。
延迟确认也是类似的。接收方不会立即发送ACK,而是等待一段时间,看看有没有数据要一起发。这会导致发送方收不到ACK,无法继续发送数据。
为什么HFT要禁用它们?因为它们引入了不确定的延迟。Nagle算法可能等待200ms,延迟确认可能等待100ms。这些时间在HFT中是不可接受的。
我曾经在一个项目中,发现交易延迟忽高忽低。排查了半天,发现是Nagle算法在作祟。关闭后,延迟立刻稳定下来。
4.6 TCP_NODELAY与快速ACK:HFT的“救星”
TCP_NODELAY是Nagle算法的克星。设置了这个选项,TCP就不会攒数据,而是立即发送。说白了,就是告诉TCP:“别等了,有数据就发”。
快速ACK则是延迟确认的克星。它让接收方立即发送ACK,不等待。这样,发送方就能及时收到确认,继续发送数据。
在HFT中,这两个选项是标配。我建议,所有HFT应用都必须设置TCP_NODELAY。至于快速ACK,可以通过设置TCP_QUICKACK选项来实现。但要注意,快速ACK会增加网络开销,因为ACK包变多了。
- 设置TCP_NODELAY,禁用Nagle算法
- 设置TCP_QUICKACK,启用快速ACK
- 放大TCP窗口,避免窗口太小导致发送阻塞
- 使用长连接,避免频繁握手和挥手
知识体系图:TCP在HFT中的关键点
好了,这一章的内容就到这里。TCP协议在HFT中是个双刃剑,用好了能保证数据可靠,用不好就是性能杀手。下一章我们会深入UDP,看看它为什么更适合HFT。