TCP 笔记整理

1. TCP 首部格式

在这里插入图片描述

1.1 序列号(Sequence Number)

字段长 32 位,指发送数据的位置(序号)。每发送一次数据,就累加一次该数据 字节数 的大小。

序列号初始值为建立连接时由计算机生成的随机数,通过 SYN 包传给接收端主机。

此外,在建立间接时和断开连接时发送的 SYN 包和 FIN 包虽然并不携带数据,但是也会作为一个字节增加对应的序列号。

需要注意,这里的序号指的是字节流中某一个字节的序号,而不是报文段的序号。

1.2 确认应答号(Acknowledgement Number)

字段长 32 位,指下一次应该收到的数据的序列号。发送端收到这个确认应答以后,可以认为在这个序号以前的数据已经被正常接收了。

Seq(Sequence Number 的简写)为每一次 TCP 段中数据部分最后一个字节的序号,假设为 1000(数据长 100 个字节),此时接收端收到之后,会进行确认应答,应答时 Ack(Acknowledgement Number 的简写)则为 1001,即下一次需要接收的数据的起始序号。

1.3 控制位(Control Flag)

在这里插入图片描述
字段长 8 位。

(1)ACK(Acknowledgement Flag):为 1 时,确认应答(Acknowledgement Number)的字段变为有效。TCP 规定除了最初建立连接时的 SYN 包之外该位必须置位 1。

(2)SYN(Synchronize Flag):用于建立连接,为 1 表示希望建立连接,并在其序列号(Sequence Number)的字段进行序列号初始值的设定。

(3)FIN(Fin Flag):为 1 时表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相关交换 FIN 位置为 1 的 TCP 段。每个主机又对对方的 FIN 包进行确认应答 以后就可以断开连接。不过,主机收到对方的 FIN 包以后不必马上回复 FIN 包,而是可以等到缓冲区中的所有数据都因发送成功而被自动删除之后再发。


2. 三次握手与四次挥手
2.1 三次握手

在这里插入图片描述

其中 Seq 即 Sequence Number,Ack 即 Acknowledgement Number。

