TCP四次挥手

一、TCP四次挥手

第一次挥手

客户端打算关闭连接,此时会将TCP首部的FIN标志位标为1,将此报文发送给服务端,发送之后,客户端进入FIN_WAIT_1状态。

第二次挥手

服务端收到报文后,向客户端发送ACK应答报文,之后服务端进入CLOSED_WAIT状态。客户端收到ACK报文之后进入FIN_WAIT_2状态。

第三次挥手

等待服务端处理完数据,服务端向客户端发送FIN报文,之后服务端进入LAST_ACK状态。

第四次挥手

客户端收到服务端的FIN报文之后,给服务端发送一个ACK,之后进入TIME_WAIT状态。客户端在经过2MSL之后,自动进入CLOSED状态,到这里客户端完成连接的关闭。服务端在收到客户端的ACK应答报文之后,就进入了CLOSED状态,至此,服务端完成了连接的关闭。
在这里插入图片描述
主动关闭连接的一方才具有TIME_WAIT的状态,这点很重要。

二、四次回收的原因

  • 关闭连接时,客户端向服务端发送FIN,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务端收到客户端的FIN报文后,先回一个应答报文,因为服务端可能还有数据需要处理和发送,等服务器不再发送数据时,才发送FIN报文给客户端表示同意现在关闭连接。
  • 服务端通常要等待完成数据的发送和处理,所以服务端的ACK和FIN会分开发,从而比三次握手多了一次。

三、TIME_WAIT状态详解

TIME_WAIT=2MSL的原因

MSL(Maximum Segement Lifetime):报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。
2MSL的时间是从客户端接收到FIN后发送ACK开始计时的。如果在TIME-WAIT 时间内,因为客户端的ACK没有传输到服务端,客户端又接收到了服务端重发的FIN报文,那么2MSL时间将重新计时。
在Linux系统里2MSL默认是60秒,那么一个MSL也就是 30 秒。Linux系统停留在TIME_WAIT的时间为固定的60秒。(如果要修改TIME_WAIT的时间长度,只能修改Linux内核代码里TCP_TIMEWAIT_LEN 的值,并重新编译 Linux内核。)

TIME_WAIT的作用

  • 防止旧连接的数据包
    经过2MSL的时间足以让两个方向的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
  • 保证连接正确的关闭
    等待足够的时间以确保最后的ACK能让被动关闭方接收,从而帮其正常关闭。

TIME_WAIT没有等待时间或者时间太短导致的问题

  • 客户端四次挥手的最后⼀个ACK报文如果在网络中被丢失了,此时如果客户端TIME_WAIT过短或没有,则就直接进入了CLOSED状态了,那么服务端则会⼀直处在LASE_ACK状态。
  • 当客户端发起建立连接的 SYN 请求报文后,服务端会发送 RST报文给客户端,连接建立的过程就会被终止。

如果等待的时间足够长的话

  • 服务端正常收到四次挥手的最后⼀个ACK报文,则服务端正常关闭连接。
  • 服务端没有收到四次挥⼿的最后⼀个ACK报文时,则会重发FIN关闭连接报文并等待新的ACK报文。

过多的TIME_WAIT导致的问题

  • 客户端端口资源限制
    • 客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就65536个,被占满就会导致无法创建新的连接。
  • 服务端受系统资源限制
    • 由于⼀个四元组表示TCP连接,理论上服务端可以建立很多连接,服务端确实只监听⼀个端口,但是会把连接扔给处理线程,所以理论上监听的端口可以继续监听。
    • 但是线程池处理不了那么多⼀直不断的连接了。所以当服务端出现大量TIME_WAIT时,系统资源被占满时,会导致处理不过来新的连接。

优化TIME_WAIT

  • 连接复用
    打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项,
    可以复用处于TIME_WAIT的socket 为新的连接所用。 有⼀点需要注意的是,tcp_tw_reuse功能只能用于客户端(连接发起方),因为开启了该功能,在调用connect() 函数时,内核会随机找⼀个time_wait 状态超过1秒的连接给新的连接复用。
  • 连接重置
    net.ipv4.ip_local_port_range net.ipv4.tcp_max_tw_buckets,默认值是18000,当系统中处于TIME_WAIT的连接一旦超过这个值时,系统就会将后面的TIME_WAIT连接状态重置。 这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题多,不推荐使用。
  • 直接关闭
    使用SO_LINGER,应用强制使用RST关闭,如果 l_onoff 为非0, 且 l_linger 值为 0,那么调用close 后,会立刻发送⼀个RST标志给对端,该TCP连接 将跳过四次挥手,也就跳过了TIME_WAIT状态,直接关闭。 但这为跨越 TIME_WAIT状态提供了⼀个可能,不过是⼀个非常危险的行为,不值得提倡。

四、已经建立连接,客户端出现故障

TCP保活机制

TCP 有⼀个机制是保活机制。这个机制的原理是这样的

  • net.ipv4.tcp_tw_reuse = 1
  • net.ipv4.tcp_timestamps=1(默认即为 1)
  • so_linger.l_onoff = 1
  • so_linger.l_linger = 0
  • setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger))

定义⼀个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔⼀个时间间隔,发送⼀个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值

  • net.ipv4.tcp_keepalive_time=7200 :表示保活时间是 7200 秒(2小时),也就2小时内如果没有任何连接相关的活动,则会启动保活机制。
  • net.ipv4.tcp_keepalive_intvl=75:表示每次检测间隔 75 秒。
  • net.ipv4.tcp_keepalive_probes=9:表示检测9次无响应,认为对方是不可达的,从而中断本次的连接。

在 Linux 系统中,最少需要经过 2小时11分15秒才可以发现⼀个死亡连接。

保活机制需考虑的问题

  • 对端程序是正常正作的。当TCP保活的探测报文发送给对端, 对端会正常响应,这样TCP保活时间会被重置,等待下⼀个TCP保活时间的到来。
  • 对端程序崩溃并重启。当TCP保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生⼀个RST报文,这样很快就会发现TCP连接已经被重置。
  • 是对端程序崩溃,或对端由于其他原因导致报文不可达。当TCP保活的探测报⽂发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP会报告该TCP连接已经死亡。
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值