Java:吊打面试官!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题( 九 )


也就是说 , TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收 , 从而帮助其正常关闭 。
假设 TIME-WAIT 没有等待时间或时间过短 , 断开连接会造成什么问题呢?
Java:吊打面试官!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
本文插图
没有确保正常断开的异常

  • 如上图红色框框客户端四次挥手的最后一个 ACK 报文如果在网络中被丢失了 , 此时如果客户端 TIME-WAIT 过短或没有 , 则就直接进入了 CLOSE 状态了 , 那么服务端则会一直处在 LASE-ACK 状态 。
  • 当客户端发起建立连接的 SYN 请求报文后 , 服务端会发送 RST 报文给客户端 , 连接建立的过程就会被终止 。
如果 TIME-WAIT 等待足够长的情况就会遇到两种情况:
  • 服务端正常收到四次挥手的最后一个 ACK 报文 , 则服务端正常关闭连接 。
  • 服务端没有收到四次挥手的最后一个 ACK 报文时 , 则会重发 FIN 关闭连接报文并等待新的 ACK 报文 。
所以客户端在 TIME-WAIT 状态等待 2MSL 时间后 , 就可以保证双方的连接都可以正常的关闭 。
TIME_WAIT 过多有什么危害?
如果服务器有处于 TIME-WAIT 状态的 TCP , 则说明是由服务器方主动发起的断开请求 。
过多的 TIME-WAIT 状态主要的危害有两种:
  • 第一是内存资源占用;
  • 第二是对端口资源的占用 , 一个 TCP 连接至少消耗一个本地端口;
第二个危害是会造成严重的后果的 , 要知道 , 端口资源也是有限的 , 一般可以开启的端口为 32768~61000 , 也可以通过如下参数设置指定:
net.ipv4.ip_local_port_range
如果服务端 TIME_WAIT 状态过多 , 占满了所有端口资源 , 则会导致无法创建新连接 。
如何优化 TIME_WAIT?
这里给出优化 TIME-WAIT 的几个方式 , 都是有利有弊:
  • 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;
  • net.ipv4.tcp_max_tw_buckets
  • 程序中使用 SO_LINGER, 应用强制使用 RST 关闭 。
方式一:net.ipv4.tcp_tw_reuse 和 tcp_timestamps
如下的 Linux 内核参数开启后 , 则可以复用处于 TIME_WAIT 的 socket 为新的连接所用 。
net.ipv4.tcp_tw_reuse = 1
使用这个选项 , 还有一个前提 , 需要打开对 TCP 时间戳的支持 , 即
net.ipv4.tcp_timestamps=1(默认即为 1)
这个时间戳的字段是在 TCP 头部的「选项」里 , 用于记录 TCP 发送方的当前时间戳和从对端接收到的最新时间戳 。
由于引入了时间戳 , 我们在前面提到的 2MSL 问题就不复存在了 , 因为重复的数据包会因为时间戳过期被自然丢弃 。
温馨提醒:net.ipv4.tcp_tw_reuse要慎用 , 因为使用了它就必然要打开时间戳的支持 net.ipv4.tcp_timestamps , 当客户端与服务端主机时间不同步时 , 客户端的发送的消息会被直接拒绝掉 。 小林在工作中就遇到过 。。。 排查了非常的久
方式二:net.ipv4.tcp_max_tw_buckets
这个值默认为 18000 , 当系统中处于 TIME_WAIT 的连接一旦超过这个值时 , 系统就会将所有的 TIME_WAIT 连接状态重置 。
这个方法过于暴力 , 而且治标不治本 , 带来的问题远比解决的问题多 , 不推荐使用 。
方式三:程序中使用 SO_LINGER
我们可以通过设置 socket 选项 , 来设置调用 close 关闭连接行为 。
struct linger so_linger;so_linger.l_onoff = 1;so_linger.l_linger = 0;setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger,sizeof(so_linger));


推荐阅读