一、TCP_TW概述
TCP_TW全称为TCP Time Wait状态,指的是一种TCP协议中的状态,一般出现在TCP连接断开的过程中。在一个TCP连接的关闭过程中,经过FIN、ACK、ACK的握手确认,最终由一方发送最后的ACK包,这个包在发送后需要等待一段时间后才能进入CLOSE状态。这个等待时间就是TCP_TW状态。
TCP_TW状态主要的目的是确保确认方正确的接受了另外一方的FIN包,并在此时判断一些延迟的重复数据包等问题。TCP_TW状态的默认等待时间是2分钟,这个时间可以通过修改操作系统的参数来进行设置。
二、TCP_TW状态产生原因
TCP_TW状态的主要原因是防止由于网络原因,FIN包或者ACK包没有到达对方。如果没有进入TCP_TW状态,那么就会立即回收socket和相关资源,这个时候FIN包到了接收方,接收方返回一个ACK包,但是由于sender已经释放了相关资源,这个时候ACK就无处可去,接收方无法获取到这个ACK,这就不只是一个连接的问题了,可能会导致链接资源耗尽等问题。
三、TCP_TW如何回收
TCP_TW状态的回收是通过定时器来完成的。每当一个socket进入TCP_TW状态时,系统就会开启一个定时器,并等待固定时间,比如2分钟。在这个时间内,如果接收到对方的ACK包,那么这个定时器就会被立即销毁,并进入CLOSE状态。
然而,在TCP_TW状态下,如果由于ACK漏接或者其他原因,这个时间到了之后还没有收到对方的ACK包,那么这个socket就需要被回收。如果这个socket处于端口共享状态,那么socket实际上不会被立即回收,而是进入假CLOSE状态。这个时候,TCP协议会重新分配一个随机数seq,同时重置计时器,如果在一段时间之内,没有收到对方发来的重复的ACK包,那么socket就会被彻底关闭。
四、TCP_TW状态需要注意的问题
TCP_TW状态实际上是一个非常重要的状态,需要注意以下三个问题:
1. 系统中同时存在大量TCP_TW状态的socket就会导致系统资源的压力,可能会引导奔溃。为了避免这种情况,可以通过修改内核参数来限制TCP_TW状态的数量。一般来说,建议将内核参数设置为6000左右。
2. 防止SYN等IP攻击。攻击者可以通过大量的SYN包来伪造TCP协议中的一个socket,从而放置于TCP_TW状态。如果这种攻击成功,系统的队列资源将被占满,无法被其他请求使用,系统就会崩溃。为了防止这个问题,可以在系统中添加过滤规则,阻止来自可疑IP地址的请求。
3. 在协议栈中,应用程序和内核之间的性能问题。每进入一个TCP_TW状态,都意味着会在内核中创建一个资源对象,这个资源对象的使用可能会带来一些性能问题。如果TCP_TW状态对象过多,就有可能导致内存使用过高,而且更加影响网络系统的性能。
五、TCP_TW状态的代码实现
#include#include #include #include int tcp_time_wait(struct sock *sk, int state, int timeo) { int flags = sk->sk_shutdown; whitle (timeo) { flags |= send_sigurg(sk->sk_socket, NULL); release_sock(sk); if (signal_pending(current)) goto ; if (sk->sk_state != state) goto ; if (tcp_out_of_resources(sk, GFP_ATOMIC)) tcp_enter_memory_pressure(sk); if (time_after(jiffies, timeo)) goto ; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(to_backoff(timeo)); set_current_state(TASK_RUNNING); lock_sock(sk); } exit_reset: if (TCP_SKB_CB(sk->sk_send_head)->when == 0) TCP_SKB_CB(sk->sk_send_head)->when = tcp_time_stamp; if (sk->sk_state != TCP_TIME_WAIT) sk->sk_shutdown = flags|SEND_SHUTDOWN; sk->sk_state = TCP_TIME_WAIT; tcp_set_state(sk, TCP_TIME_WAIT); tcp_time_wait(sk, timeo); exit_ok: release_sock(sk); return 1; exit_signal: release_sock(sk); return -EINTR; }