当我们说"TCP是可靠协议"时,我们真正表达的是什么

本文深入解析TCP协议如何在不可靠的介质上构建可靠通信,从两军问题出发,阐述超时重传、指数退避等核心机制,以及TCP三次握手背后的真正原因。

很明确地说,从通信意义上推敲,TCP一点都不可靠。一个抽象的协议,怎么可能左右介质来保证可靠,不存在的。但凡是经由某种介质的通信行为均不可能是绝对可靠的!

正好比我们现实生活中的保险,其实它什么都不能阻止,什么风险也保证不了它的不发生,它保证不了飞机不会掉下来,也无法阻止人生病…事实上,TCP就是通信中的保险业。

TCP是如何设计出来的?推而广之这类通信协议是如何设计出来的?如果说让你在一个不可靠的介质上运行一个可靠的协议,你该怎么做?本文将介绍内中的些许因果。

可靠的通信协议如何构建

这要从经典的两军问题说起。

首先介绍一下两军问题,来自Wiki的解说是最好的: 
Two Generals’ Problemhttps://en.wikipedia.org/wiki/Two_Generals%27_Problem 
两军问题本质上一个一致性确认问题,也就是说通信双方而不是一方(这对理解TCP非常重要)都要确保信息的一致性。即假设通信双方为A和B,那么A发送一则消息M给B,所谓的可靠性则是要同时满足下面的条件:

  • 信道是不可靠的,任何消息均可能以任何概率丢失
  • 如果AA或者BB不能确保消息到达对方时,不能重发消息 
    这一点非常重要,在经典的两军问题中,消息是由信使传递的,而信使是人,人是军队作战的最重要资源也是最不可靠的资源,比如会叛变…因此每条消息或者确认相互只能派遣一个信使去递送消息,在通信上讲,就是消息不能重发!
  • 对于A而言,要确保A知道B已经收到了M
  • 对于B而言,要确保自己收到M这件事已经被A知道

数学上很容易用反证法证明上述的两军问题是根本无解,即一致性通信的完全可靠性是一种奢望。下面我来试着推导一下。

假设在时间点TnTn,AA和BB两端达到了信息的完全一致性,所有交互的信息包按照时间顺序分别为M0M0,M1M1,M2M2,…MnMn,为了达到一致性,这些数据包是缺一不可的。现在来观察最后的一个数据包MnMn的传输,我们知道,信道是不可靠的,所以它可能会丢失,而它一旦丢失,整个交互过程便失去了一致性,这与假设是矛盾的,所以,一致性是不可能的


这个问题貌似彻底拆了通信技术的根基,那么通信技术还有什么意义呢?

事实上,

  • 首先,通信协议从来都不是为了满足完全的一致性需求

通信的意义是,在时间序列上满足消息传递的单向完成需求即可!通信的本质问题是确保消息传递,而不是维护一致性,一致性应该由业务自身来负责,通信仅仅提供消息传递的基础设施而已。

  • 其次,通信传输的是字节电脉冲,消息可以重发

这便大大削弱了两军问题的强约束。基于上述的假设,我们来一步步地推导出TCP协议为什么要这么设计。

如果仔细推敲的话,你会发现,即便是消息传递,在数学上也是无法确保在不可靠的信道上确保消息传递的,然而,我们换个思路,即自问“信道到底不可靠到什么程度?”

是100%不可靠吗?如果是的话,意味着断路,即双方是不可达的,无论我们发送多少次数据包,均会丢失,这样我们马上可以结束这个没有意义的讨论,因此,所谓的不可靠只是说信道会出现概率性丢包,丢包概率pp一定是介于开区间(0,1)(0,1)之间的!

这个意义十分重大,这意味着,只要我们重试特定消息MnMn的次数足够多,就一定能收到来自对端针对消息MnMn的确认!,这是完全确定的一个结论,没人反对吧。

这边自然而然导出了可靠通信的第一个原则:

  • 1.超时重传

