TCP数据接收之入口

IP层组合出一包数据后,如果数据包首部的协议字段表明上层协议为TCP,则调用TCP的tcp_v4_rcv()函数将数据传递给传输层继续处理,传输层的整体处理过程是非常复杂的,这篇笔记就先来看看传输层的入口处是如何处理的。1. 三个队列TCP对输入数据包的整体处理流程可以简单的用下图表达:从上图中可以看到TCP的接收过程会涉及到三个队列:prequeue队列、receive队列以及backl...
摘要由CSDN通过智能技术生成

IP层组合出一包数据后,如果数据包首部的协议字段表明上层协议为TCP,则调用TCP的tcp_v4_rcv()函数将数据传递给传输层继续处理,传输层的整体处理过程是非常复杂的,这篇笔记就先来看看传输层的入口处是如何处理的。

1. 三个队列

TCP对输入数据包的整体处理流程可以简单的用下图表达:
在这里插入图片描述

从上图中可以看到TCP的接收过程会涉及到三个队列:prequeue队列、receive队列以及backlog队列,这里首先介绍下这三个队列的作用,然后再跟踪源代码实现。
从数据接收的角度考虑,可以将TCP的传输控制块的状态分为如下三种:

  1. 用户进程正在读写数据,此时TCB是被锁定的
  2. 用户进程正在读写数据,但是因为没有数据可用而进入了休眠态,等待数据可用,这时TCB是不会被用户进程锁定的
  3. 用户进程根本就没有在读写数据,当然这时TCB也不会被用户进程锁定

再考虑一点,由于协议栈对输入数据包的处理实际上都是软中断中进行的,出于性能的考虑,我们总是期望软中断能够快速的结束。

这样,再来理解上图:

  • 如果被用户进程锁定,那么处于情形一,此时由于互斥,没得选,为了能快速结束软中断处理,将数据包放入到backlog队列中,这类数据包的真正处理是在用户进程释放TCB时进行的;
  • 如果没有被进程锁定,那么首先尝试将数据包放入prequeue队列,原因还是为了尽快让软中断结束,这种数据包的处理是在用户进程读数据过程中处理的;
  • 如果没有被进程锁定,prequeue队列也没有接受该数据包(出于性能考虑,比如prequeue队列不能无限制增大),那么没有更好的办法了,必须在软中断中对数据包进行处理,处理完毕后将数据包加入到receive队列中。

综上,可以总结如下:

  • 放入receive队列的数据包都是已经被TCP处理过的数据包,比如校验、回ACK等动作都已经完成了,这些数据包等待用户空间程序读即可;相反,放入backlog队列和prequeue队列的数据包都还需要TCP处理,实际上,这些数据包也都是在合适的时机通过tcp_v4_do_rcv()处理的;
  • 设计三个队列是有其特殊目的的,理解背后的设计意图非常重要。

2. 接收入口:tcp_v4_rcv()

tcp_v4_rcv()是TCP协议的接收入口函数。

int tcp_v4_rcv(struct sk_buff *skb)
{
   
	const struct iphdr *iph;
	struct tcphdr *th;
	struct sock *sk;
	int ret;
	//非本机数据包扔掉
	if (skb->pkt_type != PACKET_HOST)
		goto discard_it;

	/* Count it even if it's bad */
	TCP_INC_STATS_BH(TCP_MIB_INSEGS);

	//下面主要是对TCP段的长度进行校验。注意pskb_may_pull()除了校验,还有一个额外的功能,
	//如果一个TCP段在传输过程中被网络层分片,那么在目的端的网络层会重新组包,这会导致传给
	//TCP的skb的分片结构中包含多个skb,这种情况下,该函数会将分片结构重组到线性数据区

	//保证skb的线性区域至少有20个字节数据
	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
		goto discard_it;

	th = tcp_hdr(skb);

	if (th->doff < sizeof(struct tcphdr) / 4)
		goto bad_packet;
	//保证skb的线性区域至少包括实际的TCP首部
	if (!pskb_may_pull(skb, th->doff * 4))
		goto discard_it;

	//数据包校验相关,校验失败,则悄悄丢弃,不产生任何的差错报文
	/* An explanation is required here, I think.
	 * Packet length and doff are validated by header prediction,
	 * provided case of th->doff==0 is eliminated.
	 * So, we defer the checks. */
	if (!skb_csum_unnecessary(skb) && tcp_v4_checksum_init(skb))
		goto bad_packet;

	//初始化skb中的控制块
	th = tcp_hdr(skb);
	iph = ip_hdr(skb);
	TCP_SKB_CB(skb)->seq = ntohl(th->seq);
	TCP_SKB_CB(skb)->end_seq = (TCP_SKB_CB(skb)->seq + th->syn + th->fin 
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值