写点什么

太难了,救救孩子吧,到现在还搞不懂 TCP 的三次握手四次挥手

用户头像
极客good
关注
发布于: 刚刚

[](


)面向连接怎么理解




从我表白失败的例子就能看到,我还未确保连接的正常就开始表白,导致我说完了对方却因为信号不好没有听到。如果我事先确保连接正常,就不会出现这样的情况了!我们前面说了 TCP 是面向连接的,那 TCP 是怎么面向连接的呢?

[](

)三次握手交代了什么?


没错,都是从握手开始!我们都知道,tcp 建立连接需要经过三次握手,那每次握手都交代了什么呢?如果只进行两次握手行不行?我们先看一个电话接通的场景:


A:你好,你能听到吗?


B:我能听到,你能听到吗?


A:我也能听到。



在正式通话之前,为了确保通话的可靠,往往都需要经过上面的三次对话进行确认。那这三次对话是必须的吗?每一次对话的必要性又是什么呢?


A:你好,你能听到吗?(让 B 知道 A 能说话)


B:我能听到,你能听到吗?(让 A 知道 B 能听到,且能说话)


A:我也能听到。(让 B 知道 A 能听到)



只有经过三次的对话,才能确认自己的声音能被对方听到且能听到对方的声音。这也才能开展后续的对话。这里我们就不得不祭出经典的三次握手图了:



我们分析三次握手过程及每次握手后的状态如下:


1、A 主机发送标识 SYN=1(SYN 表示 A 请求跟 B 建立连接,前面在讲 TCP 头部时候有说到过),序号 Seq=x,第一次握手请求发送后 A 的状态为 SYN_SENT,B 在接收到请求后状态由 LISTEN 变为 SYN_RCVD!


2、B 主机收到连接请求后向 A 主机发送标识 SYN=1,ACK=1(SYN 表示 B 请求跟 A 建立连接,ACK 表示对 A 的连接请求进行应答),序号 Seq=y,确认号 Ack=(x+1),A 接收到 B 的确认后,状态变为 ESTABLISHED,B 的状态依然为 SYN_RCVD!


3、主机 A 收到后检查 Ack 是否正确,若正确,则发送标识 ACK=1(表示对 B 的连接请求进行应答),序号 Seq=(x+1),确认号 Ack=(y+1)。B 接收到 A 的确认后,A 和 B 的状态都变为 ESTABLISHED!


这里我们要注意的几点是:


1、图中的发送请求中中括号里面的 SYN、ACK 就是前面说 TCP 头部中的那几个标志位!而 Seq 和 Ack 分别代表序号和确认号。


2、接收方在接收到发送方发送的 Seq 后,应答一个 Ack,Ack 的值等于 Seq+1,表示已要发送方开始发送 Seq+1 位置的数据。


2、B 在接收到了 A 的连接请求后回复中同时发送了 SYN、ACK 两个标识位,将建立连接的请求和对 A 的应答在同一个包中发送了,这也是为什么只需要三次握手,就能建立连接。


我们依然向 www.17coding.info 发送请求,下面为三次握手的包:



在 info 那一栏,我们很明显的能看到发送的数据包头部有我们上面说到的那些标志位,还有 Seq、Ack 等头部信息,还有 Win、MSS 等头部选项数据!因此三次握手不仅仅是单纯建立连接,还会协商一些参数!


当我鼠标选择某一行时,如果这个数据包包含了对某个数据包的确认(也就是有 ACK 的标记),就能在对应的数据包的 No 列上面看到一个小勾勾,比如上面图中我鼠标选择的是第三次握手的数据包,在第二次握手的数据包前面就有个小勾勾。

[](

)为什么握手只需要三次而挥手需要四次?


通过三次握手,双方就建立了一个可靠的连接,就能进行数据的传输了!当数据传输完成,就得将连接关闭,因为连接也是一种资源!连接的关闭需要经过四次挥手!


为什么握手可以三次完成,但是挥手却需要四次呢?我偏要三次行不行?其实也没啥不可以的!比如下面的对话场景:


A:我说完了,你说完就挂电话吧!


B:好嘞,我也说完了,可以挂电话了!


A:好嘞,拜拜。


挂断…


这样三次对话就可以实现挥手了,但是在实际的网络中,当我发出一个请求的时候,可能服务器的响应体比较大,需要较长时间的传输!所以当客户端主动发起断开请求的时候,服务器先回应一个确认,等所有数据传输完毕后再发送服务器断开的请求。


A:我说完了,你说完就挂电话吧!


B:好嘞…


B:…


B:我也说完了,可以挂电话了


A:好嘞,拜拜


挂断…


所以大部分情况下都需要进行四次挥手!但是,在我个人的抓包实践中,也会有三次挥手就能完成断开连接的情况。


这里我们又不得不祭出经典的四次挥手图了:



我们分析四次挥手过程及每次挥手后的状态如下:


