四次挥手建立一个 TCP 连接需要三次握手,而终止一个 TCP 连接需要经过四次挥手,这是由于 TCP 的半关闭特性造成的,TCP 提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力 。

文章插图
四次挥手
第一次挥手:客户端发送一个 FIN 报文(请求连接终止:FIN = 1),报文中会指定一个序列号 seq = u 。并停止再发送数据,但依然能够接收数据,主动关闭 TCP 连接 。此时客户端处于 FIN_WAIT-1 状态,等待服务端确认 。
第二次挥手:服务端收到 FIN 之后,会发送一个 ACK 报文,且把客户端的序号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态 。客户端收到服务端的确认后,进入 FIN-WAIT-2 状态,等待服务端发出的连接释放报文段 。
前两次挥手 既让服务端知道了客户端想释放连接,也让客户端知道了服务端了解了自己想要释放连接的请求 。于是,可以确认关闭客户端到服务端方向上的连接的 。第三次挥手:如果服务端也想断开连接,会发送 FIN 报文,且指定一个序列号 。此时服务端处于 LAST_ACK 状态,等待客户端的确认,并停止向客户端发送数据,但服务端仍能够接收从客户端传输过来的数据 。
第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答(ack = w+1),且把服务端的序列值 +1 作为自己 ACK 报文的序号值(seq=u+1),此时客户端处于 TIME_WAIT 状态,并在这个状态等待 2MSL 。服务端收到从客户端发出的 TCP 报文之后结束 LAST-ACK 阶段,进入 CLOSED 阶段 。
客户端等待完 2MSL 之后,结束 TIME-WAIT 阶段,进入 CLOSED 阶段,由此完成四次挥手 。
为什么客户端在 TIME-WAIT 阶段要等 2MSL?
为的是确认服务端是否收到客户端发出的 ACK 确认报文 。
当客户端发出最后的 ACK 确认报文后,并不能确定服务端能够接收到,所以在发完之后,等待 2MSL,服务端在 1MSL 内没有收到客户端发出的 ACK 确认报文,就会再次向客户端发出 FIN 报文 。
窗口滑动TCP 以 1 个段为单位,每发一个段就要进行一次确认应答的处理 。这种传输方式有个缺点,包往返时间越长通信性能越低 。

文章插图
为了解决这个问题,引入了窗口这个概念 。确认应答不再是以一个分段,而是以更大的单位进行确认,发送端发了一个段后不需要一直等待确认应答,而是能够继续发送 。

文章插图
窗口大小就是指无需等待确认应答而可以继续发送数据的最大值,窗口大小分为四个段 。

文章插图
如上图所示,白色部分就是窗口,窗口内的数据即便没有收到确认应答也可以发送出去 。
滑动窗口以外的部分包括尚未发送的数据和已经确认对端收到的数据 。收到确认应答后,会将窗口滑动到确认应答中的
序列号的位置 。这样就可以顺序的将多个段同时发送提高通信性能 。这种机制就是窗口滑动机制 。
窗口滑动机制下重发处理
在使用窗口控制时,可能会遇到段丢失 。在没有使用窗口控制,没有收到确认应答的数据会被重发,使用了窗口滑动,默写应答即使丢失也不需要进行重发 。

文章插图
结合下图所示,当某段报文丢失后,发送端会一直收到序号 1001 的确认应答,这个确认应答就像在提醒发送端"我想接收的是从 1001 开始的数据" 。
出现报文丢失的情况下,同一个序号的确认应答会重复发送 。当发送端连续三次收到同一个应答,就会将所对应的数据进行重发,这种重发机制叫做高速重发机制 。

文章插图
流量控制TCP 提供了一种机制可以让发送端根据接收端的实际接收能力控制发送的数据量 。
接收端主机向发送端主机通知自己可以接收的数据大小,发送端就会发送不超过这个限度的数据 。其实这个大小就是窗口大小,窗口大小的值是由接收端决定的 。
拥塞控制因为计算机是个共享的环境,也有可能会因为其他主机之间的通信造成网络拥堵 。在网络出现拥堵时,突然发送一个较大量的数据可能会导致网络瘫痪,如果在通信一开始就发送大量数据,也会引发一些问题 。
推荐阅读
- nginx实现TCP转发
- 坚持爬山你的身体“活跃”起来了!
- 这事儿每天坚持1小时,绝对抗癌!
- 在偏见、繁忙中这样坚持跑步才有效果
- 坚持跑步的人看过来,这些好处绝对亮瞎你的眼
- 早上几点起来跑步比较好呢?
- 拔罐减肥食谱,最好坚持
- 一文带你搞定TCP挥手
- TCP/IP 网络模型有几层?分别有什么用?
- 一文带你搞定TCP连接队列