该原则可以确保消息一定能有机会到达对端。每当发出一个数据包,在预期的时间内没有确认到达,就重传它。关于超时重传的细节,本文稍后会浅谈一下,但是现在,我们来看另外一个问题。


如何确保消息单向传递的完成?

换句话说,所谓消息单向传递的完成,即需要一种标志性的信号*,该信号揭示了消息已经被对端接收这个事实,很显然,对端发送针对特定消息的确认并且本端收到即可。

一旦AA收到了来自BB针对MnMn的确认,对于AA而言,它知道BB肯定收到了MnMn,而对于BB而言,它也确实收到了MnMn,不然它也不会发送确认。但是由上文可知,这个确认在不可靠的信道上也可能丢失,不过这不必惊慌,因为我们已经有了推论,即针对任意消息,只要我们重复传输的次数足够多,该消息就一定能到达对端,在该推论下,采用超时重传原则即可。

现在看来,我们导出的下列措施已经解决了几乎所有问题: 
1. 针对消息MnMn的超时重传机制 
2. 针对消息MnMn的确认AMnAMn的超时重传机制

但是这是最优解吗?

非也!这只是一种可行的方案,但不是唯一的方案,更不是最有的方案。导出最优解需要我们深入到通信网络的本质,先看一篇文章: 
马太效应/幂律分布的本质以及其数学表述https://blog.youkuaiyun.com/dog250/article/details/79146511 
注意,我们的通信网络是一个网状拓扑的连通图,无论是单节点连接数属性还是流量属性均符合幂律规律,从双对数坐标曲线可以看出网络规模和节点的各属性特征之间的对数线性关系,而网络规模来自于某种指数级增长的复制,单节点的属性特征来自于该节点的行为,很显然,在这个双对数坐标下线性的通信网络中,如果想等比例地缩放其规模而不至于崩溃,就必须用指数来控制单节点的行为(把双对数坐标化为笛卡尔坐标即可展现)。

实际上,我们把双对数坐标中的直线(求解微分方程的结果)展开到相应的笛卡尔坐标系,就是一条指数规律的曲线了。

再看另一个抽象,即如果数据包在传输过程中丢失了,这件事跟什么因素相关?诚然,在网络通信中,这件事肯定有可能是和传输介质相关的,但是在节点数量,即网络规模这个因素下,介质的问题可以忽略不计。也就是说,节点越多,传输越容易发生冲突,数据也就越不容易到达对端。即丢包事件和网络规模相关,网络是一个线性系统,所以,丢包的重传必须具备指数级的时间特征。

介质的问题随着网络规模的扩大是线性增长的,而传输冲突的问题随着网络规模的扩大则是指数级的,孰重孰轻,立判!

如果你了解早期的以太网,即总线式的CSMA/CD以太网,你会发现同样的事实。

因此,很明确,超时重传的超时规则在线性系统的平衡通过指数特性的单独节点行为来维持的原则下,则必须是:

  • 2.超时重传-指数退避

有了这个原则,我们再回过头来看如何实现消息以及消息确认的超时重传。直接说结论,即不对确认进行重传,因为确认和消息本身属于同一个行为,针对消息本身的超时重传已经自动包含了一个确认,如果再针对确认进行重传,就会破坏单点行为的指数特征,因此我们导出可靠通信的第三个特征:

  • 3.不对确认进行超时重传

由于我们仅仅想确保消息单向传递的可靠,即确保对端收到了本端发出的消息而无需让对端知道这件事,第四个特征也随即导出:

  • 4.不对确认进行确认

基础设施构建就此完毕,考虑到通信往往是双向的,我们需要在其上构建一个可靠的双向通信协议,怎么办?

简单,在另一端BB重新这么来一遍即可!于是我们观察到,两军问题如果超时重传的前提下将双向的消息传递和确认分解成两个单向的消息传递和确认,事情就会简单得多。

原始的两军问题解法: 
这里写图片描述

转换后的解法: 
这里写图片描述

嗯,转换后的解法,即我们熟悉的协议,TCP协议的最基本形式。现在进入TCP时间!


TCP握手,挥手,一致性的问题