1、主机 A 发送标识 FIN=1(FIN 表示 A 请求关闭连接)用来关闭 A 到 B 的数据传输。此时 A 的状态为 FIN_WAIT_1!


2、主机 B 收到关闭请求后向 A 发送 ACK(ACK 表示应答 A 的关闭连接请求),A 不再向 B 发送数据。此时 A 的状态为 FIN_WAIT_2,B 为 CLOSE_WAIT!


3、主机 B 发送标识 FIN=1 用来关闭 B 到 A 的数据传输。此时 A 的状态为 TIME_WAIT,B 为 LAST_ACK!


4、主机 A 收到关闭请求后向 B 发送 ACK,此时 B 不再向 A 发送数据。此时 A、B 都关闭了,状态变为 CLOSED。


在图中我们能看到,A 的 TIME_WAIT 状态会持续 2MSL 再变成 CLOSED,MSL(Maximum Segment Lifetime)的中文可以译为“报文最大生存时间”!他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。那 TIME_WAIT 维持 2MSL 的作用是什么呢?


1、第 4 次挥手的时候主机 A 发送 ACK 到主机 B,如果发送完成后就直接就关闭连接,那如果由于网络原因 B 没有收到 ACK,那 B 就没法关闭连接了!因此 A 在回复确认后,还需要等待,万一 B 没有收到应答还会继续发送 FIN 的请求。


2、如果不等待 2MSL,那客户端的端口可能会被重用,如果再次用这个端口建立与服务器的连接,那前后两个使用相同四元组的的连接之间会形成干扰!



我们看上面向 www.17coding.info 发送请求的挥手数据包:



可能大家在抓包的时候不能立马看到四次挥手的数据包!那是因为在 HTTP1.1 及之后,默认都开启了长连接!也就是在一次请求之后,建立的连接并不会立马关闭,而是供后续的其他请求继续使用,以减少每次重新建立连接的资源消耗!如果想发出请求后立马能抓到四次挥手的数据包,可以设置 Http 的头部Connection:close。这样每次发送请求都能看到完整的三次握手四次挥手的过程啦!


[](


)TCP 是怎么保证可靠传输的?




保证传输的可靠我们前面已经说到了面向连接,建立连接是保证数据传输的第一步。那在连接建立之后的数据传输怎么保证可靠呢?


我们再次回到我们打电话的场景,一般在对话的过程中,都是得双方都有互动,给与对方回应。而不是一个人一个劲的说而另一方没有任何回应!比如下面场景:


A:跟你讲哦,我上周网上认识了一个妹子


B:嚯,牛逼啊!


A:然后我昨天约出来见面了


B:666 啊!然后呢?


A:然后我们 @#¥%……&


B:卧槽,你刚刚说啥我没听清,你再说一遍?…


这样的确认和应答就确保了双方的通信能够完整可靠。TCP 也采用了这种 y 应答和确认重传的机制,保证在不可靠的网络上实现可靠的传输。只要我没有收到确认,我就认为没有发送成功,就会重发。

[](

)停止等待协议


停止等待协议就是每次给对方发送数据包后,需要等待对方的回应然后再发送下一个数据包!停止等待协议会出现如下几种情况:


1、无差错情况:A 发送 M1 包到 B,B 收到后会给 A 一个确认,当 A 收到 B 的确认后再发送包 M2。



2、超时重传:A 发送 M1 包到 B,如果发送过程中包丢失,A 会重新发送。A 等待重发的时间是比一个报文的往返时间(RTT)稍微多一点。



3、确认丢失:如果 B 在给 A 发送确认的时候丢失,A 会重新发送 M1 包给 B,由于 B 已经处理过 M1 的数据包所以 B 会丢弃报文,然后重传确认 M1 给 A。



4、确认迟到:如果 A 发送数据包 M1 给 B,B 回复确认的时候延迟了。这时 A 又会重新发送包 M1 给 B,B 收到后丢弃数据包,然后重传确认 M1 给 A。这时 A 会收到多次确认,当第二次收到迟到的确认后 A 也会丢弃该确认。



我们从上面能看到,停止等待协议每次都是等到收到确认后再发下一个数据包。只要我没收到你给我的确认,我就认为你没有收到我发的数据包,我就会进行重发!这样虽然可靠,但是会导致信道利用率较低!

[](

)流水线传输


流水线传输就是每次发送多组数据包,不必每次发完一组就停下来等待对方的确认。由于信道上一直有数据不间断的传输,因此可以获得较高的信道利用率!


流水线传输如何保证可靠的呢?需要发送方维持发送窗口,假如发送窗口是 5,那 5 个数据包会同时发送,然后等确认!如果有收到接收方的确认,窗口就会滑动,进行第 6 个数据包的发送。


