转自 https://juejin.im/post/5da07a066fb9a04ddf2c3e05
前言
首发自 blog.cc1234.cc/
TCP在不可靠的IP协议之上实现了可靠性, 从而使得我们不必再去关注网络传输中的种种复杂性,所谓的可靠就是让我们去信任它即可
信任归信任,可我们还是的得去了解它,知道它为何值得信任,信任主要体现在哪些方面,换句话说就是
- TCP的可靠性是什么
- TCP如何实现的可靠性
上面的问题就是本文讨论的核心点
TCP的可靠性实则是一个很大的话题,很多细节都值得深究,由于本人水平有限,文中很多描述都没有深入甚至可能有错误,读者若有不同观点,尽可提出
什么是可靠性
其实在RFC 793的1.5 Operation
专门对Reliability(可靠性)做了说明
总结下来如下
确保一个进程从其接受缓存中读出的数据流是无损坏,无间隔,非冗余和按序的数据流;即字节流与连接的另一方端系统发送出的字节流是完全相同的
需要解决的问题
前面说到的可靠性,提到了无损坏,无间隔,非冗余和按序等几个关键词, 而在网络中要实现这些指标,我们都有对应的问题需要去解决
其中最典型的几个问题如下
-
干扰
网络的干扰可能是因为硬件故障导致数据包受到破坏, 也有可能是网络波动导致数据包的某些bit位产生了变化
题外话:这里不的干扰并不包含恶意攻击,恶意攻击是属于传输安全的范畴了,比如我们熟知的SSL/TLS就是一个成熟的网络传输安全问题的解决方案
如下图,发送的
111
由于干扰变成了101
-
乱序
发送方连续先后发送两个数据包, 后发送的数据包可能先到达接收方,如果接收方按接收顺序处理数据包,这就会导致接收的数据包与发送的数据包不一致。
造成这样的原因是因为每一个数据包都会根据当时的网络情况选择不同的路由进行传输, 就像从开车从上海到北京有很多路线可选,不一定你先出发就能先到(我没去过北京,请不要杠我......)
如下图,发送方顺序发送了
A -> B -> C
三个数据包, 然而接收方可能是以A -> C -> B
这样的顺序接收的报文,很明显 B 和 C两个个报文的顺序不符合期望,产生了乱序
-
丢包
网络丢包是一个很常见的现象,造成的原因也多种多样,比较常见的有
-
接收方由于缓存溢出,导致无法再处理到来的数据包了,直接丢弃从而造成丢包
-
网络拥塞导致数据包丢包
-
数据包被检测到损坏了,被接收方丢弃造成了丢包
-
......
下图展示了这种情况,发送的数据
CBA
由于A
产生了丢包,导致接收方只收到了CB
-
-
冗余
发送方可能因为某些原因重复发送了同一个数据包,接收方要有能力处理这种冗余数据包
比如发送方发送的一个数据包因为网络拥塞迟迟没有被接收方收到, 发送方认为产生了丢包就又重发了一次,结果最终接收方收到了两个同样的数据包,产生了数据冗余
在继续往下看之前,可以先思考一下: 你会如何去解决这些问题?
0x01 解决干扰
为了能够检测到数据包在传输过程中是否发生了差错,TCP引入了checksum。
checksum的具体细节可以查阅RFC1071
下图是TCP的报文结构,蓝色部分就是checksum
checksum是一个16bit长的字段,发送方在计算checksum时会先将报文中的Checksum置零,然后基于整个报文(头部 + 数据部分)计算出checksum
实际上还会加上96bit的伪头部,可以参考RFC 793 Header Format 一节
接收方在收到报文后也会计算checksum
- 如果计算结果符合期望值,说明数据包没有收到干扰/损坏
- 如果不符合期望,一般会直接丢弃该数据包
TCP的校验和也有一定的限制,并不一定100%能检测到数据包产生的错误
这就和我们平常做API开发时的签名一样
0x02 解决乱序和冗余
请先回顾以下前面谈到乱序时的一个示例图
乱序有多个解决方案,比如发送一个数据后,我确认该发送的数据被接收方接收了我再发下一个,这样肯定是有序的, 但是这样的方案对网络利用率实在是太低了
另一个很朴实的解决方案就是为每个报文标上序号, 这样接收方在收到报文后只需要按序号对报文排序就可以得到有序的报文了,过程如下图所示
实际上TCP协议采用的就是为报文加上序号这样的方法
TCP的报文结构上维护着一个Sequence Number
(下面简称seq
),如下图的红色区域所示
TCP的发送端和接收端各自独立维护一个seq
, seq
的初始值是在创建连接时初始化的(值是随机的)
有一个控制位SYN
就是专门用来在发送方和接收方同步seq
的 (这里的同步指的是让对方知道自己的seq初始值是多少)
详细内容参考RFC 793 的 3.3. Sequence Numbers
建立连接后的每一个报文都会携带seq
如下图所示,假设初始seq=1
,发送的第一个报文A的长度为12, 那么发送第二个报文B的seq=1+12=13
接收方接收到多个报文后,可以基于seq多数据包进行升序排序,并且通过检查seq
的值,可以判断接收的数据是否有间隔,以及数据是否是有序的。
除此之外,seq
使得TCP有能力处理重复数据包的问题,因为接收方可以根据seq
判断出该数据包是不是已经被接收了,这样顺带还解决了数据冗余的问题
0x03 解决丢包
丢包的原因可能各式各样,我们从一个简单的场景开始开始分析
假设没有丢包的情况下,如何让发送方知道接收方已成功接收到数据包了呢?
这就像人与人之间交谈,你如何判断对方听见了呢?
现实生活中我们靠的是对方的响应来做出判断
TCP也采用类似的机制,我们一般称之为ACK
(Acknowledgment):接收方在收到数据包以后会对发送方响应一个特定的数据包.
还是继续看一下TCP的结构图,注意绿色区域的Acknowledgment Number
(后面简称ack
)
注意大写ACK和小写ack是有区别的
大写ACK一般指的是报文的类型
小写ack指的就是这个32bit长的号码
ack
与seq
都是32bit长,前面我们说到TCP建立连接后发送的报文都会带上seq
, 接收方在收到报文后,会响应一个类型为ACK的报文
报文的acknowledgment number
的值是接收方下次期望收到的报文的seq
值
实际上ack的值会受很多情况影响, 比如TCP的累积确认机制, 选择重传机制等等都会影响响应的ack值,细节可以参考RFC 793
有了ACK
后, 发送方就可以知道报文有没有被正确接收了
请看下图,这是一个简单的交互, 发送方发送数据,接收方确认数据后做出响应
前面我们都是基于没有丢包的情况进行分析的,ACK
并没有解决丢包的问题,如下图所示,发送的数据如果丢包了就没有ACK
, ACK
如果丢包了就不知道数据是否被正确接收。
此时我们引入超时和重传, 结合ACK
机制一起来应对丢包的问题
- 发送方发送一个未被确认的数据包后就启动一个计时器
- 如果在指定时间内没有收到
ACK
, 发送方可以重传该报文
超时重传 + ACK也有一个小问题,就是最开始提到的数据冗余问题
重传数据包可能导致接收方接收到多个重复的数据包, 如果你还没忘记的话,这个可以通过前面一节说到的seq
去解决
实际上TCP的拥塞控制也是处理丢包的有效机制之一,有兴趣的同学可以去了解
0x04 基本可靠
在回顾一下我们最开始对可靠的要求
确保一个进程从其接受缓存中读出的数据流是无损坏,无间隔,非冗余和按序的数据流......
前面我们通过checksum, seq,ack,超时重传等机制,算是达到了一个可靠性的基本要求, 为什么说是基本可靠呢?
因为到目前为止我们的场景还都相对简单,所以会忽略掉很多变量和细节问题。
比如我们都没有提到很重要的滑动窗口
,拥塞控制算法
等, 但实际上这些也是TCP在复杂的网络环境中实现可靠性不可获取的东西
0x05 番外篇
本节作为番外篇,可以认为是对前面内容的一些补充,补充主要也是针对一些细节的地方,以FAQ的方式进行表述
-
每个
seq
都需要一个ack
吗?当然不是
发送方可以直接发送多个报文, 接收方在接收到报文后先不急着响应
ack
,因为后续报文可能马上到达, 这就是ACK延迟确认
延迟确认可以让我们同时对多个接受的报文进行一次确认,这个又称之为
累计确认
这样接收方就不必对每个报文都进行确认,接收到多个报文后如果延迟时间内没有报文到来,就发送下一个期望接收报文的
ack
, 如下图所示 -
上图中,如果同时发送多个seq报文,若中间某一个丢包,ack如何响应呢?
一般接收方会有一个接收缓冲,会缓存接收到的报文,这些报文按seq值排序, 如果中间缺少某段报文,那么接收方就会响应这段报文的seq值
如下图所示,发送方发送了
seq=1,seq=2,seq=3
的报文,seq=2
丢包, 但是接收方缓存了1和3,所以知道这部分报文不连续,中间缺少2,所以响应了一个
ack=2
(一般叫做最小ack)发送方重发seq=2的报文, 接收方发现
1,2,3
已经完整接收了,就响应下一个期望值, 即响应ack=4
-
每发送一个报文就启动一个定时器吗?
为了保证可靠性,TCP增加了超时重传机制, 使得每个未被确认(ACK)的报文在一定时间后可以被重新发送
一种实现方式就为每个未被确认的报文都单独配置一个计时器,可是这样做的话开销太大了
在RFC 6298 的
Managing the RTO Timer
中提及了一种单一计时器的管理方式(具体细节请参考文档)每个已发送但未确认数据包都会被放进队列里, 这个队列持有一个单独的计时器
当第一个数据包进入队列时,计时器启动了
如果计时器超时,队列头部的数据包会被重发,并且计时器重新计时
当收到ACK时,计时器也会重启
队列的所有数据都被确认了的话,就关闭定时器
-
超时时间怎么设置呢?
重传超时时间(Retransmission TimeOut), 一般简称RTO, 这个时间既不能太长也不能太短。
-
太长可能会出现数据包已经丢了,但还要等待无谓的时间才能重传
-
太短可能数据包尚未到达,此时发生重传,浪费了资源
往返时延 RTT (Round Trip Time)是配置RTO的一个重要指标, 但是由于网络间端到端的RTT并不是固定的,所以TCP采用了一种自适应的方法来计算RTT, 并且根据计算的值来配置RTO
整个过程是动态的,也就是说当RTT变化时,RTO也能相应的做出调整
具体的细节可以参考RFC 6298的
The Basic Algorithm
-
-
一定要超时了才重传吗?
TCP有一个快速重传机制, 当一个接收方收到三个以上的重复
ack
时,接收方就会直接根据ack
的值重传对应的报文而无需等待超时下图展示了一个简单的示例
1. 发送方发送了seq=1, seq=2, seq=3, seq=4的报文 2. seq=1的报文发送了丢包 3. 接收方分别接受到了`2,3,4`的报文,接收方缓存收到的报文,然后检查seq知道报文不连续,缺少`seq=1`的报文, 所以每次都响应`ack=2` (为了简化描述,我们不考虑延迟确认和缓冲区大小) 4. 接收方连续收到了3个`ack=2`的报文,所以认为`seq=1`的报文丢包了,重传`seq=1` 5. 接收方收到`seq=1`的报文后,发现seq=2,seq=3,seq=4已经接收过了,直接响应`ack=5` 复制代码
总结
我们看见TCP在实现可靠性上做出了很多精妙的设计,这些设计在大的方面追求至简,而在细节又追求极致,绝对是非常值得学习和思考的
TCP的这些设计你也可以在现在的软件系统中看到它的影子
- 比如消息队列有类似的ack机制去确保消息已经被投递
- 比如为了保证异步消息的有序性也会有类似
seq
的机制
正如前言所说,可靠性
实则是一个很大的话题,不可避免的我还是留下了很多坑,如果有错误的地方,还望指出。
相关推荐
通过对TCP段格式的理解以及TCP连接建立与拆除流程的学习,我们可以更深入地理解TCP是如何保证数据传输的可靠性的。在未来的发展中,随着网络技术的进步,TCP协议也会不断优化和发展,更好地适应日益复杂的网络环境。
这是TCP保证可靠性的基础。 2. **套接字编程**:在单片机中,你需要使用套接字API(如socket(), bind(), listen(), accept(), connect() 和 send()/recv())来实现TCP通信。这些函数用于创建、绑定、监听、接受连接...
在计算机网络中,TCP(传输控制协议)和UDP(用户数据报协议)是两种...TCP保证可靠性和顺序,但牺牲了效率;UDP追求效率和实时性,但可能丢失或乱序数据。了解这些基础知识,对于开发网络应用和优化网络性能至关重要。
TCP 协议假定其所使用的网络栈下层协议(如 IP 协议)是非可靠的,其自身提供机制保证数据的可靠性传输。在目前的网络栈协议族中,在需要提供可靠性数据传输的应用中,TCP 协议是首选的,有时也是唯一的选择。 TCP ...
(3)可靠性:基于Linux环境的TCP协议一致性测试实现可以确保TCP协议的实现符合定义和要求,从而提高了系统的可靠性。 本文讨论了基于Linux环境的TCP协议一致性测试实现的基础概念、模型、方法和步骤,并分析了其...
总结来说,传输层通过TCP和UDP提供不同的数据传输服务,TCP保证可靠性,适合于需要高可靠性的应用,如HTTP、FTP等;而UDP则追求效率,适用于实时应用,如DNS、VoIP等。对于网络规划设计师,理解这两者的特性及其在...
首先,TCP通过建立连接来确保数据传输的可靠性,这个过程被称为三次握手。在代码实现中,我们需要创建socket对象,然后通过send和recv函数发送和接收数据。三次握手涉及SYN(同步序列编号)和ACK(确认)标志的交换...
为了优化TCP服务的性能并确保数据的可靠性,开发者通常会采用各种策略,其中一种就是利用I/O多路复用技术,如`select`模型。本文将详细探讨如何通过`select`模型构建发送队列,以实现TCP数据的可靠发送。 首先,`...
TCP(Transmission ...通过这样的视觉化展示,学习者能够更直观地看到TCP如何保证数据的可靠性,理解TCP协议背后的工作原理。而`.fla`和`.swf`文件是Flash动画的源文件和可执行文件,分别用于编辑和查看动画内容。
总结来说,RUDP是针对特定应用场景优化的传输协议,它在保持UDP的低延迟优势的同时,提供了类似于TCP的可靠性保证。对于需要高效、实时传输且对数据丢失敏感的系统来说,RUDP是一个理想的选择。通过深入理解RUDP的...
4. **重传机制**:TCP具有自动重传未确认的报文段功能,这是通过超时重传和快速重传两种方式实现的,以提高数据传输的可靠性。 5. **拥塞控制**:TCP通过慢启动、拥塞避免、快速重传和快速恢复等算法来检测并应对...
TCP提供了一种面向连接的服务,确保数据的顺序和可靠性,而UDP则是无连接的,不保证数据到达或者顺序。 TCP数据包的构造通常涉及到以下步骤: 1. **建立连接**:在发送任何数据之前,TCP需要通过三次握手建立连接。...
TCP客户端软件模拟TCP连接的发起方,可以发送和接收数据,验证连接的可靠性以及数据的正确传输。而TCP服务器软件则模拟连接的接收方,等待客户端的连接请求,处理接收到的数据,并向客户端发送数据。 调试TCP和...
基于TCP的文件传输是通过TCP协议的可靠性和顺序性保证文件的安全、完整传输。QQ作为典型的TCP应用,其文件传输功能利用了TCP的各种优势,如连接可靠性、流量控制和优化策略,同时考虑了安全性,确保了用户文件的高效...
2. **数据传输**:TCP的可靠性体现在其确认机制和重传机制上。VB的Socket控件提供了SendData和ReceiveData方法来发送和接收数据。每次发送数据后,服务器都会返回一个确认,如果客户端没有收到确认,它会重新发送...
IP负责将数据从源主机送达目的地,但不保证可靠性。TCP则通过自身机制弥补了IP的这一不足,实现了在不可靠的网络环境中提供可靠的传输服务。 总的来说,TCP是互联网协议的核心组成部分,它解决了在不可靠的网络环境...
TCP通过三次握手建立连接,并通过确认、重传、流量控制和拥塞控制等机制保证数据的可靠性。然而,这种可靠性也意味着TCP在效率上相对较低,因为其需要更多的开销来处理错误和拥塞。 相比之下,UDP是一种无连接的...
TCP是一种可靠性传输协议,在计算机网络中扮演着重要的角色。通过序列号和确认应答、超时重传、滑动窗口、连接管理和拥塞控制等机制,TCP保证了数据在网络中的可靠传输。这些机制共同协作,适应不同的网络环境,确保...
TCP保证了数据传输的可靠性,确保了即使在网络不稳定的情况下,数据也能准确无误地送达。 穿越NAT是TCP P2P通信的关键环节,因为大多数家庭和企业网络都使用NAT来分配内部IP地址并保护内部网络。NAT将私有IP地址...