当TCP主动关闭一端调用了close()来执行连接的完全关闭时会执行以下流程,本端发送FIN给对端,对端回复ACK,本端进入FIN_WAIT_2状态,此时只有对端发送了FIN,本端才会进入TIME_WAIT状态,为了防止对端不发送关闭连接的FIN包给本端,将会在进入FIN_WAIT_2状态时,设置一个FIN_WAIT_2定时器,如果该连接超过一定时限,则进入CLOSE状态;
注意:上述是针对close调用完全关闭连接的情况,shutdown执行半关闭不会启动FIN_WAIT_2定时器;
启动定时器:
close系统调用关闭连接最终会调用到tcp_close函数,其中当状态为TCP_FIN_WAIT2时,如果有设置该状态等待时间linger2,且等待时间大于TCP_TIMEWAIT_LEN则启动FIN_WAIT_2定时器;
1 void tcp_close(struct sock *sk, long timeout) 2 { 3 if (sk->sk_state == TCP_FIN_WAIT2) { 4 struct tcp_sock *tp = tcp_sk(sk); 5 if (tp->linger2 < 0) { 6 tcp_set_state(sk, TCP_CLOSE); 7 tcp_send_active_reset(sk, GFP_ATOMIC); 8 __NET_INC_STATS(sock_net(sk), 9 LINUX_MIB_TCPABORTONLINGER); 10 } else { 11 const int tmo = tcp_fin_time(sk); 12 13 if (tmo > TCP_TIMEWAIT_LEN) { 14 inet_csk_reset_keepalive_timer(sk, 15 tmo - TCP_TIMEWAIT_LEN); 16 } else { 17 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); 18 goto out; 19 } 20 } 21 } 22 }
定时器回调函数:
定时器超时会调用tcp_keepalive_timer处理函数,当连接处于FIN_WAIT_2状态,且socket即将关闭,则继续判断FIN等待时间,若有剩余时间,则进入tcp_time_wait函数处理;否则发送rst,并关闭连接;
注:因函数是保活定时器和WAIT_2共用的,我们省略了部分WAIT_2无关代码;
1 static void tcp_keepalive_timer (unsigned long data) 2 { 3 /* 省略部分代码 */ 4 5 /* 处于fin_wait2且socket即将关闭,用作FIN_WAIT_2定时器 */ 6 if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) { 7 8 /* 停留在FIN_WAIT_2的停留时间>=0 */ 9 if (tp->linger2 >= 0) { 10 /* 获取在FIN_WAIT_2时间与TIMEWAIT时间差 */ 11 const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN; 12 13 /* 时间差>0,则进入TIME_WAIT状态 */ 14 if (tmo > 0) { 15 tcp_time_wait(sk, TCP_FIN_WAIT2, tmo); 16 goto out; 17 } 18 } 19 20 /* 发送rst */ 21 tcp_send_active_reset(sk, GFP_ATOMIC); 22 goto death; 23 } 24 25 /* 省略部分代码 */ 26 }
tcp_time_wait后续补充;