TCP 老司机遭遇新对手

HTTP/3 的新车当面拐进 UDP 通道,TCP 老司机气得追了三条街:“不可靠的路也敢运网页?”更气人的是,QUIC 不但没丢货,还在断网换路后扬长而去……

分享
TCP 老司机遭遇新对手

谁抢了 TCP 的乘客

本文承接上一篇:《Cookie 的行李检查》

“站住——”

TCP 老司机把喇叭按到底,驾驶着蓝色运输车冲出站台。

前方,一辆挂着 HTTP/3 货牌的银色新车刚刚变道。它没驶入 TCP 专用隧道,反而一甩车尾,钻进了隔壁的 UDP 通道。

TCP 老司机差点把方向盘掰下来。

“HTTP 运货不坐我的车,去坐 UDP?”

副驾驶上的实习生死死抓住安全带:“师傅,前面限速!”

“它都抢生意了,还限什么速!”

“限咱们的速……”

话音刚落,蓝色运输车一头冲进减速带。

“咣!”

老司机和实习生同时弹了起来。咖啡也飞出了杯子,在车顶绕了一圈,竟然一滴不剩地落了回去。

实习生看呆了:“师傅,好技术。”

“开了这么多年,靠的就是稳定。”

前方的新车终于停下。一个银灰色的身影跳下驾驶室,掸了掸夹克,胸前的 Q 在隧道灯下闪了一下。

“找我?”

TCP 老司机推门下车,把那张名片拍在引擎盖上。

QUIC

“就是你把 HTTP/3 带进 UDP 通道的?”

“对。”

“UDP 可不负责可靠送达。包丢了,它不替你重发;顺序乱了,它也不替你整理。”

TCP 老司机指着满车货物:“拿网页资源走这种路,出了事谁负责?”

QUIC 拉开夹克。

里面挂着确认单、重传器、拥塞控制仪、流量控制阀,还有一整排加密钥匙。

“我负责。”

TCP 老司机沉默了两秒。

“你出门为什么穿工具墙?”

“主要为了等这句。”

一张丢失的货单

浏览器老大闻讯赶到时,两辆车已经堵住了运输大厅。

“先熄火。” 浏览器老大看看 TCP,又看看 QUIC,“谁再按喇叭,谁去 Cookie 大厅扫地。”

两只手同时离开了方向盘。

“到底怎么回事?”

TCP 老司机抢先开口:“从 《Chrome 的秘密实验》开始,HTTP/2 就一直坐我的车。”

“它把数据拆成帧,让多条 Stream 共用一条连接。这些年合作得好好的,HTTP/3 凭什么突然换车?”

“就因为那条连接。” QUIC 说。

“我的连接怎么了?”

QUIC 没回答,只把众人带进事故演练场。

场内只有一条 TCP 运输隧道。三组货物挤在同一条连接里:橙色方块装网页,绿色方块装样式,紫色方块装图片。

每块货物都被切成小段,沿着同一条传送带快速滑行。

网页流:1  2  3  4
样式流:1  2  3  4
图片流:1  2  3  4

“看见没?HTTP/2 可以把不同 Stream 的数据帧交错着送进来。”

TCP 老司机挺起胸口:“哪个响应先准备好,就先送哪个。不必像 HTTP/1.1 管道化那样,让完整的响应一个跟着一个排队。”

QUIC 按下演练按钮。

“啪!”

一只装着网页数据的方块掉进了轨道缝隙。

后面的绿色与紫色方块明明已经到达出口,闸门却“哐”地落下,把它们全拦在里面。

样式流急得拍门:“丢的是橙色那块,关我什么事?”

图片流也在后面喊:“紫色全到齐了!先放我出去!”

TCP 老司机挡在闸门前:“不行。我答应过上层,交出去的字节必须完整,而且顺序不能乱。前面缺了一块,就得先把它找回来。后面的货再早到,也不能越过它出门。”

“问题就在这里。” QUIC 指着越排越长的队伍,“HTTP/2 虽然让多条 Stream 不必在 HTTP 那一层排成一队,可到了 TCP 这里,它们还是汇进了同一条有序字节流。”

QUIC 敲了敲落下的闸门:“只要前面丢了一块,后面的 Stream 就可能跟着一起等。”

TCP 老司机脸色沉了下来:“网络丢包,怎么能怪我?我是在保证数据有序。”

“没怪你。” QUIC 说,“这是你的承诺,也是你的限制。”

“你刚才的语气就像在怪我。”

“那是我的性格限制。”

浏览器老大赶紧站到两辆车中间:“讨论机制,禁止讨论性格。”

UDP 只借了一扇门

QUIC 打开另一座演练场。

