tcp四次挥手源码解析(服务器角度)
发布日期:2021-05-09 16:03:11 浏览次数:14 分类:精选文章

本文共 5052 字,大约阅读时间需要 16 分钟。

第一次第二次挥手

服务端收到客户端的fin包进入TCP_CLOSE_WAIT状态。即第一个挥手完成,然后服务器发送ack给客户端,即第二次挥手完成。

static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th){   	sk->fin_seq = th->seq + skb->len + th->syn + th->fin;	if (!sk->dead) 	{   		sk->state_change(sk);		sock_wake_async(sk->socket, 1);	}	switch(sk->state) 	{   			case TCP_SYN_RECV:		case TCP_SYN_SENT:		case TCP_ESTABLISHED:			/*			 * move to CLOSE_WAIT, tcp_data() already handled			 * sending the ack.			 */			tcp_set_state(sk,TCP_CLOSE_WAIT);			if (th->rst)				sk->shutdown = SHUTDOWN_MASK;			break;		case TCP_CLOSE_WAIT:		case TCP_CLOSING:			/*			 * received a retransmission of the FIN, do			 * nothing.			 */			break;		case TCP_TIME_WAIT:			/*			 * received a retransmission of the FIN,			 * restart the TIME_WAIT timer.			 */			reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);			return(0);		case TCP_FIN_WAIT1:			/*			 * This case occurs when a simultaneous close			 * happens, we must ack the received FIN and			 * enter the CLOSING state.			 *			 * This causes a WRITE timeout, which will either			 * move on to TIME_WAIT when we timeout, or resend			 * the FIN properly (maybe we get rid of that annoying			 * FIN lost hang). The TIME_WRITE code is already correct			 * for handling this timeout.			 */			if(sk->ip_xmit_timeout != TIME_WRITE)				reset_xmit_timer(sk, TIME_WRITE, sk->rto);			tcp_set_state(sk,TCP_CLOSING);			break;		case TCP_FIN_WAIT2:			/*			 * received a FIN -- send ACK and enter TIME_WAIT			 */			reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);			sk->shutdown|=SHUTDOWN_MASK;			tcp_set_state(sk,TCP_TIME_WAIT);			break;		case TCP_CLOSE:			/*			 * already in CLOSE			 */			break;		default:			tcp_set_state(sk,TCP_LAST_ACK);				/* Start the timers. */			reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);			return(0);	}	return(0);}

第三次挥手

接着服务器调用close函数关闭本端的写端。

// 关闭一个socketstatic void tcp_close(struct sock *sk, int timeout){   	/*	 * We need to grab some memory, and put together a FIN,		 * and then put it into the queue to be sent.	 */		sk->inuse = 1;	// 监听型的socket要关闭建立的连接	if(sk->state == TCP_LISTEN)	{   		/* Special case */		tcp_set_state(sk, TCP_CLOSE);		// 关闭已经建立的连接		tcp_close_pending(sk);		release_sock(sk);		return;	}		sk->keepopen = 1;	sk->shutdown = SHUTDOWN_MASK;	if (!sk->dead) 	  	sk->state_change(sk);	if (timeout == 0) 	{   		struct sk_buff *skb;				/*		 *  We need to flush the recv. buffs.  We do this only on the		 *  descriptor close, not protocol-sourced closes, because the		 *  reader process may not have drained the data yet!		 */		// 销毁未处理的数据 		while((skb=skb_dequeue(&sk->receive_queue))!=NULL)			kfree_skb(skb, FREE_READ);		/*		 *	Get rid off any half-completed packets. 		 */		// 有小数据包则发送		if (sk->partial) 			tcp_send_partial(sk);	}			/*	 *	Timeout is not the same thing - however the code likes	 *	to send both the same way (sigh).	 */	 	if(timeout)	{   		tcp_set_state(sk, TCP_CLOSE);	/* Dead */	}	else	{   		if(tcp_close_state(sk,1)==1)		{   			tcp_send_fin(sk);		}	}	release_sock(sk);}

状态的处理逻辑在tcp_close_state函数中。由下面代码可知,当前是TCP_CLOSE_WAIT状态,即将进入LAST_ACK状态,然后发送一个fin包给客户端,即完成了第三次挥手。

// 根据socket的当前状态修改下一个状态 static int tcp_close_state(struct sock *sk, int dead){   		// 默认状态是关闭	int ns=TCP_CLOSE;	// 默认不需要发送fin包	int send_fin=0;	switch(sk->state)	{      // 还没有建立连接,直接转为关闭状态		case TCP_SYN_SENT:	/* No SYN back, no FIN needed */			break;		// 收到syn包并且发出了ack,对方也可能已经收到了ack把状态置为已建立,所以需要发送fin包,并把状态置为fin_wait1		case TCP_SYN_RECV:		case TCP_ESTABLISHED:	/* Closedown begin */			ns=TCP_FIN_WAIT1;			send_fin=1;			break;		// 本端已关闭,即已经发送了fin包,不需要再发送了,状态不变		case TCP_FIN_WAIT1:	/* Already closing, or FIN sent: no change */		case TCP_FIN_WAIT2:		case TCP_CLOSING:			ns=sk->state;			break;		// 直接置为关闭状态		case TCP_CLOSE:		case TCP_LISTEN:			break;		// 对端已关闭,现在是本端准备关闭,需要发送fin包,然后进入last_ack状态		case TCP_CLOSE_WAIT:	/* They have FIN'd us. We send our FIN and					   wait only for the ACK */			ns=TCP_LAST_ACK;			send_fin=1;	}		tcp_set_state(sk,ns);			/*	 *	This is a (useful) BSD violating of the RFC. There is a	 *	problem with TCP as specified in that the other end could	 *	keep a socket open forever with no application left this end.	 *	We use a 3 minute timeout (about the same as BSD) then kill	 *	our end. If they send after that then tough - BUT: long enough	 *	that we won't make the old 4*rto = almost no time - whoops	 *	reset mistake.	 */	// 如果是本端已经关闭,在等待对端关闭的状态,则设置一个定时器,如果超时还没有收到对端的fin包则强行关闭	if(dead && ns==TCP_FIN_WAIT2)	{   		int timer_active=del_timer(&sk->timer);		if(timer_active)			add_timer(&sk->timer);		else			reset_msl_timer(sk, TIME_CLOSE, TCP_FIN_TIMEOUT);	}		return send_fin;}

第四次挥手

这时候服务器socket的状态是LAST_ACK,收到客户端发送的最后ack包的时候即完成了第四次挥手,处理代码在tcp_ack中。

if (sk->state == TCP_LAST_ACK) 	{   		if (!sk->dead)			sk->state_change(sk);		if(sk->debug)			printk("rcv_ack_seq: %lX==%lX, acked_seq: %lX==%lX\n",				sk->rcv_ack_seq,sk->write_seq,sk->acked_seq,sk->fin_seq);		if (sk->rcv_ack_seq == sk->write_seq /*&& sk->acked_seq == sk->fin_seq*/) 		{   			flag |= 1;			tcp_set_state(sk,TCP_CLOSE);			sk->shutdown = SHUTDOWN_MASK;		}	}
上一篇:应用层发送一个数据包的时候,是如何到达网卡的(上)
下一篇:tcp四次挥手源码解析(客户端角度)

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2025年04月24日 07时42分59秒