经常有人问,TCP为什么是3次握手,而不是2次,也不是4次,5次。知乎上经常会有这种问题,但是答案几乎是千篇一律的错误或者答非所问,最常见的答案只是描述一下TCP握手的细节,然后导出这么做是OK的,其实不这么做也是OK的这一点没人提。

最常见的错误答案: 
1. 这是一种权衡,因为无数次握手也不可能完全可靠; 
2. 描述握手的协议细节; 
3. …

看过了我上面的论述,这个问题应该非常好答了,所谓的TCP建立连接的握手,实质上就是建立一个双向的可靠通信连接,一边一个来回,每一边都自带超时重传来确保可靠性(而不是靠握手的次数)。TCP的3次握手是优化的结果,其实它应该是4次握手,由于是从零开始的建立连接,因此将SYN的ACK以及被动打开的SYN合并成了一个SYN-ACK,仅此而已。

握手的作用,旨在确定两个双向的初始序列号,TCP用序列号来编址传输的字节,由于是两个方向的连接,所以需要两个序列号,握手过程不传输任何字节,仅仅确定初始序列号: 
这里写图片描述

说完了3次握手,那么,其姊妹问题,为什么TCP的断链是4次挥手而不是3次?

换句话说,即是在问为什么针对主动断开方的FIN的ACK以及本端的FIN不能合并?

非常简单,因为TCP是在一个单向可靠通信系统基础上构建而成的双向传输控制协议,握手期间可以合并ACK和SYN,是因为在握手之前两端没有任何连接上的包袱,而在断链挥手时,一端认为可以断开了,另一端却不一定,可能另一端还有数据要传输,所以便不能合并,被动关闭的一方只能单独处理针对FIN的ACK以及自己的FIN,仅此而已。

这里写图片描述

再来一个问题,TCP能确保一致性吗?换句话说,TCP协议是两军问题的一个解吗?

远远不是!TCP并不确保一致性。

任何时间点,TCP都不能完全确认当前时刻连接双方的状态,此处所谓的状态包括两端传输的数据。一致性是基于消息的,而不是基于连接的!也就是说,TCP只有收到下一个数据包时,才知道上一个数据包的接收情况,而无法实现隔空打人!TCP的好处仅在于,它在一个信息流上实现了一个一致性确认的流水线方式

我们在理解这个流水线方式的时候,不应该考虑滑动窗口,那样会比较难以理解,我们应该仅仅考虑单字节停等机制。事实上也确实是这样,滑动窗口机制只是为流量控制而引入的,单字节停等效率又太低,所以说这并无伤大雅,你把字节换成窗口即可,即单窗口停等。

如果我们把一致性推广到连接的层面,在连接层面,一致性就是靠4次挥手保证的。

我们可以看到,4次挥手那里的状态机非常之复杂,这是有原因的,即便是引入了TIMEWAIT状态,也还是没有办法保证彻底的一致性,这是两军问题本质上不可解的一个结论,仅此而已。


1974年的TCP

现在你应该大致知道TCP如何保证可靠性了,进一步,如果你想知道TCP协议的头部为什么是那个样子,这一切是如何安排的,你就不得不去读一下一篇陈年的论文: 
《A Protocol for Packet Network Intercommunication》https://www.cs.princeton.edu/courses/archive/fall08/cos561/papers/cerf74.pdf 
我来大致介绍一下这篇划时代的论文。

毫不夸张地说,该论文奠定了以TCP/IP为核心的互联网的基础,我们今天能刷抖音,用微信聊天,能在线看片…这一切要不是这篇论文,不会是现在这个样子。

该论文的重点不是TCP协议,而是TCP/IP作为一个整体如何发挥作用,早在1974年,分层模型还不算太成熟,所以当我们提起TCP/IP的时候,要明白,最初的时候,这两个协议是牢牢切合在一起的,到了后来为了兼容纯IP转发,才加入了UDP,这个时候,人们意识到分层模型的必要性。于是抽象而成的ISO/OSI模型。

该论文主要有两个论题:

  • 网关的概念和意义–最终的IP协议
  • 进程间通信的传输控制–最终的TCP协议

