传输控制协议(Transmission Control Protocol,TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。
它通过三次握手建立连接,确保数据的可靠传输,并提供顺序控制、流量控制和拥塞控制等功能。
在简化的计算机网络 OSI 模型中,它完成第四层传输层所指定的功能。用户数据报协议(UDP)是同一层内另一个重要的传输协议。
在因特网协议族(Internet protocol suite)中,TCP 层是位于 IP 层之上,应用层之下的中间层。
大写ACK是确认值(Acknowledgement),为1便是确认连接。
小写ack是确认编号(Acknowledgement Number),即接收到的上一次远端主机传来的seq然后+1,再发送给远端主机。提示远端主机已经成功接收上一次所有数据。
TCP 是面向连接的,客户端与服务器要建立连接才能进行通信,通信后需要断开连接。
假设运行在一台主机(客户)上的一个进程想与另一台主机(服务器)上的一个进程建立一条连接,客户应用进程首先通知客户 TCP,他想建立一个与服务器上某个进程之间的连接,客户中的 TCP 会用以下步骤与服务器中的 TCP 建立一条 TCP 连接:
参与一条TCP连接的两个进程中的任何一个都能终止该连接,连接结束后,主机中的“资源”(缓存和变量)将被释放。
此时如果客户端等待 2MSL 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。
在TCP通信中,每次发送数据都需要等待一次确认应答,如果在等待前一个数据包的确认时才发送下一个数据包,通信效率会变得很低。
为解决这个问题,TCP 引入了滑动窗口这个概念。它允许发送方在不接收确认应答的情况下持续发送数据,并根据接收方的反馈及时调整数据发送速率。即使在往返时间较长的情况下,它也不会降低网络通信的效率。
TCP 报文里有一个字段叫窗口(Window,WIN),记录着窗口大小的信息窗口大小。
这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收
端的处理能力来发送数据,而不会导致接收端处理不过来。
发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。所以,通常窗口的大小是由接收方的窗口大小来决定的。
滑动窗口基本概念
发送窗口的工作空间,根据处理的情况分成四个部分:
已发送并收到 ACK确认的数据、已发送但未收到 ACK确认的数据、未发送但总大小在接收方处理范围内(接收方还有空间)、未发送但总大小超过接收方处理范围(接收方没有空间)
其中已发送但未收到 ACK确认的数据的部分方框是发送窗口,未发送但总大小在接收方处理范围内(接收方还有空间)的部分是可用窗口。
下面是发送窗口工作原理的示意图,在下图中,蓝色部分为已发送并收到 ACK确认的数据的子节,绿色部分为已发送但未收到 ACK确认的数据,黄色部分为未发送但总大小在接收方处理范围内的数据,白色部分为未发送但总大小超过接收方处理范围。
下图以滑动窗口大小为 8 为例子:
(a):
(b):
(c):
(d):
接收窗口根据处理的情况划分成三个部分: 已成功接收并确认的数据(等待应用进程读取)、 未收到数据但可以接收的数据、未收到数据并不可以接收的数据;
上面发送窗口的图d情况中,接收窗口的示意图如下:
可靠:保证接收方进程从缓存区读出的字节流与发送方发出的字节流是完全一样的。
前文提到 TCP 是可靠的,那么 TCP 靠着哪些机制保证了可靠性呢?
TCP 主要通过连接管理、序列号、确认应答、重传机制、流量控制、拥塞控制四个机制来保证可靠性的。
在TCP中,通过序列号与确认应答来实现数据的可靠传输。
TCP将每个字节的数据都进行了编号,这就是序列号,序列号能够保证可靠性和有序性。
接收方接收数据之后,会回传ACK报文,报文中带有此次确认的序列号,用于告知发送方此次接收数据的情况这就是确认应答。
序列号与确认应答机制就是,在发送端将数据传输到接收主机时,接收主机会根据收到消息的序列号,发送一个确认应答消息以表示已成功接收到数据。
在错综复杂的网络,数据在传输过程中是有可能丢失的,所以 TCP 针对数据包丢失的情况,会用重传机制解决。
发送方在发送数据时,设定一个定时器,使用一个保守估计的时间作为收到 ACK 确认应答报文的超时上限。如果超过这个上限仍未收到对方的 ACK 确认应答报文,发送方将重传这个数据包。每当发送方收到确认包后,会重置这个重传定时器。
TCP 会在以下两种情况发生超时重传:数据包丢失、确认应答丢失。
超时重传时间怎么确定
RTO 的确定是一个关键问题,因为它直接影响到 TCP 的性能和效率。当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差; 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。
因此,RTO 应该根据网络的实际状况,动态地进行调整。
TCP采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是报文段的往返时间 RTT 。TCP保留了 RTT 的一个加权平均往返时间 RTTS(这又称为平滑的往返时间,S表示 Smoothed。因为进行的是加权平均,因此得出的结果更加平滑)。每当第一次测量到 RTT 样本时,RTT值就取为所测量到的 RTT 样本值。但以后每测量到一个新的 RTT样本,就按下式重新计算一次 RTTS:
(1)首次计算RTO时:
R T T S = R 1 RTT_S=R1 RTTS=R1
R T T d e v = R 1 / 2 RTT_{dev}=R1/2 RTTdev=R1/2
R T O = μ × R T T S + ∂ × R T T d e v = μ × R 1 + ∂ × R 1 / 2 RTO = μ \times RTT_S+∂ \times RTT_{dev}=μ \times R1+∂ \times R1/2 RTO=μ×RTTS+∂×RTTdev=μ×R1+∂×R1/2
其中:R1 是第一次测量的 RTT
(2)迭代计算RTO时:
R T T S = R T T S + α × ( R T T − R T T S ) = R T T S + α × ( R 2 − R 1 ) RTT_S=RTT_S+ α \times(RTT-RTT_S)=RTT_S+ α \times(R2 - R1) RTTS=RTTS+α×(RTT−RTTS)=RTTS+α×(R2−R1)
R T T d e v = ( 1 − β ) × R T T d e v + β × ( R T T − R T T S ) RTT_{dev}=(1-β) \times RTT_{dev}+β \times (RTT-RTT_S) RTTdev=(1−β)×RTTdev+β×(RTT−RTTS)
R T O = μ × R T T S + ∂ × R T T d e v RTO = μ \times RTT_S+∂ \times RTT_{dev} RTO=μ×RTTS+∂×RTTdev
其中:R2 是新测量的 RTT
RTTS 是计算平滑的 RTT ,RTTdev 是计算平滑的 RTT 与 最新 RTT 的差距。
在 Linux 下,
α
=
0.125
α = 0.125
α=0.125,
β
=
0.25
β = 0.25
β=0.25,
μ
=
1
μ = 1
μ=1,
∂
=
4
∂ = 4
∂=4。是大量实验中调出来的。
TCP 还有另外⼀种快速重传(Fast Retransmit)机制,它基于接收端的反馈信息来引发重传,而非重传计时器超时。
换言之,当数据包未连续到达时,接收方会针对可能丢失的最后一个包发送 ack 确认。若发送方连续3次接收到相同的ack,便会触发重传机制。快速重传的优势在于,它无需等待超时即可进行重传,从而提高了数据传输的效率。
如上图的例子,发送方发出了 1,2,3,4,5 份数据:
当发送方依次发送了seq序号为1、2、3、4、5的数据包后,接收方首先成功接收了seq1并返回了确认(ack=2)请求下一个包(即seq2)。然而,由于某些原因,seq2未能到达接收方。随后,seq3、seq4、seq5号包均成功送达,但接收方依然只能回复ack=2,因为它仍然期待seq2的到达。发送方在连续三次收到ack=2的反馈后,识别出2号包可能丢失,于是立即重传2号包。在接收方最终收到seq2后,鉴于seq3、seq4、seq5号包均已成功接收,它随即发送ack=6,请求下一个期望的包(若存在)。
快速重传机制解决了超时时间的问题,但是它依然面临着另外一个问题。就是重传的时候,是重传一个,还是重传所有的问题。
改进的方法就是选择确认重传 (Selective Acknowledgment,SACK)。
在TCP头部的选项(Options) 字段中,添加了选择性确认(SACK)选项。通过SACK,接收方可以返回最近收到的报文段的序列号范围,使发送方能够准确知道哪些数据包已经成功接收,哪些数据包尚未到达。
有了这些信息,发送端只需重传丢失的数据包,而不是整个数据流,从而提高了重传的精确性和效率。
如上图的例子:
现在先后发送了5个数据包的数据,如果此时发送第2个数据包时数据丢失,而其余4个数据包都收到了接收方的确认信息,那么此时根据SACK可以判断出只有第二个数据包丢失,而第1个、第3个、第4个、第5个数据包已被接收,此时可以只重新发送第2个数据包。
DSACK,即重复 SACK(Duplicate Selective Acknowledgment),这个机制是在 SACK 的基础上,额外携带信息,告知发送方有哪些数据包自己重复接收了。DSACK 的目的是帮助发送方判断,是否发生了包失序、ACK 丢失、包重复或伪重传。让 TCP 可以更好的做网络流控。
为避免触发重发机制和避免网络流量浪费,发送方需要考虑接收方的处理能力。如果发送方持续发送数据给接收方而后者无法及时处理,将导致重发机制触发,进而浪费网络资源。为此,TCP引入了流量控制机制。
通过流量控制,发送方可以根据接收方当前的接收能力来动态调整发送的数据量,避免过载接收方而导致数据丢失或延迟,从而优化整个网络通信的效率和可靠性。
一句话总结一下,流量控制就是:让发送方慢点 ,要让接收方来得及接收。
TCP利用 滑动窗口 机制实现流量控制。
在通信过程中,接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小,即接收窗口rwnd(接收方设置确认报文段的窗口字段来将rwnd通知给发送方) ,发送方的发送窗口取接收窗口 rwnd 和拥塞窗口cwnd 的最小值。
例子如下:
A向B发送数据,连接建立时,B告诉A:“我的rwnd=400(字节)”,设每一个报文段100B,报文段序号初始值为1
TCP 通过让接收方指明滑动窗口的大小来进行流量控制。如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。
当窗口关闭时,接收方在处理完数据后会发送一个非0窗口大小的ACK报文通知发送方。然而,如果这个通告窗口的ACK报文在网络中丢失,可能会导致问题。发送方会继续等待ACK确认,无法发送新的数据。如不采取措施,这种相互等待的过程,会造成了死锁的现象。
为了解决上述的死锁问题,TCP为每一个连接设有一个持续计时器,只要TCP连接的一方收到对方的零窗口通知,就启动持续计时器。
若持续计时器设置的时间到期,就发送一个**零窗口探测 ( Window probe )**报文段 。接收方收到探测报文段时给出现在的窗口值。
若窗口仍然是0,那么发送方就重新设置持续计时器;如果不是0,则发送方可以根据窗口的大小继续发送数据,死锁的局面就被打破了。
这样通过定时地进行探测,就可以避免死锁的产生。
在网络拥堵时,若持续发送大量数据包,可能导致数据包时延和丢失。TCP会进行重传,然而重传会增加网络负担,导致更大的延迟和丢包。这会形成恶性循环,不断放大问题。
为了解决此问题,就有了拥塞控制的概念,拥塞控制是指防止过多的数据注入网络,保证网络中的路由器或链路不致过载。
拥塞控制是通过拥塞拥塞窗口实现的。
拥塞窗口(cwnd)是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。只要网络中没有出现拥塞,cwnd 就会增大; 但网络中出现了拥塞,cwnd 就减少。
常见的拥塞控制算法有慢启动、拥塞避免、快速恢复。
在 TCP 刚刚连接好并开始发送TCP报文段时,先令拥塞窗口 cwnd=1,即一个最大报文段长度 MSS。每收到一个对新报文段的确认后,将 cwnd 加1,即增大一个 MSS。用这样的方法逐步增大发送方的拥塞窗口 cwnd,可使分组注入网络的速率更加合理。
例如:
A 向 B发送数据,发送时 A的拥塞窗口为 1,那么A一次可以发送1个 TCP 报文段,经过一个RTT后(也称一个传输轮次),A收到B对刚才1个报文的确认,于是把拥塞窗口调整为 2,下一次发送时就可一次发送2个报文段。
第二轮数据发送时,发送时 A的拥塞窗口为 2,那么A一次可以发送2个 TCP 报文段,经过一个RTT后(也称一个传输轮次),A收到B对刚才2个报文的确认,于是把拥塞窗口调整为 4,下一次发送时就可一次发送4个报文段。
慢启动算法,发包的个数是指数性的增长,这样拥塞窗口的增长速度会过快,所以需要拥塞避免算法来降低拥塞窗口的增长速度。
拥塞避免算法的做法如下:
发送端的拥塞窗口 cwnd(Congestion Window) 每经过一个往返时延 RTT就增加1个 MSS的大小,而不是加倍,使cwnd 按线性规律缓慢增长(即加法增大)。
可以通过 慢启动门限 ssthresh(slow start threshold)状态变量,来决定慢启动算法和拥塞避免算法的使用。
网络出现拥塞时,无论是在慢开始阶段还是在拥塞避免阶段,只要发送方检测到超时事件的发生(未按时收到确认,重传计时器超时),就要采取激烈的应对措施。具体反应如下:
拥塞避免并不能完全能避免拥塞。利用以上措施要完全避免网络拥塞是不可能的。拥塞避免是指在拥塞避免阶段把拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。
当拥塞发生时,如果将拥塞窗口直接设置为1,并重新开始慢启动,是会突然减少数据流的。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。所以提出了快速重传算法。
快速重传
如上文的重传机制,当丢包的时候,会有两种情况:超时重传和快速重传。
在拥塞发生时,使用“快速重传算法”进行重传更符合需求。当接收端检测到有中间数据包丢失时,会连续三次发送对前一个数据包的确认(ack),从而促使发送端迅速进行重传,无需等待超时。TCP协议认为这种情况下的网络拥塞并不严重,因为仅丢失了少量数据包。
在发生此类拥塞时的快速重传流程如下:
快速恢复:
RFC5681文档中描述了快速恢复算法。快速重传与快速恢复算法常被并行运用。快速恢复算法的基本思想是,当收到三个重复的ack确认时,意味着网络状况尚可并未严重恶化,因此无需采取像RTO超时那样的严厉措施来应对。
进入快速恢复之前,cwnd 和 sshthresh在快速重传算法执行之前已被进行如下更新:
快速恢复过程概述如下:
用户数据报协议(User Datagram Protocol,UDP)又称用户数据包协议,是一个简单的是一种无连接的、不保证可靠的、面向数据包的传输层通信协议,位于OSI模型的传输层。
该协议由 David P. Reed 在1980年设计且在RFC 768中被规范。典型网络上的众多使用 UDP 协议的关键应用在一定程度上是相似的。
在 TCP/IP 模型中,UDP 为网络层以上和应用层以下提供了一个简单的接口。UDP 只提供数据的不可靠传递,它一旦把应用程序发给网络层的数据发送出去,就不保留数据备份(所以 UDP 有时候也被认为是不可靠的数据包协议)。UDP 在 IP 数据包的头部仅仅加入了复用和数据校验字段。
因篇幅问题不能全部显示,请点此查看更多更全内容