概述

tcp_rcv_established用于处理已连接状态下的输入,处理过程根据首部预测字段分为快速路径和慢速路径;

1. 在快路中,对是有有数据负荷进行不同处理:

(1) 若无数据,则处理输入ack,释放该skb,检查是否有数据发送,有则发送;

(2) 若有数据,检查是否当前处理进程上下文,并且是期望读取的数据,若是则将数据复制到用户空间,若不满足直接复制到用户空间的情况,或者复制失败,则需要将数据段加入到接收队列中,加入方式包括合并到已有数据段,或者加入队列尾部,并唤醒用户进程通知有数据可读;

2. 在慢路中,会进行更详细的校验,然后处理ack,处理紧急数据,接收数据段,其中数据段可能包含乱序的情况,最后进行是否有数据和ack的发送检查;

源码分析
 he first three cases are guaranteed by proper pred_flags setting,
* the rest is checked inline. Fast processing is turned on in
* tcp_data_queue when everything is OK.
*/
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
const struct tcphdr *th, unsigned int len)
{
struct tcp_sock *tp = tcp_sk(sk); skb_mstamp_get(&tp->tcp_mstamp);
/* 路由为空,则重新设置路由 */
if (unlikely(!sk->sk_rx_dst))
inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
/*
* Header prediction.
* The code loosely follows the one in the famous
* "30 instruction TCP receive" Van Jacobson mail.
*
* Van's trick is to deposit buffers into socket queue
* on a device interrupt, to call tcp_recv function
* on the receive process context and checksum and copy
* the buffer to user space. smart...
*
* Our current scheme is not silly either but we take the
* extra cost of the net_bh soft interrupt processing...
* We do checksum and copy also but from device to kernel.
*/ tp->rx_opt.saw_tstamp = ; /* pred_flags is 0xS?10 << 16 + snd_wnd
* if header_prediction is to be made
* 'S' will always be tp->tcp_header_len >> 2
* '?' will be 0 for the fast path, otherwise pred_flags is 0 to
* turn it off (when there are holes in the receive
* space for instance)
* PSH flag is ignored.
*/ /* 快路检查&& 序号正确 && ack序号正确 */
if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags &&
TCP_SKB_CB(skb)->seq == tp->rcv_nxt &&
!after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) {
/* tcp头部长度 */
int tcp_header_len = tp->tcp_header_len; /* Timestamp header prediction: tcp_header_len
* is automatically equal to th->doff*4 due to pred_flags
* match.
*/ /* Check timestamp */
/* 有时间戳选项 */
if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) {
/* No? Slow path! */
/* 解析时间戳选项失败,执行慢路 */
if (!tcp_parse_aligned_timestamp(tp, th))
goto slow_path; /* If PAWS failed, check it more carefully in slow path */
/* 序号回转,执行慢路 */
if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < )
goto slow_path; /* DO NOT update ts_recent here, if checksum fails
* and timestamp was corrupted part, it will result
* in a hung connection since we will drop all
* future packets due to the PAWS test.
*/
} /* 无数据 */
if (len <= tcp_header_len) {
/* Bulk data transfer: sender */
if (len == tcp_header_len) {
/* Predicted packet is in window by definition.
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
*/
/*
有时间戳选项
&& 所有接收的数据段均确认完毕
保存时间戳
*/
if (tcp_header_len ==
(sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp); /* We know that such packets are checksummed
* on entry.
*/
/* 输入ack处理 */
tcp_ack(sk, skb, );
/* 释放skb */
__kfree_skb(skb); /* 检查是否有数据要发送,并检查发送缓冲区大小 */
tcp_data_snd_check(sk);
return;
}
/* 数据多小,比头部都小,错包 */
else { /* Header too small */
TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS);
goto discard;
}
}
/* 有数据 */
else {
int eaten = ;
bool fragstolen = false; /* 读取进程上下文 */
if (tp->ucopy.task == current &&
/* 期待读取的和期待接收的序号一致 */
tp->copied_seq == tp->rcv_nxt &&
/* 数据<= 待读取长度 */
len - tcp_header_len <= tp->ucopy.len &&
/* 控制块被用户空间锁定 */
sock_owned_by_user(sk)) { /* 设置状态为running??? */
__set_current_state(TASK_RUNNING); /* 拷贝数据到msghdr */
if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) {
/* Predicted packet is in window by definition.
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
*/
/* 有时间戳选项&& 收到的数据段均已确认,更新时间戳 */
if (tcp_header_len ==
(sizeof(struct tcphdr) +
TCPOLEN_TSTAMP_ALIGNED) &&
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp); /* 接收端RTT估算 */
tcp_rcv_rtt_measure_ts(sk, skb); __skb_pull(skb, tcp_header_len); /* 更新期望接收的序号 */
tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
NET_INC_STATS(sock_net(sk),
LINUX_MIB_TCPHPHITSTOUSER);
eaten = ;
}
} /* 未拷贝数据到用户空间,或者拷贝失败 */
if (!eaten) {
/* 检查校验和 */
if (tcp_checksum_complete(skb))
goto csum_error; /* skb长度> 预分配长度 */
if ((int)skb->truesize > sk->sk_forward_alloc)
goto step5; /* Predicted packet is in window by definition.
* seq == rcv_nxt and rcv_wup <= rcv_nxt.
* Hence, check seq<=rcv_wup reduces to:
*/
/* 有时间戳选项,且数据均已确认完毕,则更新时间戳 */
if (tcp_header_len ==
(sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) &&
tp->rcv_nxt == tp->rcv_wup)
tcp_store_ts_recent(tp); /* 计算RTT */
tcp_rcv_rtt_measure_ts(sk, skb); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS); /* Bulk data transfer: receiver */
/* 数据加入接收队列 */
eaten = tcp_queue_rcv(sk, skb, tcp_header_len,
&fragstolen);
} tcp_event_data_recv(sk, skb); /* 确认序号确认了数据 */
if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) {
/* Well, only one small jumplet in fast path... */
/* 处理ack */
tcp_ack(sk, skb, FLAG_DATA);
/* 检查是否有数据要发送,需要则发送 */
tcp_data_snd_check(sk);
/* 没有ack要发送 */
if (!inet_csk_ack_scheduled(sk))
goto no_ack;
} /* 检查是否有ack要发送,需要则发送 */
__tcp_ack_snd_check(sk, );
no_ack:
/* skb已经复制到用户空间,则释放之 */
if (eaten)
kfree_skb_partial(skb, fragstolen); /* 唤醒用户进程有数据读取 */
sk->sk_data_ready(sk);
return;
}
} slow_path:
/* 长度错误|| 校验和错误 */
if (len < (th->doff << ) || tcp_checksum_complete(skb))
goto csum_error; /* 无ack,无rst,无syn */
if (!th->ack && !th->rst && !th->syn)
goto discard; /*
* Standard slow path.
*/
/* 种种校验 */
if (!tcp_validate_incoming(sk, skb, th, ))
return; step5:
/* 处理ack */
if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < )
goto discard; /* 计算rtt */
tcp_rcv_rtt_measure_ts(sk, skb); /* Process urgent data. */
/* 处理紧急数据 */
tcp_urg(sk, skb, th); /* step 7: process the segment text */
/* 数据段处理 */
tcp_data_queue(sk, skb); /* 发送数据检查,有则发送 */
tcp_data_snd_check(sk); /* 发送ack检查,有则发送 */
tcp_ack_snd_check(sk);
return; csum_error:
TCP_INC_STATS(sock_net(sk), TCP_MIB_CSUMERRORS);
TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); discard:
tcp_drop(sk, skb);
}

