1. 为什么TCP会“不够用”:多路径的起点是痛点
先说一个我经常在培训时抛给学员的问题:一条TCP连接,能不能同时走两条网线?答案听起来有点反直觉——不能。标准TCP连接的四元组(源IP、源端口、目的IP、目的端口)一旦确定,数据就只能在这唯一一条路径上传输。哪怕你的手机同时开着Wi-Fi和5G,服务器上明明挂着两块千兆网卡,一条TCP流也只会霸占其中一条链路,另一条闲着。
这个限制在早年不算什么大问题,但现在越来越难受。视频会议卡顿、大文件同步慢、跨机房数据迁移要等半天,很多时候不是带宽不够,而是单条路径没法把多路资源都用起来。于是就有了MPTCP(Multipath TCP,多路径TCP)。它做的事情用一个比喻就能说清:普通TCP是一条单腿走路的连接,MPTCP让这条连接长出多条腿——每条腿对应一条独立的TCP子流,可以走不同的网卡、不同的IP、甚至不同的运营商网络,但应用层看到的仍然是一条普通的TCP连接。
MPTCP握手的本质,就是这套“长腿机制”的起点。在第一条子流建立时,双方通过TCP选项完成能力协商和密钥交换;之后新子流加入时,又有一套独立的握手流程。这两个握手阶段是理解MPTCP全部行为的钥匙——包括它怎么确保兼容普通TCP、怎么防止会话被劫持、怎么在多条路径上保证数据不乱序。这篇文章我会从标准三次握手出发,一层层拆解MPTCP是如何扩展它的,并结合Linux内核实测和Wireshark抓包,把整个握手过程彻底讲透。适合对TCP有一定基础、想深入理解MPTCP原理,或者正准备在实验室里跑MPTCP测试的人。
1.1 单条连接的天花板,以及MPTCP为什么能突破它
先看一个实际例子。假设你有一台双网卡服务器,分别接在电信和联通的线路上,各自带宽都是100Mbps。客户端也从两条线路发起访问。如果走标准TCP,一条连接只会被分配到其中一个IP和端口组合上,所以这条连接最多跑满100Mbps。除非应用层自己拆成多条连接,否则200Mbps的总带宽跟你没什么关系。
MPTCP的思路完全不同。它不改变应用层接口——你打开一个socket,connect,read/write,和使用普通TCP的代码一模一样。但内核里的MPTCP协议栈会把这个socket拆成多个子流(subflow),每个子流是完整的TCP连接,拥有自己的四元组。这样,一条MPTCP连接就可以同时占用电信和联通两条链路,理论吞吐上限接近两者之和。
这套机制要工作,首先就得解决一个问题:两个通信端点怎么知道对方支持MPTCP?如果不知道,就退化成普通TCP,保证兼容性。这就是MPTCP握手要做的第一件事。我在实验室里第一次跑通MPTCP时,第一反应是“这玩意儿的手续比TCP多好多”,但每一条消息都不是白加的,后面逐个拆给你看。
1.2 三个容易混淆的概念:MPTCP连接、子流、TCP连接
很多人看MPTCP文档会蒙圈,因为里面同时出现了Connection、Subflow、TCP Connection三个词。这里必须先厘清。
MPTCP连接是上层看到的逻辑连接,对应应用程序的一个socket,有一条统一的字节流。子流是底层真正跑数据的TCP连接,每个子流都有自己的序列号空间、自己的拥塞控制状态,可以随时建立和关闭。而TCP连接就是大家熟悉的标准TCP连接,一个子流本质上就是一个普通TCP连接,只是多了MPTCP选项。
这三个概念之间的关系可以想象成“总公司—分公司”的关系。MPTCP连接是总公司,负责把应用层的数据流拆成一段段分配给各分公司;每个分公司(子流)是独立的TCP连接,自己负责可靠传输;但它们都受总公司统一调度。握手的第一阶段——MP_CAPABLE协商——就是在确认“你我之间要不要成立这个总公司”;第二阶段——MP_JOIN——是给总公司新增分公司(新腿)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 第一阶段握手:MP_CAPABLE如何把三次握手变成“连接骨架”
好消息是,MPTCP不需要发明一套全新的建连协议,它直接复用TCP三次握手,只是在SYN、SYN-ACK、ACK这三个报文里塞了一个特殊TCP选项——MP_CAPABLE。所以从网络抓包看,它依然遵循三次握手的时序,只是每个包都“胖”了一点。
2.1 三次握手还是那三次,但选项变了
TCP有个很优雅的设计叫“选项字段”(TCP Options),它位于TCP头部末尾,最多40字节。MPTCP就是靠在这里面注册了自己的私有选项,type编号是30。普通路由器、交换机不认识这个选项也没关系——它们按TCP原样转发就行;不支持MPTCP的对端则会直接忽略这个选项,双方自然就按普通TCP通信。
这样设计的高明之处在于:MPTCP的能力协商是“渐进增强”的。不需要额外端口,不需要协议栈之外的任何配合,只要两端都开启了MPTCP,握手时就都能识别出对方带着MP_CAPABLE选项,于是这条连接自动升级为MPTCP连接。我经常跟人说,MPTCP的兼容性思路就是“在别人家大楼的电梯里多加了一个楼层按钮,按不按取决于双方,但电梯还是那部电梯”。
2.2 第一次SYN:带着“钥匙”来敲门
MPTCP握手的第一个关键动作,发生在客户端发出的SYN报文里。除了标准的TCP头部,客户端会在MP_CAPABLE选项里放入自己随机生成的8字节Key,业内常叫Sender Key。为什么要放Key?这跟MPTCP的身份认证体系有关——后续所有子流的加入,都需要通过密钥来验证“你确实属于这条MPTCP连接”,防止第三方往连接里塞假子流。这个Key就是整条MPTCP连接的主密钥。
从报文层级看,客户端发出的SYN中MP_CAPABLE选项大概长这样(以RFC 8684描述的v1版本为例):
code复制TCP Option - Multipath TCP
Kind: 30
Length: 12
Subtype: 0 (MP_CAPABLE)
Version: 1
Flags: 0x00
Sender Key: 8e2f6c1a9b3d4e5f
注意这里只有发送方自己的Key,长度是8字节;选项总长度12字节,多出的字节用于对齐TCP选项。内核会保存这个Key,等收到对方答复后,双方就共同持有两个Key。
