time_wait过多的危害

第一是内存资源占用,这个目前看来不是太严重,基本可以忽略。 第二是对端口资源的占用,一个 TCP 连接至少消耗一个本地端口。要知道,端口资源也是 有限的,一般可以开启的端口为 32768~61000 ,也可以通过net.ipv4.ip_local_po rt_range指定,如果 TIME_WAIT 状态过多,会导致无法创建新连接。这个也是我们在一 开始讲到的那个例子

怎么避免time_wait过多

net.ipv4.tcp_max_tw_buckets 一个暴力的方法是通过 sysctl 命令,将系统值调小。这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将所有的 TIME_WAIT 连接状态重置,并 且只打印出警告信息。这个方法过于暴力,而且治标不治本,带来的问题远比解决的问题 多,不推荐使用 调低 TCP_TIMEWAIT_LEN,重新编译系统 这个方法是一个不错的方法,缺点是需要“一点”内核方面的知识,能够重新编译内核。我 想这个不是大多数人能接受的方式。 SO_LINGER 的设置

1
2
int setsockopt(int sockfd, int level, int optname, const void *optval,
socklen_t optlen);
1
2
3
4
struct linger {
  int l_onoff; /* 0=off, nonzero=on */
  int l_linger; /* linger time, POSIX specifies units as seconds */
}

如果l_onoff为非 0, 且l_linger值也为 0,那么调用 close 后,会立该发送一个 RST 标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了 TIME_WAIT 状态,直接关 闭。

如果l_onoff为非 0, 且l_linger的值也非 0,那么调用 close 后,调用 close 的线 程就将阻塞,直到数据被发送出去,或者设置的l_linger计时时间到。

第二种可能为跨越 TIME_WAIT 状态提供了一个可能,不过是一个非常危险的行为,不值 得提倡。

net.ipv4.tcp_tw_reuse:更安全的设置 那么什么是协议角度理解的安全可控呢?主要有两点:

  1. 只适用于连接发起方(C/S 模型中的客户端);
  2. 对应的 TIME_WAIT 状态的连接创建时间超过 1 秒才可以被复用

使用这个选项,还有一个前提,需要打开对 TCP 时间戳的支持,即net.ipv4.tcp_time stamps=1(默认即为 1)。 SO_REUSEADDR

1
2
int on = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

SO_REUSEADDR 是用户态的选项,SO_REUSEADDR 选项用来告诉操作系统内核,如果端口已被占用,但是 TCP 连接状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而 TCP 处于其他状态,重用端口时依旧得到“Address already in use”的错误信息。注意,这里一般都是连接的服务方。