TCP输入 之 tcp_rcv_established的更多相关文章

  1. TCP输入 之 快速路径和慢速路径

    概述 快速路径:用于处理预期的,理想情况下的数据段,在这种情况下,不会对一些边缘情形进行检测,进而达到快速处理的目的: 慢速路径:用于处理那些非预期的,非理想情况下的数据段,即不满足快速路径的情况下数 ...

  2. tcp 输入 简析 转载

    正常来说 TCP 收消息过程会涉及三个队列: Backlog Queue sk->sk_backlog Prequeue tp->ucopy.prequeue Receive Queue  ...

  3. tcp 输入 prequeue以及backlog队列

    /*ipv4_specific是TCP传输层到网络层数据发送以及TCP建立过程的真正OPS, 在tcp_prot->init中被赋值给inet_connection_sock->icsk_ ...

  4. TCP输入 之 tcp_data_queue

    tcp_data_queue作用为数据段的接收处理,其中分为多种情况: (1) 无数据,释放skb,返回: (2) 预期接收的数据段,a. 进行0窗口判断:b. 进程上下文,复制数据到用户空间:c. ...

  5. TCP输入 之 tcp_prequeue

    在未开启tcp_low_latency的情况下,软中断将skb送上来,加入到prequeue中,然后 在未启用tcp_low_latency且有用户进程在读取数据的情况下,skb入队到prequeue ...

  6. TCP输入 之 tcp_v4_rcv

    tcp_v4_rcv函数为TCP的总入口,数据包从IP层传递上来,进入该函数:其协议操作函数结构如下所示,其中handler即为IP层向TCP传递数据包的回调函数,设置为tcp_v4_rcv: sta ...

  7. TCP输入 之 tcp_queue_rcv

    tcp_queue_rcv用于将接收到的skb加入到接收队列receive_queue中,首先会调用tcp_try_coalesce进行分段合并到队列中最后一个skb的尝试,若失败则调用__skb_q ...

  8. Linux2.6内核协议栈系列--TCP协议2.接收

    1.排队机制 接收输入TCP报文时,有三个队列: ● 待处理队列 ● 预排队队列 ● 接收队列 接收队列包含了处理过的TCP数据段,也就是说,去除了全部的协议头,正准备将数据复制到用户应用程序.接收队 ...

  9. 前端学HTTP之连接管理

    前面的话 HTTP连接是HTTP报文传输的关键通道.要掌握HTTP就需要理解HTTP连接的来龙去脉以及如何使用这些连接 如果想查看一个网页,浏览器收到URL时,会执行下图所示的步骤.将服务器的IP地址 ...

随机推荐

  1. 完美解决Uncaught SyntaxError: Unexpected end of input

    Unexpected end of input  的英文意思是“意外的终止输入” 他通常表示我们浏览器在读取我们的js代码时,碰到了不可预知的错误,导致浏览器 无语进行下面的读取 通常造成这种错误的原 ...

  2. Spring整合Hessian的使用

    该文章转赞自  https://www.cnblogs.com/ontheroad_lee/p/3797239.htm 个人感觉写的非常好,刚学习,先记录下来 1.1     Hessian简介 He ...

  3. go语言入门(4)函数

    1,函数的定义格式 函数构成代码执行的逻辑结构.在Go语言中,函数的基本组成为:关键字func.函数名.参数列表.返回值.函数体和返回语句. Go 语言函数定义格式如下 func FuncName(/ ...

  4. odoo标识符

    class Book(models.Model): _name = "library.book" _description = "Book" _order = ...

  5. Samba set of user authentication and file access rights

    This series is compatible with Linux certification exam LPIC. A typical Linux user-level topics omit ...

  6. python基础编程:生成器、迭代器、time模块、序列化模块、反序列化模块、日志模块

    目录: 生成器 迭代器 模块 time 序列化 反序列化 日志 一.生成器 列表生成式: a = [1,2,3,3,4,5,6,7,8,9,10] a = [i+1 for i in a ] prin ...

  7. 第七章 路由 72 路由-路由redirect重定向的使用

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  8. 解决Android无法正常https://dl.google.com/dl/android/maven2/com/的办法

    最近需要进行移动开发,在安装Android Studio时,遇到了很纠结的问题,安装一直很不顺利.经过2天的百度搜索终于是找到解决的办法. 问题花了2天的时间才从茫茫大海中找到确切的答案.所以必须开个 ...

  9. 多规格商品SKU 组件封装

    前言 做电商项目呢,离不开多规格商品,SKU 也是弄了许久才搞出来,主要是多层级的联动关系,用ID和库存来判断是否是按钮禁止状态 下面就放下代码: 以封装的小程序为例: WXML: <view ...

  10. 【C#-多线程】实现每隔一段时间执行代码(多线程) 3种定时器

    总结以下三种方法,实现c#每隔一段时间执行代码: 方法一:调用线程执行方法,在方法中实现死循环,每个循环Sleep设定时间: 方法二:使用System.Timers.Timer类: 方法三:使用Sys ...