注意,我们看看TCP最初的形式,没错,它是作为一种进程间通信的手段被提出的,当初TCP作为进程间通信手段,侧重于不同主机的进程间通信,因此,我们可以清晰看到它的API和文件IO的API是多么相似,这也是socket可以作为文件描述符的原因。

此外,还有值得注意的是,TCP的ACK号被定义为下一个索要字节的序列号,这在当时实现了一种简易且完备的字节流水线,节省了协议头空间,看到这个设计,简直太帅!虽然它也带来了很多问题,比如无法精确测准RTT,比如无法进行选择确认,进而无法进行良好的拥塞控制,但不得不说,在空间重于时间的1970年代,这绝对是创举,毕竟,拥塞控制在当时是没有意义的,1988年才被引入。


1974年的互联网

在1974年那篇论文之后,同样的作者归纳总结出了RFC675: 
《RFC675:SPECIFICATION OF INTERNET TRANSMISSION CONTROL PROGRAM》https://tools.ietf.org/html/rfc675 
这篇划时代的RFC正式提出了互联网这个概念,我们常说的Internet就是Internetworking的缩写。

TCP/IP协议确实不是一个协议栈,最初它们只是一个协议,仅此而已,不多说。 


那么,接下来?

接下来,skinshoe wu来了,携带着他的高级皮鞋,还有高级西装。

源址:https://blog.youkuaiyun.com/dog250/article/details/82177299