其余控制位没有表示出来即为 0。(参考自:TCP 协议详解

X 代表客户端的 ISN,Y 代表服务端的 ISN。

ISN(Initial Sequence Number),动态随机生成的,可防止攻击者容易猜出后续的确认号。

刚开始客户端处于 CLOSED 的状态,服务端处于 LISTEN 状态。然后:

1、第一次握手:客户端给服务端发一个 SYN 报文段(不携带数据,但是占用一个序号),并指明客户端的初始化序列号 ISN。此时客户端处于 SYN_SEND 状态。

2、第二次握手:服务器收到客户端的 SYN 报文段之后,会以自己的 SYN+ACK 报文段(不携带数据,也占一个序号)作为应答,并且也是指定了自己的初始化序列号 ISN,同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RECV 的状态。

3、第三次握手:客户端收到 SYN+ACK 报文段之后,会发送一个 ACK 报文段(该报文段可以携带正式的数据,如果不携带,则不占用序号),当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文段,此时客户端处于 ESTABLISED 状态。

4、服务器收到 ACK 报文段之后,也处于 ESTABLISED 状态,此时,双方以建立起了链接。

三次握手的作用也是有好多的,例如:
1、确认双方的接受能力、发送能力是否正常。
2、指定自己的初始化序列号,为后面的可靠传送做准备。
3、为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
4、如果是 https 协议的话,三次握手这个过程,还会进行数字证书的验证以及加密密钥的生成到。

需要注意,三次握手中前两次握手时双方互相传输的 Seq 值,会作用于建立连接之后的正式数据的传输。

相关概念

(1)半连接队列:服务端维护一个半连接队列(有容纳上限),该队列为每个客户端的 SYN 报文段开设一个条目,该条目表明服务器已收到 SYN 报文段,并向客户端发出确认,正在等待客户端的确认应答。这些条目所标识的连接在服务端处于 SYN_RECV 状态,当服务器收到客户的确认应答时,删除该条目,服务器进入 ESTABLISED 状态。

(2)SYN-ACK 重传次数:服务发送了 SYN-ACK 报文段后,如果在某一时间内未收到客户端的确认应答,就会重传 SYN-ACK 报文段。如果重传次数超过系统规定的最大重传次数,则将该连接信息从半连接队列中删除。(每次重传等待时间不一定相同)

(3)半连接存活时间(Timeout 时间,SYN_RECV 存活时间):指半连接队列中条目存活的最长时间,也即服务从收到 SYN 包到确认这个报文无效的最长时间,该时间值是所有重传等待时间的总和。

2.2 四次挥手

在这里插入图片描述
其中 Seq、Ack 值的变化主要参考自:Wireshark: 分析 TCP 四次挥手且对于主动方发起的 FIN 报文段的 Seq、Ack 值猜测也是随机生成的,也有可能是发送数据的最后一个字节以及收到的最新的数据的最后一个字节的序号有关(暂时没有找到相关文章说明生成的原理)。

主动方和被动方是相对而言的,即首先发送 FIN 报文段希望断开连接的即主动方,可以是客户端,也可以是服务端。

1、第一次挥手:主动方希望断开连接,发送 FIN 报文段,此时进入 FIN_WAIT_1 状态。

2、第二次挥手:被动方收到主动方发送的 FIN 报文段,发送一个 ACK 报文段进行确认应达,表示同意 “关闭” 连接,此时进入到 CLOSE_WAIT 状态。主动方在收到 ACK 报文段之后进入到 FIN_WAIT_2 状态。

3、第三次挥手:当被动方也不用再发送数据,想要断开连接时,也会发送一个 FIN 报文段给主动方,此时被动方进入到 LAST_ACK 状态。

4、第四次挥手:主动方在收到被动方的 FIN 报文段后,也会回复 ACK 报文段进行确认应答,此时主动方进入到 TIME_WAIT 状态。而被动方接收到主动方的 ACK 报文段后,就进入到 CLOSED 状态,即连接可以断开了。

5、主动方进入到 TIME_WAIT 状态后,会等待 2MSL(MSL:Maximum Segment Lifetime,报文段最大生存时间)后依然没有收到回复,则证明被动方已正常关闭,则主动方也可以断开连接了,即进入到 CLOSED 状态。

注意:如果 FIN_WAIT_1 状态下,收到了对方同时带 FIN 标志和 ACK 标志的报文时,就可以直接进入到 TIME_WAIT 状态,而无须经过 FIN_WAIT_2 状态。

状态变化参考自:TCP 协议详解

补充:为什么要等待 2MSL?

1、要确保服务器是否已经收到了我们的 ACK 报文,如果没有收到的话,服务器会重新发 FIN 报文给客户端,客户端再次收到 FIN 报文之后,就知道之前的 ACK 报文丢失了,然后再次发送 ACK 报文。其中服务器在等待收到 ACK 报文时,最长等待 1MSL,如果超过 1MSL 没有收到,就表示发生超时,此时服务器重新发送 FIN 报文,该报文最长要 1MSL 才能达到客户端,两者加起来是 2MSL。
2、保证本次连接的所有数据都从网络中消失

为什么要进行四次挥手

因为TCP是全双工通信的(可以同时(瞬时)进行信号的双向传输),因此每一个方向都必须单独进行关闭。这原则是当一方完毕它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个FIN仅仅意味着这一方向上没有数据流动。一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将运行主动关闭。而还有一方运行被动关闭。


3. 特点及目的

TCP(Transmission Control Protocol) 可以说是对 “传输、发送、通信” 进行 “控制” 的 “协议”。

3.1 通过序列号与确认应答提高可靠性

确认应答是为了通知数据发送方已经成功接收数据,如果在发送方在发送数据之后,如果在一定时间(重新发送超时时间(RTO, retransmission timeout))内没有收到确认应答,则会对数据进行重发。

但是有些情况下会导致确认应答延迟到达,此时发送方只要按照机制重发数据即可。但是对于接收方来说,会反复接收到相同的数据,为了对上层应用提供可靠的传输,必须得放弃重复的数据此时通过序列号可以识别是否已经接收数据,又能够判断是否需要接收。

接收端查询接收数据的 TCP 首部中的序列号和数据的长度,将自己下一步应该接收的序号作为确认应答返送回去。从而通过序列号和确认应答号,实现可靠传输。

3.2 重发超时的确定

重发超时时间长短随着数据包途经的网络环境的不同而变化。

TCP 在每次发包时都会计算往返时间(Round Trip Time,RTT,及报文段的往返时间)及其偏差(RTT 波动的值、方差,有时也叫抖动)。将往返时间和偏差相加,重发超时就是比这个和稍微大一点的值。

数据被重发之后,如果还是不能收到确认应答,则再次重发。此时,等待确认应答的时间将会以 2 倍、4 倍的指数函数延长。

且数据不会被无限重发,当达到一定次数后,如果仍然没有收到任何确认应答,则会判断为网络或者对端主机异常,强制关闭连接,且通信应用通信异常强行终止。

3.3 TCP 以段为单位发送数据

在建立 TCP 连接时,也可以确定发送数据包的单位,即最大消息长度(Maximum Segment Size,MSS)。最理想情况,MSS 正好是 IP 中不会被分片处理的最大数据长度(注意,是指数据的长度,不包括首部)。

TCP 在传送大量数据时,以及重发时,都是以 MSS 为单位进行分割发送。

MSS 在三次握手时,两端主机会在 TCP 首部写入 MSS 选项(附加 MSS 选项,则 TCP 首部将不再是 20 字节,而是 4 的整数倍),告诉对方自己的接口能够接收的 MSS 大小,然后取两者的较小值投入使用。

如果建立连接时,某一方的 MSS 选项被忽略,可以选 IP 包的长度不超过 576 字节的值(IP 首部 20 字节,TCP 首部 20 字节,MSS 536 字节)。

3.4 窗口控制的作用

TCP 在每个方向上(客户端发向服务端,服务端发向客户端)的数据传输上使用两个窗口(发送窗口和接收窗口),即双方通信会涉及到四个窗口。窗口的实现是基于缓存的。

后文虽然会涉及到滑动窗口、拥塞窗口,但是这里需要说明的是,实际上,对于发送方来说,只有一个发送窗口,该窗口决定了发送方最终的数据量,其实际的大小 = min(rwnd size, cwnd size),其中 rwnd size 对应滑动窗口的大小。

3.4.1 提高发送速度

TCP 如果每发一个段,进行一次确认应答处理,再发下一段,则当包的往返时间越长则通信性能就越低。

因此引入窗口的概念,即在窗口规定序号内的段,在发送了一个段之后,可以不用一直等待确认应答再发送,而是直接继续发送窗口规定序号内的段。

从而在往返时间较长的情况下,也能控制网络性能的下降。

且该机制的实现使用大量的缓冲区,需要对窗口内的未被确认的段进行缓存,等确认之后就可以将缓存的段删除。

且根据序号最小段的确认应答,将窗口滑动到确认应答中序列号的位置,从而顺序的将多段同时发送。这称之为滑动窗口

3.4.2 重发控制

(1)报文段丢失的情况

如果是是发送方的报文段丢失了,而没有到达接收方,因为窗口机制的作用,当接收方收到自己应该接收的序号以外的序号时,会返回应该接收的序号进行确认应答。

比如发送方关于 1001-2000 的数据丢失了,没有到达接收方,且因为窗口机制,在短时间内又收到了 2001-3000 的段,则接收方会返回 Ack 为 1001 的确认应答,告诉接收方自己目标期望的数据序号。

因此,在窗口比较大,又出现报文段丢失的情况下,同一序号的确认应答将会被重复不断的返回。而发送方如果连续 3 次收到同一序号的确认应答,则可以知道目标报文段丢失了,需要重发。

上述称之为高速重发控制,比前面的超时管理更加高效。

(2)确认应答未能返回的情况

如果数据已经到达接收方,只是其确认应答没有返回并被接收到,此时是不需要进行重发的。

如何没有使用窗口控制,则需要进行重发。

但是使用了窗口控制,基于第(1)点,因为继续发送了后续的段,但是没有重复收到那个本来没有收到的确认应答,因此可以知道只是确认应该没有及时收到而已,而不用重发了。

3.5 滑动窗口与流控制

TCP 提供一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量,即流控制。

接收端主机会在确认应答的报文段中设置 Window Size(即 rwnd 值,rwnd 表示 receiver window),发送端根据 rwnd 值改变窗口的大小,于是发送的数据不会超过该限度。该大小限度即窗口大小,进一步的说,是滑动窗口的大小。

在 TCP 首部中有一个字段 “窗口大小(Window Size)”,占 16 位,就是接收端用设置发送端窗口大小的。

当 Window Size 为 0 时,发送端就会暂停发送新数据(需要重发的旧数据可以正常重发),此时发送端的持续计时器(persistence timer)开始计时,如果在计时到期时还没有接收到接收端新的 Window Size 值,则会发送一个 1 字节的探测报文,如果接收方可以接收数据,就重新开始发送数据;如果接收方不能接收数据,就重新设置持续计时器。

3.6 拥塞控制与拥塞窗口

拥塞控制是为了应付网络的拥堵。

通过 “拥塞窗口”(cwnd,congestion window) 的概念,调节发送端所要发送数据的数量,从空控制对于网络中的拥塞状况。

TCP 发送方使用两个事件作为网络中拥塞的标志:超时、接收到三次重复 ACK。

拥塞策略:慢启动、拥塞避免、快速恢复。

且对于这三种策略的转换,涉及到 TCP 的三个版本:Taho TCP、Reno TCP、新 Reno TCP。(具体内容暂不涉及)

3.6.1 慢启动

指最开始将 cwnd size 设置为 1MSS(最大报文段),使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大 cwnd。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个 MSS 的数值(底数为 2 的指数增长规律)。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。

为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量。慢开始门限 ssthresh 的用法如下:

当 cwnd < ssthresh 时,使用上述的慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
3.6.2 拥塞避免

让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样拥塞窗口 cwnd 按线性增长规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送 方窗口值的一半(但不能小于 2)。然后把拥塞窗口 cwnd 重新设置为1,执行慢开始算法。

这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生 拥塞的路由器有足够时间把队列中积压的分组处理完毕。

3.6.3 快速恢复

首先需要说一下快重传,以 3.4.2 为前提,发送方相继发送了 M1、M2、M3 报文段,但是接收方没有收到 M1,却陆续收到了 M2、M3,按照传重传的规定,接收方在接收到 M2、M3 的时候,应及时对 M1 进行重复确认,以便让发送方及早知道 M1 没有到达。

快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段 M1,而不必继续等待 M1 设置的重传计时器到期。

由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约20%。

与快重传配合使用的还有快恢复算法,其过程有以下两个要点:

当发送方连续收到三个重复确认(会被解释为网络的轻微阻塞),就执行“乘法减小”算法,把慢启动门限 ssthresh 减半。

与慢启动不同之处是现在不执行慢启动算法(即拥塞窗口 cwnd 现在不设置为 1),而是把 cwnd 值设置为 慢开始门限 ssthresh 减半+3 的值(+3 的原因是收到 3 个重复的 ACK,表明有 3 个 “老” 数据包离开了网络(有的版本不 +3) ),然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。

4. 补充
4.1 关于滑动窗口与拥塞窗口的补充

滑动窗口(sliding window)是接收方的流量控制,即对应接收方的接收窗口(rwnd)。

拥塞控制(congestion control)是发送方的流量控制。即使接收方的缓存无限大,依然会因为网络层的原因发生拥塞。


主要参考文章:

1、https://lrh1993.gitbooks.io/android_interview_guide/content/computer-networks/tcpip.html
2、关于三次握手与四次挥手面试官想考我们什么?— 不看后悔系列
3、TCP 协议详解
4、Wireshark: 分析 TCP 四次挥手
5、《图解 TCP/IP》
6、《计算机网络:自顶向下方法》(第六版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值