个人做同城网站赚钱吗,wordpress 比特币,互联网出版中的网站建设策划,阿里云建设网站的步骤部分内容来源#xff1a;小林coding TCP四次挥手过程是怎样的
天下没有不散的宴席#xff0c;对于 TCP 连接也是这样#xff0c; TCP 断开连接是通过四次挥手方式
双方都可以主动断开连接#xff0c;断开连接后主机中的「资源」将被释放#xff0c;四次挥手的过程如下图…部分内容来源小林coding TCP四次挥手过程是怎样的
天下没有不散的宴席对于 TCP 连接也是这样 TCP 断开连接是通过四次挥手方式
双方都可以主动断开连接断开连接后主机中的「资源」将被释放四次挥手的过程如下图 1.客户端打算关闭连接此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文也即 FIN 报文之后客户端进入 FIN_WAIT_1 状态。
2.服务器收到报文后就向客户端发送 ACK 应答报文接着服务端进入 CLOSE_WAIT 状态。客户端收到服务器的 ACK 应答报文后之后进入 FIN_WAIT_2 状态。
3.等待服务器处理完数据后也向客户端发送 FIN 报文之后服务端进入 LAST_ACK 状态。
4.客户端收到服务器的 FIN 报文后回发 ACK 应答报文之后进入 TIME_WAIT 状态。服务器收到了 ACK 应答报文后就进入 CLOSE 状态至此服务器端已经完成连接的关闭。客户端经过 2MSL 一段时间后自动进入 CLOSE 状态至此客户端也完成连接的关闭。 你可以看到每个方向都需要一个 FIN 和一个 ACK因此通常被称为四次挥手。
这里一点需要注意是主动关闭连接的一方才有 TIME_WAIT 状态 为什么挥手需要四次
再来回顾下四次挥手双方发 FIN 包的过程就能理解为什么需要四次了。
关闭连接时客户端向服务端发送 FIN 时仅仅表示客户端不再发送数据了但是还能接收数据。服务器收到客户端的 FIN 报文时先回一个 ACK 应答报文而服务器端可能还有数据要处理和发送等服务器端不再发送数据时才发送 FIN 报文给客户端来表示同意现在关闭连接。
从上面过程可知服务器端通常需要等待完成数据的发送和处理所以服务端的 ACK 和 FIN 一般都会分开发送因此是需要四次挥手。
但是在特定情况下四次挥手是可以变成三次挥手的具体情况可以看这篇TCP 四次挥手可以变成三次吗 第一次挥手丢失了会发生什么
当客户端主动关闭方调用 close 函数后就会向服务端发送 FIN 报文试图与服务端断开连接此时客户端的连接进入到 FIN_WAIT_1 状态。
正常情况下如果能及时收到服务端被动关闭方的 ACK则会很快变为 FIN_WAIT_2 状态。
如果第一次挥手丢失了那么客户端迟迟收不到被动方的 ACK 的话也就会触发超时重传机制重传 FIN 报文
重发次数由 tcp_orphan_retries 参数控制。
当客户端传输 FIN 报文的次数超过 tcp_orphan_retries 后就不再发送 FIN 报文机会在等待一段时间时间为上一次超时的 2 倍如果还是没能收到第二次挥手那么直接进入 close 状态。 第二次挥手丢失了会发生什么
当服务端收到客户端的第一次挥手后就会先回一个 ACK 确认报文此时服务端的连接进入到 CLOSE_WAIT 状态。
在前面我们也提了ACK 报文是不会重传的所以如果服务端的第二次挥手丢失了客户端就会触发超时重传机制重传 FIN 报文直到收到服务端的第二次挥手或者达到最大的重传次数 这里提一下当客户端收到第二次挥手也就是收到服务端发送的 ACK 报文后客户端就会处于 FIN_WAIT_2 状态在这个状态需要等服务端发送第三次挥手也就是服务端的 FIN 报文。 close () 函数与 FIN_WAIT_2 状态的时长
当使用 close () 函数关闭连接时该连接就无法再发送和接收数据了。
为了避免 FIN_WAIT_2 状态持续过长时间占用资源系统通过 tcp_fin_timeout 这个参数来控制处于该状态下连接的持续时长默认值是 60 秒。也就是说超过 60 秒还没收到服务端的 FIN 报文连接就会进行相应处理比如释放资源等 shutdown () 函数与 FIN_WAIT_2 状态
果主动关闭方使用 shutdown () 函数关闭连接并且指定只关闭发送方向接收方向并没有关闭那么主动关闭方仍然可以接收数据。
在这种情况下如果主动关闭方一直没有收到服务端发送的第三次挥手FIN 报文那么主动关闭方的连接就会一直处于 FIN_WAIT_2 状态不会超时关闭
因为它还在等待接收服务端的数据所以不会按照 tcp_fin_timeout 设定的时间去结束这个状态 close() 函数
在网络编程中close() 函数通常用于关闭一个已经建立的 TCP 连接。当调用 close() 函数时意味着该连接不再用于发送和接收数据。从 TCP 协议的角度来看调用 close() 会触发 TCP 连接的关闭流程即发送一个 FINFinish报文给对方表示本方已经没有数据要发送了 shutdown() 函数
shutdown() 函数也是用于关闭 TCP 连接的但它比 close() 函数更加灵活。shutdown() 函数可以指定关闭连接的不同方向即只关闭发送方向、只关闭接收方向或者同时关闭两个方向 第三次挥手丢失会发生什么
当服务端被动关闭方收到客户端主动关闭方的 FIN 报文后内核会自动回复 ACK同时连接处于 CLOSE_WAIT 状态顾名思义它表示等待应用进程调用 close 函数关闭连接。
此时内核是没有权利替代进程关闭连接必须由进程主动调用 close 函数来触发服务端发送 FIN 报文。
服务端处于 CLOSE_WAIT 状态时调用了 close 函数内核就会发出 FIN 报文
同时连接进入 LAST_ACK 状态等待客户端返回 ACK 来确认连接关闭。 如果迟迟收不到这个 ACK服务端就会重发 FIN 报文重发次数仍然由 tcp_orphan_retries 参数控制这与客户端重发 FIN 报文的重传次数控制方式是一样的 第四次挥手丢失会发生什么
当客户端收到服务端的第三次挥手的 FIN 报文后就会回 ACK 报文也就是第四次挥手此时客户端连接进入 TIME_WAIT 状态。
在 Linux 系统TIME_WAIT 状态会持续 2MSL 后才会进入关闭状态。
然后服务端被动关闭方没有收到 ACK 报文前还是处于 LAST_ACK 状态。
如果第四次挥手的 ACK 报文没有到达服务端服务端就会重发 FIN 报文重发次数仍然由前面介绍的 tcp_orphan_retries 参数控制 为什么需要Time_Wait状态
主动发起关闭连接的一方才会有 TIME-WAIT 状态
需要 TIME-WAIT 状态主要是两个原因
防止历史连接中的数据被后面相同四元组的连接错误的接收保证「被动关闭连接」的一方能被正确的关闭 原因一防止历史连接中的数据被后面相同四元组的连接错误的接收
TIME_WAIT状态保持一定时间防止错误接收数据
为了能更好的理解这个原因我们先来了解序列号SEQ和初始序列号ISN。
序列号是一个头字段标识了 TCP 发送端到 TCP 接收端的数据流的一字节因为 TCP 是面向字节流的可靠传输协议为了确保数据的顺序性和可靠性TCP 为每个传输方向上的每个字节都赋予了一编号以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。序列号是一个 32 位的无符号数因此在到达 4G 之后会循环到 0。初始序列号在 TCP 建立连接的时候客户端和服务端都会各自生成一个初始序列号它是基于时钟生成的一个随机数来保证每个连接都有不同的初始序列号。初始序列号与 32 位的序列号数该计数器的数值每 4 微秒加 1循环一次需要 4.55 小时 通过前面我们知道序列号和初始化序列号并不是无限递增的会发生回绕为初始值的情况
这意味着无法根据序列号来判断新老数据。 假设 TIME-WAIT 没有等待时间或时间过短被延迟的数据包抵达后会发生什么呢
服务端在关闭连接之前发送的 SEQ 301 报文被网络延迟了。接着服务端以相同的四元组重新打开了新连接前面被延迟的 SEQ 301 这时抵达了客户端而且该数据报文的序列号刚好在客户端接收窗口内因此客户端会正常接收这个数据报文但是这个数据报文是上一个连接残留下来的这样就产生数据错乱等严重的问题。
为了防止历史连接中的数据被后面相同四元组的连接错误的接收
因此 TCP 设计了 TIME_WAIT 状态状态会持续 2MSL 时长这个时间足以让两个方向上的数据包都被丢弃
使得原来连接的数据包在网络中自然消失再出现的数据包一定都是新建立连接所产生的 原因二保证「被动关闭连接」的一方能被正确的关闭
客户端必须等待足够长的时间确保服务端能够收到 ACK
TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收从而帮助其正常关闭
如果客户端主动关闭方最后一次 ACK 报文第四次挥手在网络中丢失了那么按照 TCP 可靠性原则服务端被动关闭方会重发 FIN 报文。 假设客户端没有 TIME_WAIT 状态而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态如果该 ACK 报文丢失了服务端则重传的 FIN 报文而这时客户端已经进入到关闭状态了在收到服务端重传的 FIN 报文后就会回 RST 报文 服务端收到这个 RST 并将其解释为一个错误Connection reset by peer
这对于一个可靠的协议来说不是一个优雅的终止方式 为了防止这种情况出现客户端必须等待足够长的时间确保服务端能够收到 ACK如果服务端没有收到 ACK那么就会触发 TCP 重传机制服务端会重新发送一个 FIN这样一来一往刚好两个 MSL 的时间 TIME_WAIT过多会有什么危害
过多的 TIME-WAIT 状态主要的危害有两种
占用系统资源比如文件描述符、内存资源、CPU 资源、线程资源等占用端口资源端口资源也是有限的一般可以开启的端口为 3276861000 也可以通过 net.ipv4.ip_local_port_range 参数指定范围。
客户端和服务端 TIME_WAIT 过多造成的影响是不同的。
客户端的 TIME_WAIT 状态过多
客户端发起连接方都是和「目的 IP 目的 PORT 」都一样的服务端建立连接的话当客户端的 TIME_WAIT 状态连接过多的话就会受端口资源限制
如果占满了所有端口资源那么就无法再跟「目的 IP 目的 PORT」都一样的服务端建立连接了。 不过即使是在这种场景下只要连接的是不同的服务端端口是可以重复使用的所以客户端还是可以向其他服务端发起连接的这是因为内核在定位一个连接的时候是通过四元组源 IP、源端口、目的 IP、目的端口信息来定位的并不会因为客户端的端口一样而导致连接冲突。 服务端主动发起关闭连接方的 TIME_WAIT 状态过多
如果服务端主动发起关闭连接方的 TIME_WAIT 状态过多并不会导致端口资源受限
因为服务端只监听一个端口而且由于一个四元组唯一确定一个 TCP 连接因此理论上服务端可以建立很多连接但是 TCP 连接过多会占用系统资源比如文件描述符、内存资源、CPU 资源、线程资源等 服务器出现大量 TIME_WAIT 状态的原因有哪些
首先要知道 TIME_WAIT 状态是主动关闭连接方才会出现的状态所以如果服务器出现大量的 TIME_WAIT 状态的 TCP 连接就是说明服务器主动断开了很多 TCP 连接。
问题来了什么场景下服务器端会主动断开连接呢
第一个场景HTTP 没有使用长连接第二个场景HTTP 长连接超时第三个场景HTTP 长连接的请求数量达到上限 如果已经建立了连接但是客户端突然出现故障了怎么办
客户端出现故障指的是客户端的主机发生了宕机或者断电的场景。发生这种情况的时候如果服务端一直不会发送数据给客户端那么服务端是永远无法感知到客户端宕机这个事件的也就是服务端的 TCP 连接将一直处于 ESTABLISH 状态占用着系统资源。
为了避免这种情况TCP 搞了个保活机制。这个机制的原理是这样的
定义一个时间段在这个时间段内如果没有任何连接相关的活动TCP 保活机制会开始作用每隔一个时间间隔发送一个探测报文该探测报文包含的数据是异常小如果连续几个探测报文都没有得到响应则认为当前的 TCP 连接已经死亡系统内核将该错误信息通知给上层应用程序 说一下TCP的保活机制
TCP保活机制的原理
定义一个时间段在这个时间段内如果没有任何连接相关的活动TCP 保活机制会开始作用每隔一个时间间隔发送一个探测报文该探测报文包含的数据是异常小如果连续几个探测报文都没有得到响应则认为当前的 TCP 连接已经死亡系统内核将该错误信息通知给上层应用程序 如果已经建立了连接但是服务端的进程崩溃会发生什么
TCP 的连接信息是由内核维护的所以当服务端的进程崩溃后内核需要回收该进程的所有 TCP 连接资源于是内核会发送一次挥手 FIN 报文后续的挥手过程也都是在内核完成并不需要进程的参与所以即使服务端的进程退出了还是能与客户端完成 TCP 四次挥手的过程。
我自己做了个实验使用 kill -9 来模拟进程崩溃的情况
发现在 kill 掉进程后服务端会发送 FIN 报文与客户端进行四次挥手