tcp四次挥手源码解析(服务器角度)

第一次第二次挥手

服务端收到客户端的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函数关闭本端的写端。

// 关闭一个socket
static 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;
		}
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值