TCP的核心系列 — SACK和DSACK的实现(四)
和18版本不同,37版本把DSACK的检测部分独立出来,可读性更好。
37版本在DSACK的处理中也做了一些优化,对DSACK的两种情况分别进行处理。
本文主要内容:DSACK的检测、DSACK的处理。
Author:zhangskd @ csdn
dsack检测
根据RFC 2883,DSACK的处理流程如下:
1)look at the first SACK block :
—If the first SACK block is covered by the Cumulative Acknowledgement field, then it is a D-SACK
block, and is reporting duplicate data.
—Else, if the first SACK block is covered by the second SACK block, then the first SACK block is a
D-SACK block, and is reporting duplicate data.
2)otherwise, interpret the SACK blocks using the normal SACK procedures.
简单来说,符合以下任一情况的,就是DSACK:
1)第一个SACK块的起始序号小于它的确认序号,说明此SACK块包含了确认过的数据。
2)第一个SACK块包含在第二个SACK块中,说明第一个SACK块是重复的。
static int tcp_check_dsack(struct sock *sk, struct sk_buff *ack_skb,
struct tcp_sack_block_wire *sp, int num_sacks, u32 prior_snd_una)
{
struct tcp_sock *tp = tcp_sk(sk);
u32 start_seq_0 = get_unaligned_be32(&sp[0].start_seq); /* 第一个SACK块的起始 */
u32 end_seq_0 = get_unaligned_be32(&sp[0].end_seq); /* 第一个SACK块的结束 */
int dup_sack = 0; /* 是否有DSACK */ /* 如果第一个SACK块的起始序号小于它的确认序号,说明此SACK块包含了确认过的数据,
* 所以第一个SACK块是DSACK。
*/
if (before(start_seq_0, TCP_SKB_CB(ack_skb)->ack_seq)) {
dup_sack = 1;
tcp_dsack_seen(tp);
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKRECV); } else if (num_sacks > 1) {
u32 end_seq_1 = get_unaligned_be32(&sp[1].end_seq); /* 第二个块的结束序号 */
u32 start_seq_1 = get_unaligned_be32(&sp[1].start_seq); /* 第二个块的起始序号 */ /* 如果第一个SACK块包含在第二个SACK块中,说明第一个SACK块是重复的,即为DSACK */
if (! after(end_seq_0, end_seq_1) && ! before(start_seq_0, start_seq_1)) {
dup_sack = 1;
tcp_dsack_seen(tp);
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPDSACKOFORECV);
}
} /* D-SACK for already forgotten data... Do dumb counting.
* undo_retrans记录重传数据包的个数,如果undo_retrans降到0,
* 就说明之前的重传都是不必要的,进行拥塞调整撤销。
*/
if (dup_sack && ! after(end_seq_0, prior_snd_una) &&
after(end_seq_0, tp->undo_marker))
tp->undo_retrans--; return dup_sack;
}
/* Take a notice that peer is sending D-SACKs */
static void tcp_dsack_seen(struct tcp_sock *tp)
{
tp->rx_opt.sack_ok |= 4;
}
在以上函数中,undo_marker为进入Recovery或FRTO状态时记录的snd_una,prior_snd_una为根据该ACK
更新窗口前的snd_una。如果回复的DSACK在这块中间,说明是超时重传或FRTO后进行的重传,因此需要减
少undo_retrans。当undo_retrans减小到0,说明之前的重传都是不必要的,网络并没有拥塞,因此要进行拥
塞调整撤销。
dsack处理
当处理一个块时,会检查下一个块是不是DSACK块,如果是则用next_dup指向该DSACK块。
为什么在处理当前SACK块的时候,还要考虑到下个DSACK块呢?
我们知道DSACK有两种情况,一种是DSACK块小于snd_una,另一种情况是DSACK块大于snd_una且包含在
第一个块中,我们来分别分析下。
(1)DSACK块大于snd_una且包含在第一个SACK块中
排序前块的顺序:start_seq2、start_seq1、start_seq3,start_seq2为DSACK块起始序号,是第一个块。
排序后块的顺序:start_seq1、start_seq2、start_seq3,DSACK变为第二个块了。
从上图可以看出,start_seq2表示的DSACK块,是包含在start_seq1表示的SACK块中的,因此两个块需要
同时处理。不然等start_seq1表示的SACK块处理完后,再处理DSACK块,就需要做一些重复的工作。
当DSACK包含在第一个SACK块中,那么处理DSACK块在cache中的部分。
static struct sk_buff *tcp_maybe_skipping_dsack(struct sk_buff *skb, struct sock *sk,
struct tcp_sack_block *next_dup,
struct tcp_sacktag_state *state,
u32 skip_to_seq)
{
/* 如果下个SACK块不是DSACK块,那么不用进行dsack处理 */
if (next_dup == NULL)
return skb; /* 如果在(cache->start_seq, cache->end_seq)中包含dsack */
if (before(next_dup->start_seq, skip_to_seq)) { /* 找到next_dup->start_seq之后的skb */
skb = tcp_sacktag_skip(skb, sk, state, next_dup->start_seq); /* 处理next_dup->start_seq之后的skb */
skb = tcp_sacktag_walk(skb, sk, NULL, state, next_dup->start_seq, next_dup->end_seq, 1);
}
}
(2)DSACK块小于snd_una
这时候DSACK排序后也是第一个块,会被直接处理,next_dup在这里就没有意义了。
DSACK的两种情况都在tcp_sacktag_walk()中处理,第一种时next_dup不为空、dup_sack_in为0;
第二种时next_dup为空,dup_sack_in为1。
Reference
RFC 2883
TCP的核心系列 — SACK和DSACK的实现(四)的更多相关文章
- TCP的核心系列 — SACK和DSACK的实现(一)
TCP的实现中,SACK和DSACK是比较重要的一部分. SACK和DSACK的处理部分由Ilpo Järvinen (ilpo.jarvinen@helsinki.fi) 维护. tcp_ack() ...
- TCP的核心系列 — SACK和DSACK的实现(二)
和18版本相比,37版本的SACK和DSACK的实现做了很多改进,最明显的就是需要遍历的次数少了, 减少了CPU的消耗.37版的性能提升了,代码有大幅度的改动,逻辑也更加复杂了. 本文主要内容:37版 ...
- TCP的核心系列 — SACK和DSACK的实现(七)
我们发送重传包时,重传包也可能丢失,如果没有检查重传包是否丢失的机制,那么只能依靠超时来恢复了. 37版本把检查重传包是否丢失的部分独立出来,这就是tcp_mark_lost_retrans(). 在 ...
- TCP的核心系列 — SACK和DSACK的实现(三)
不论是18版,还是37版,一开始都会从TCP的控制块中取出SACK选项的起始地址. SACK选项的起始地址是保存在tcp_skb_cb结构的sacked项中的,那么这是在什么时候做的呢? SACK块并 ...
- TCP的核心系列 — SACK和DSACK的实现(六)
上篇文章中我们主要说明如何skip到一个SACK块对应的开始段,如何walk这个SACK块包含的段,而没有涉及到 如何标志一个段的记分牌.37版本把给一个段打标志的内容独立出来,这就是tcp_sack ...
- TCP的核心系列 — SACK和DSACK的实现(五)
18版本对于每个SACK块,都是从重传队列头开始遍历.37版本则可以选择性的遍历重传队列的某一部分,忽略 SACK块间的间隙.或者已经cache过的部分.这主要是通过tcp_sacktag_skip( ...
- TCP的核心系列 — ACK的处理(二)
本文主要内容:tcp_ack()中的一些细节,如发送窗口的更新.持续定时器等. 内核版本:3.2.12 Author:zhangskd @ csdn 发送窗口的更新 什么时候需要更新发送窗口呢? (1 ...
- TCP的核心系列 — ACK的处理(一)
TCP发送数据包后,会收到对端的ACK.通过处理ACK,TCP可以进行拥塞控制和流控制,所以 ACK的处理是TCP的一个重要内容.tcp_ack()用于处理接收到的ACK. 本文主要内容:TCP接收A ...
- TCP的核心系列 — 重传队列的更新和时延的采样(二)
在tcp_clean_rtx_queue()中,并非对每个ACK都进行时延采样.是否进行时延采样,跟这个ACK是否为 重复的ACK.这个ACK是否确认了重传包,以及是否使用时间戳选项都有关系. 本文主 ...
随机推荐
- 改进版getpass库
编程伊始 正式实施 改进版 源码 以数字显示 以自定义分隔符delimiter显示 如何使用 下载及安装 在您的代码中使用 源码下载 总结 用过Linux的都知道,尤其是进行使用包管理软件类似于apt ...
- 1CCTableView的使用,TableView响应和小格子tableView实现
1 CCTableView的使用 T26TableView.h #ifndef __T26TableView_H__ #define __T26TableView_H__ #includ ...
- 使用 纯JQuery 进行 表单 验证
对于JavaScript而言,进行表单数据的验证可谓是很有必要的,而且一般我们都会在网页上先进行一下表单验证,然后服务器端再次进行验证,来确保用户提交数据的准确性.下面就来分享一个JQuery实现的表 ...
- Android更新UI的几种方法
在Android开发过程中,常需要更新界面的UI.比如网络请求操作.一些耗时操作都不能放在UI线程中运行的,需要放在子线程,而子线程又不能更新UI界面,这是我们需要引入一个Handler,消息处理机制 ...
- Dialog样式的Activity
效果图: 设置全屏模式: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInst ...
- Citrix 桌面虚拟化解决方案与VMware桌面虚拟化解决方案对比
通过 XenDesktop 和 FlexCast为各种场景交付虚拟桌面 企业桌面面临的问题 为每个用户提供安全高效的桌面环境是几乎所有公司或组织的基本要求.如果用户无法使用他们的桌面或应用程序,公司就 ...
- Android初级教程短信防火墙
如果你有女神,而且有情敌的话,你看到这篇文章会有一种窃喜的感觉. 需求:对情敌的号码进行拦截,让女神手机永远收不到它的号码. 首先定义一个广播接收者类: package com.example.sms ...
- java设计模式---三种工厂模式
工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory).GOF在 ...
- 用SpriteBuilder简化"耕牛遍地走"的动画效果(四)
写到这突然有童鞋质疑,你这哪里是牛,分明是熊嘛! 仔细看了下,还真像牛.反正是这个意思.怪本猫猪牛熊不分,好在道理是一样的. 下面继续,言归正传. 添加一个空白的touchBegan方法,如果没有这个 ...
- C#之FileInfo的简单操作
和DirectoryInfo一样,FileInfo类同样是很方便的就能实现复杂的功能. 如下,我们一起来看一下一个简单的小例子吧. using System; using System.Collect ...