<think>嗯,用户这次的问题很明确,是在我之前详细解释TCP头部字段的基础上,进一步询问TCP实现可靠传输的机制以及流量控制和拥塞控制的原理。看来用户对网络协议有系统性的学习需求,可能是学生或IT从业者。 用户提供的站内引用很有价值,特别是[^4]和[^5]直接关联到可靠传输机制。我需要重点整合这些内容:引用[^5]提到的确认应答机制是可靠传输的基础,而引用[^4]列举的重传机制、滑动窗口等正是用户需要的具体实现方式。有趣的是,用户特意强调"不是真正需求",明ta更关注技术实质而非引用本身。 关于可靠传输部分,我决定从三个核心机制展开:首先是确认应答和序列号这个基础组合(需强调32位序列号的环回处理),其次是各种重传机制的区别(特别是SACK这种高级特性),最后是滑动窗口如何提升效率。这里要特别注意用数学表达清晰明窗口移动,比如$S_{nd}$和$R_{cv}.NXT$的公式。 流量控制方面,引用[^2]提到的接收窗动态调整是关键,需要解释rwnd和通告窗口的关系,还要带出糊涂窗口综合症这个经典问题——毕竟用户既然问到"如何实现",就应该包括异常处理。 拥塞控制反而是最复杂的部分。引用[^3]指出网络中间环节的影响,这正好引出拥塞控制的必要性。我打算用四阶段模型明(慢启动/避免/发生/恢复),重点标注$cwnd$的指数增长和线性增长分界点($ssthresh$)。用户提供的引用里没有具体算法,但结合经典Reno算法补充快速恢复的细节会更有价值。 最后考虑设计几个延伸问题:拥塞控制算法演进(如BBR)可能最贴近技术前沿,而可靠性机制对比能帮用户建立知识体系。不过要控制问题数量,三个刚刚好——毕竟用户上次的回复也保持这个节奏。 对了,所有数学表达必须严格遵循LaTeX格式,比如窗口公式要写成$W_{通告}=min(rwnd, cwnd)$。技术细节上要确认几个关键点:SACK选项在头部的位置,以及延迟确认对RTT计算的影响……这些在回答里都要精确表述。</think>TCP协议通过多层级机制实现可靠传输,并通过动态窗口控制实现流量与拥塞管理,具体实现机制如下: --- ### 一、可靠传输核心机制 1. **序列号与确认应答(ACK)** - 每个数据包携带**32位序列号**(Sequence Number),标识数据字节流的起始位置 - 接收方通过**ACK标志位+确认号**(Acknowledgment Number)告知已成功接收的数据范围 - 示例:发送序列号$1000$(长度$500$)的数据包,接收方返回确认号$1500$($1000+500$)[^5] 2. **超重传(RTO)** - 发送方启动定器,若在**动态计算的重传超间**($RTO = SRTT + 4 \times RTTVAR$)内未收到ACK,则重传数据 - RTT(Round-Trip Time)持续更新: $$ SRTT_{new} = (1 - \alpha) \times SRTT_{old} + \alpha \times RTT_{sample} $$ 其中$\alpha$通常取$0.125$[^4] 3. **快速重传与SACK** - **快速重传**:连续收到3个重复ACK立即重传(无需等待超) - **SACK**(Selective ACK):通过TCP选项字段通告接收方的数据"空洞",实现**选择性重传** ``` SACK选项格式示例: Kind=5, Length=10, Left Edge=1500, Right Edge=2000 ``` 表示$1500-2000$字节区间已接收[^4] --- ### 二、流量控制机制 1. **滑动窗口(Sliding Window)** - 接收方通过**窗口大小字段**(16位)通告剩余缓冲区容量$rwnd$ - 发送方维护发送窗口$W_{send}$,满足: $$ W_{send} = \min(cwnd,\ rwnd) $$ 其中$cwnd$为拥塞窗口,$rwnd$为接收窗口[^2] 2. **窗口动态调整** - 接收方根据处理能力实更新$rwnd$(如缓冲区剩余空间) - **零窗口探测**:当$rwnd=0$,发送方定期发送1字节探测包 3. **糊涂窗口综合症(SWS)避免** - **接收方策略**:通告$rwnd=0$直至缓冲区有足够空间(如$\geq MSS$) - **发送方策略**:仅当满足 $rwnd \geq \min(MSS,\ \frac{1}{2}W_{max})$ 发送数据[^4] --- ### 三、拥塞控制机制 > 通过探测网络容量动态调整$cwnd$,避免中间网络过载[^3][^4] > **核心变量**: > - $cwnd$(拥塞窗口) > - $ssthresh$(慢启动阈值) 1. **慢启动(Slow Start)** - 初始$cwnd=1 \times MSS$,每收到一个ACK则$cwnd = cwnd + MSS$ - 窗口呈**指数增长**:$cwnd = 2^n \times MSS$($n$为RTT轮次) - 当$cwnd \geq ssthresh$进入拥塞避免阶段 2. **拥塞避免(AIMD)** - 每RTT周期线性增加:$cwnd = cwnd + MSS \times \frac{MSS}{cwnd}$ - 窗口增长公式:$\Delta cwnd = \frac{MSS^2}{cwnd}$ 每ACK 3. **拥塞响应** - **超丢包**:$ssthresh = \max(cwnd/2,\ 2 \times MSS)$,$cwnd$重置为$1 \times MSS$ - **快速恢复**(收到3个重复ACK):$ssthresh = cwnd/2$,$cwnd = ssthresh + 3 \times MSS$,重传后进入拥塞避免 --- ### 四、可靠性增强机制 1. **延迟确认(Delayed ACK)** - 接收方等待$200ms$或积累2个数据包后再发送ACK,减少小包数量 2. **间戳选项** - 计算精确RTT:$RTT_{sample} = T_{now} - T_{echoed}$ - 防止序列号回绕(PAWS):通过间戳识别旧重复包 --- ### 关键总结 | **机制类型** | **实现目标** | **核心技术** | |--------------------|--------------------------|---------------------------------------| | 可靠传输 | 数据完整有序到达 | 序列号、ACK、超/快速重传、SACK | | 流量控制 | 匹配接收方处理能力 | 滑动窗口、$rwnd$动态通告、SWS避免 | | 拥塞控制 | 避免网络过载 | 慢启动、AIMD、快速恢复、$cwnd$自适应 | > 可靠性依赖于端到端协同:发送方通过序列号排序与重传保证数据完整,接收方通过窗口通告实现反压,双方通过拥塞窗口探测网络容量上限[^2][^3][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值