这次,大厅里并排亮起三条轨道,每条都有自己的编号。网页走一条,样式走一条,图片走一条。它们各自属于一条 QUIC Stream。

银色新车从底部的 UDP 通道驶入。

TCP 老司机抱起胳膊:“所以 UDP 替你保证三条轨道可靠?”

“没有。UDP 只帮我收发一份份数据报。剩下的运输规矩,都是我在它上面重新建的。”

“有连接?”

“我自己建。”

“确认?”

“我自己记。”

“丢包重传?”

“我自己做。”

“流量控制和拥塞控制?”

“也做。”

“有序交付?”

“每条 Stream 内可靠、有序。不同 Stream 之间不强求交付顺序。”

TCP 老司机越问,眉毛挑得越高:“你这不就是把我的活重新做了一遍?”

QUIC 想了想:“不能说完全一样。”

“哪里不一样?”

“工服比较新。”

TCP 老司机撸起袖子。

浏览器老大敲响警钟:“禁止讨论工服!”

QUIC 再次按下按钮。

网页轨道上的第二块货物突然消失。这条 Stream 立刻亮起红灯,停下来等待重传。

可旁边两条轨道没有关门。

样式流从出口滑了出去。

图片流紧随其后,还朝网页流挥了挥手:“出口等你!”

“丢掉的货还是要补,这条 Stream 也得等。” QUIC 说,“但另外两条 Stream 不缺货,没必要陪它一起关门。”

实习生举手:“所以 HTTP/3 自己负责多路复用?”

“这次是 QUIC 直接提供多条 Stream。” QUIC 指向三条轨道,“HTTP/3 把不同的请求和响应放到对应的 Stream 上,不必再把它们全部塞进一条 TCP 字节流。”

“那是不是一条 Stream 丢包,绝对不会影响任何其他 Stream?”

QUIC 立刻摇头。

“别把话说满。几条 Stream 还是共用同一条 QUIC 连接,也走同一条网络。路上频繁丢包,整条连接都可能减速;要是连接本身的控制信息出了问题,影响也不只一条 Stream。”

QUIC 在黑板上画了一个圈:“我绕开的是 TCP 有序字节流造成的跨 Stream 排队,不是把所有堵车都消灭了。”

实习生认真记下:

受伤的车道单独等,不等于整条高速永不堵车。

TCP 老司机瞄了一眼笔记:“这句还算严谨。”

“师傅夸我了!”

“夸的是句子。”

握手室里少了一张椅子

“独立 Stream 算你有点本事。” TCP 老司机绕着银色新车走了一圈,“可正式运货前,总得先建立连接,再用 TLS 把加密手续办好。这两趟流程,你总躲不掉吧?”

QUIC 推开握手室的门。

房间里只有一张桌子。

桌子左边放着 QUIC 的连接资料,右边放着 TLS 1.3 的加密材料。负责连接和加密的工作人员坐到了一起。

TCP 老司机探头往里看:“TLS 怎么坐到你的桌上了?”

“我把 TLS 1.3 带进了自己的握手流程。” QUIC 拉开一把椅子,“建立新连接时,连接参数和加密手续可以一起谈。网络顺利的话,一次往返后,双方就能开始传受保护的数据。”

HTTP/2 也能使用 TLS 1.3。” TCP 老司机敲敲门框,“别把 TLS 的功劳全穿在自己身上。”

“没抢。” QUIC 说,“HTTP/2 使用 TLS 时,要先等 TCP 把连接建好,TLS 才能接着握手。到了我这里,两件事可以一起推进,少排一次队。”

浏览器老大翻了翻值班记录:“再次访问见过的服务器呢?”

QUIC 从抽屉里拿出一张旧车票。

“如果客户端以前来过,手里还留着服务器认可的恢复凭证,就可以尝试 0-RTT。” QUIC 晃了晃旧车票,“第一批数据和握手消息一起出发,不必先在门口等完一次往返。”

实习生眼睛一亮,抓起一张转账单就往车里塞。

QUIC 反手把单子抽了出来。

“这个不行。”

“不是零等待吗?”

0-RTT 的货有可能被人复制,再送一次。服务器就可能把同一份请求处理两遍。”

QUIC 把转账单拍回实习生手里:“读取信息这类可以安全重复的操作,还能根据规则考虑一下。转账、下单这种办两次就要出事的请求,不能为了快硬塞进来。”

“而且服务器可能拒绝 0-RTT。” 浏览器老大补充,“它是一项有条件的恢复能力,不是每趟车都能免检。”

实习生默默把转账单塞回碎纸机。

“刚从 Cookie 大厅拖走一辆转账车,差点又送回来。”

Wi-Fi 突然断了

演练刚结束,运输大厅的灯闪了两下。

广播急促响起:

“网络切换!Wi-Fi 信号中断,启用移动网络!”

TCP 老司机的蓝色车猛地一震,仪表盘上的连接地址全部变红。

“IP 地址变了!” 实习生盯着连接登记牌,“出发地址和端口都换了,原来的 TCP 连接认不出这条新路!”

“重新建连接。” TCP 老司机立刻抓起 SYN 工具箱,“快!”

旁边的银色新车却只晃了一下,随后驶进移动网络通道。

TCP 老司机愣住:“它怎么没散架?”

QUIC 从窗口伸出一张号码牌。

Connection ID

“我不只靠 IP 地址和端口认连接,还会看这张连接 ID。” QUIC 晃了晃号码牌,“从 Wi-Fi 换到移动网络后,我会先验证新路。验证通过,原来的连接就有机会继续跑,不必从头再来。”

TCP 老司机开着刚重连的车追上来:“有机会?”

“当然,新路必须先验证,迁移也可能失败。服务器得支持,沿途网络不能拦,客户端也得允许。不是把 Connection ID 挂上车头就能穿墙。”

实习生凑近号码牌:“一直用同一个 ID,旁边的人不就能认出这是同一辆车?”

“问得好。” QUIC 点点头,“要是换了路还一直举着同一块号码牌,旁人就可能把前后两段行程认出来。为了减少这种关联,通信双方可以准备新的 Connection ID,换路时轮换使用。”

QUIC 收起号码牌:“连接可以延续,不代表号码永远不换。”

TCP 老司机看了看自己刚填完的一沓重连表,又看了看已经开远的银色车。

“这一局,算你快。”

“只算这一局?”

“年轻车不要得寸进尺。”

TCP 把辞职信拍在桌上

回到运输大厅,TCP 老司机一言不发地走进办公室。

五分钟后,一封辞职信“啪”地拍在浏览器老大的桌上。

离职原因:部门已有新欢。

浏览器老大看完,把信转了个方向。

“你拿反了。”

TCP 老司机又转回来:“情绪到了,方向不重要。”

“谁说 QUIC 来了,TCP 就要退休?”

HTTP/3 都不坐我的车了。”

HTTP/1.1HTTP/2 还在坐你的车,其他协议也照样需要 TCP。”

浏览器老大敲敲辞职信:“HTTP/3 只是换了底下的运输方式。HTTP 的请求方法、状态码和字段都还在,哪有把整个互联网一夜改成 UDP?”

“而且客户端和服务器都得支持 HTTP/3,沿途也不能拦住 QUIC 使用的 UDP 通信。”

浏览器老大把辞职信推了回去:“新线路谈不成,或者半路走不通,浏览器仍然可以改用 HTTP/2HTTP/1.1。”

TCP 老司机瞥向门外:“所以它也有走不通的时候?”

“当然。” QUIC 靠在门边,“我只是新协议,不是许愿池。”

“那你刚才还挺神气。”

“主要为了等辞职信。”

TCP 老司机第二次撸起袖子。

浏览器老大第二次敲响警钟:“禁止讨论辞职信!”

新线路第一次发车

争吵最终被一张真正的发车单打断。

HTTP 抱着三只货箱冲进大厅。

“网页、样式、图片都准备好了。服务器支持 HTTP/3,谁送?”

QUIC 拉开车门。

HTTP 却没有立刻上车,而是先看向 TCP 老司机。

老司机靠在蓝色车头旁,擦了擦那块已经很亮的车标。

“看我干什么?新线路该跑就跑。”

“不拦了?”

“运输不看谁资历老,只看这趟货适合走哪条路。”

TCP 老司机把最后一只箱子递给 QUIC:“不过先说好,确认、重传、拥塞控制、流量控制,一样都不能少。UDP 不替你兜底,砸了招牌算你的。”

QUIC 接过箱子,收起了刚才的玩笑。

“明白。”

三条 Stream 同时亮起。不同的 HTTP 请求各自进入轨道。连接和加密手续也在同一张桌上快速推进。

银色新车驶入 UDP 通道,车身上的 Connection ID 在隧道里闪过一道冷光。

TCP 实习生望着远去的车尾:“师傅,QUIC 是不是来替代我们的?”

老司机端起重新泡好的咖啡。

“不是谁替代谁,是多了一种选择。” 老司机看着隧道,“QUIC 把可靠运输、加密和多条 Stream 放进了一套新系统。这样连接更快,也能在换网时尝试保住原连接。”

老司机喝了一口咖啡:“当然,东西都装到一起了,实现和部署也不会简单。”

“那以后是对手,还是同事?”

隧道另一头,QUIC 按了两下喇叭。

TCP 老司机也按了两下。

“白天是同事。”

“晚上呢?”

“赛道见。”