如果都是单个确认,可能效率会比较低,所以有了累计确认!也就是说假如发送方发送了数据包 1、2、3、4,接收方只需要回复对数据包 4 的确认,那表示 1234 数据包都已经收到了,就可以进行第五个数据包的发送了!假如发送了数据包 1、2、3、4,其中第三个数据包丢失,那该怎么确认呢?TCP 只会回复对数据包 2 的确认,并且对数据包 4 进行选择性确认(TCP 头部选项讲到过的 SACK),这样发送方就知道数据包 4 已经成功发送,只需要重发数据包 3。



继续前面抓包的例子,接收方并不是对每个数据包都进行确认,而是对多个数据包进行累计确认:



这里我们能看到服务器发送多个数据包后,客户端才进行了一次确认。


[](


)流量控制和拥塞控制




通过前面我们知道了,通过建立可靠的连接和确认机制,保证了 TCP 的连接的可靠!但是每个人使用的计算机的处理能力都是不一样的,我发送太快了对方处理不过来怎么办呢?通信双方怎么去协调发送和接收数据的频率呢?

[](

)以字节为单位的滑动窗口技术


在介绍 TCP 头部的时候,我们已经提到过滑动窗口,并且介绍了相关的控制参数 Win!也说到了接收窗口和发送窗口!那他们的关系是怎么样的呢?


假设现在 A 需要传输数据给 B,B 就先要告诉 A 自己的接收窗口有多大。A 根据 B 的接收窗口设置自己的发送窗口!A 的发送窗口时不能大于 B 的接收窗口的!在开始传输数据之前,初始的窗口设置如下图:



如上图我们能否看到,B 的接收窗口设置为 10 个字节,那 A 的发送窗口设置不能超过 10 个字节!如果开始传送数据,A 会将数据封装成多个数据包进行传输,如下图



在没有收到 B 的确认之前,A 的窗口不会滑动,也就是说最多能发 10 个字节的数据。如果 B 接受到数据且回复确认给了 A,那 A 的窗口则进行滑动,如下图:


![](https://i


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


mg-service.csdnimg.cn/img_convert/2c04771d0c5bf83cbfea07cec78a3c16.png)


这样,A 又可以进行第 11、12 个字节的发送啦!如果 B 的处理能力变弱了,也可以通知 A 将发送窗口调小!这样也也就很好的协调了双方的接收和发送能力!这也就很好的实现了 TCP 的可靠传输和流量控制!


上面的数据包继续发送,如果在发送过程中,3、4、5 这三个字节组成的数据包丢了,但是后面的数据却收到了,这时候 A 的发送窗口会移动么?



如果是这种情况,A 的发送窗口是不会移动的。B 在接收到后面数据包的时候回复给 A 的 Ack 会设置为 3,且在选项中设置一个 SACK(在 TCP 头部选项里面有描述),告诉 A 哪部分数据收到了,而哪部分数据需要进行重发!

[](

)拥塞控制


利用滑动窗口技术,可以很好的协调双方的收发能力。但是,网络状况是非常复杂的,且在同一个网络上可能有千千万万个发送方和接收方!如果大家都需要传输数据都需要占用网络,不做好控制措施,就会导致整个网络会堵塞甚至瘫痪。


如果我要从深圳开车去广州,我就会走高速。如果只有我一个人开车,那肯定能畅通无阻!但是高速公路不是我家的,大家都能通行!所以一到了节假日,大家都蜂拥而上,而高速的承运能力不会因为节假日而调整!这时候往往就需要交通管制、限流等措施去舒缓交通!



1、绿线代表理想状况下,如果高速公路的吞吐量为 100!当需要通过的车辆不超过 100 时,所有车辆都能顺利通过!当需要通过的车辆超过 100,那每次通行的车辆为 100,能提供的负载比较稳定。



2、红色代表没有任何交通管制情况下,如果高速公路的吞吐量为 100!当需要通过的车辆不超过 100 时,会出现轻微的塞车现象!但是随着车辆的增多,就会出现严重的阻塞,甚至瘫痪!



3、蓝色代表在交通管制下,如果高速公路的吞吐量为 100!当需要通过的车辆不超过 100 时,会出现轻微的塞车现象!但是随着车辆的增多,交通一直保存较高的负载,不会出现瘫痪的情况!


网络就好比高速公路,传输的数据包就好比要通过的车辆,而 TCP 则就更像一个交警,维护着数据传输的秩序!那 TCP 是怎么做的呢?

[](

)慢开始与拥塞避免


发送方维持一个 cwnd(拥塞窗口,注意这里的拥塞窗口不能大于前面说到的发送窗口!),刚开始拥塞窗口设置为 1。如果发现这个包没有丢失,则调整拥塞窗口为 2!如果又没有丢包,则调整拥塞窗口为 4!这样每次以 2 倍的速度一直增长到 16!然后 17、18、19 这样一个一个的增加,直到大小与发送窗口一致。这就是所谓的慢开始和拥塞避免,16 就是慢开始门限…



有没有得寸进尺的感觉!


我就蹭蹭不进去…



我就进去不动…



我就…

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
太难了,救救孩子吧,到现在还搞不懂TCP的三次握手四次挥手