在这篇文章中我们看一下server端在接收到异常数据系列时的处理,主要目的是通过wireshark示例对这些异常数据系列的处理有一个直观的认识,感兴趣的自行阅读相关代码和协议,这里不再进行详细介绍

在进行下面的测试前,首先如下设置相关的参数,其中window参数指定了到127.0.0.2的tcp连接的最大接收窗口。

  1. root@Inspiron:/home/******/tcp12# ip route change local 127.0.0.2 dev lo window 40

一、wireshark示例

1、一个包部分数据在rcv_nxt后

如下图所示,client在与server建立连接后,首先发出No4数据包,系列号对应[1,10],下图中text列表示这个TCP数据包中实际传输的数据,No4的len=10,应用层实际传输的数据内容为"0123456789"。接着client又在No6数据包中传输系列号为[6,15]的数据包,传输的内容同样为"0123456789",长度同样为10bytes。

可以看到这里No4和No6两个数据包部分系列号重复,No4数据包系列号[6,10]对应字符串"56789",而No6数据包系列号[6,10]对应字符串"01234"。server端对于No6数据包正常回复了一个ACK报文,Ack=16,也就是说确认了系列号16以前的数据,同时可以看到No7这个ACK确认包还带有一个DSACK块,指示发送端收到了重复报文。

最终server端应用层实际读取了15bytes数据,内容为"012345678956789",可见server端把No6报文系列号[6,10]对应的数据丢弃了,但是保留了系列号[11,15]的内容。

2、一个乱序包完全在接收窗口外

如下图所示,client在与server端建立连接后,首先正常发送No4报文,接着发送乱序报文No6。从No5这个确认包可以看到Ack+Win=51,也就是说从系列号51开始的数据都是接收窗外外部的数据(包括系列号51对应的byte)。No6报文对应的系列号为[51,60]正好完全处于接收窗口外部,因此server端会丢失No6这个数据包。server端应用层最终读取了10bytes的数据,即"0123456789"。

3、一个乱序包部分在接收窗口外

这个示例与上一个不同之处在于No6系列号范围为[46,55],也即这个数据包[46,50]系列号在接收窗口内,而[51,55]在接收窗口外部。

接着看No7,No7通过SACK完整的确认了No6数据包,把落在接收窗口外部的[51,55]系列号也完整确认了,注意这个示例与上面的对比。当乱序包部分系列号落在接收窗口外部的时候,linux会正常完整的接收这个数据包,包括落在接收窗口外部的数据。而当这个数据包完全落在接收窗口外部的时候,linux则不会接收这个数据包。

另外值得注意的是No8-No11四次挥手关闭连接的过程,No8携带了SACK信息,No9则回复了一个ACK,注意No10这个FIN包的起始系列号为11,server端正常的接收了这个数据包,这也就意味着server端会把收到乱序No6报文丢掉,应用层并不能成功读取。

4、快速路径下连续包落在接收窗口外

linux对于接收到的数据包处理流程分为快速路径和慢速路径,在接收到紧急指针、tcp头中的window size发生变化等场景下都会进入慢速处理路径,慢速路径相比快速路径会执行更多检查处理。我们先来看一下快速路径下,连续包落在接收窗口外部的情况。为了不让server端更新接收窗口,我们通过SO_RCVBUF选项固定server端的接收缓存,同时我们通过特定的数据包让server端进入delay ACK模式。执行下面的测试前取消通过路由表设置的window参数。

相关的数据交互如下,No4数据包用来触发server端快速的进入delay ACK模式,server回复完No5这个确认包后就会进入delay ACK模式。关于delay ACK相关内容可以参考前面的文章。同时注意No5报文通告的Win=2,也就是接收窗口只剩余2bytes。

接着我们看到client端发送的No6-No32报文都没有触发server端回复ACK确认包。其中No6报文部分落在接收窗口外部,而No7-No32报文都是完全落在接收窗口外部的数据包。从No34这个确认包可以看到server端对于部分落在接收窗口内部的No6和完全落在接收窗口外部的No7-No32都接收了。而对于No33则没有进行接收,随后在发送No35数据包server端也没有接收。

这里No6-No32能被正常接收的原因是,linux中TCP会预留一定的缓存,当在快速处理模式下时候并不会执行严格的检查,只要新接收的数据包所占用的缓存没有超过预留的缓存就会正常接收。No6-No32都没有超过预留的缓存,因此server端正常接收。但是server端在接收到No34的时候,预留缓存不足,就会直接丢弃这个数据包,并进入quick ACK模式立即回复一个ACK,可以看到No34是一个零窗ACK报文,因此server端也就退出数据包的快速处理模式,随后在收到No35的时候会进入数据包慢速处理流程,执行检查的时候发现此时接收窗口大小为0,No35落在了接收窗口外面,因此同样会直接丢弃这个数据包并立即回复一个ACK确认包。

5、慢速路径下连续包落在接收窗口外

实际上上一次示例No35的处理已经是在慢速路径下处理的了。我们在看另外一个类似的示例。下图示例中No1-No15报文的处理与上面的示例类似,不同的地方在于No16这个数据包的Win发生了更新。server端在收到No16这个数据包的时候,发现No16更新了window size就会退出快速路径的处理进入慢速路径,以执行更复杂的检查和处理。在慢速路径中server端发现No16落在的接收窗口外部,因此直接丢弃这个数据包并进入quick ACK模式立即回复一个No17确认包,回复No17确认包的时候发现Win=0,server端TCP则退出数据包的快速处理模式,在随后接收到新数据包的时候则直接进入慢速路径处理。

6、乱序包内容部分重叠

如下图所示,client依次发送5个数据包,相关TCP交互如下图所示

为了方便对比,我把五个数据包对应的做如下图展示,其中第一行表示系列号比特位,第二行表示server端应用层实际读取的数据流,余下的5行分别表示client依次发送的5个数据包,其中数据包的红色部分表示最终被server端丢弃,数据包的绿色部分表示最终被server端接收。从下图可以看到一旦一个新接收的报文与之前报文重叠的时候,如果是新接收报文的前端发生重叠,那么新接收报文的前端内容会被丢掉,如果是后端重叠,那么之前接收的报文的内容将会被丢掉。

补充说明:

1、linux相关函数数据包接收处理:tcp_rcv_established、tcp_data_queue、tcp_data_queue_ofo

2、慢速路径快速路径等相关函数:tcp_fast_path_check,重点是tp->pred_flags这个标志变量在不同地方的更新

TCP系列36—窗口管理&流控—10、linux下的异常报文系列接收的更多相关文章

  1. TCP系列27—窗口管理&流控—1、概述

    在前面的内容中我们依次介绍了TCP的连接建立和终止过程和TCP的各种重传方式.接着我们在这部分首先关注交互式应用TCP连接相关内容如延迟ACK.Nagle算法.Cork算法等,接着我们引入流控机制(f ...

  2. TCP系列31—窗口管理&流控—5、TCP流控与滑窗

    一.TCP流控 之前我们介绍过TCP是基于窗口的流量控制,在TCP的发送端会维持一个发送窗口,我们假设发送窗口的大小为N比特,网络环回时延为RTT,那么在网络状况良好没有发生拥塞的情况下,发送端每个R ...

  3. TCP系列35—窗口管理&流控—9、紧急机制

    一.概述 我们在最开始介绍TCP头结构的时候,里面有个URG的标志位,还有一个Urgent Pointer的16bits字段.当URG标志位有效的时候,Urgent Poinert用来指示紧急数据的相 ...

  4. TCP系列32—窗口管理&流控—6、TCP zero windows和persist timer

    一.简介 我们之前介绍过,TCP报文中的window size表示发出这个报文的一端准备多少bytes的数据,当TCP的一端一直接收数据,但是应用层没有及时读取的话,数据一直在TCP模块中缓存,最终受 ...

  5. TCP系列34—窗口管理&流控—8、缓存自动调整

    一.概述 我们之前介绍过一种具有大的带宽时延乘积(band-delay product.BDP)的网络,这种网络称为长肥网络(LongFatNetwork,即LFN).我们想象一种简单的场景,假设发送 ...

  6. TCP系列28—窗口管理&流控—2、延迟ACK(Delayed Acknowledgments)

    一.简介 之前的内容中我们多次提到延迟ACK(Delayed Ack),延迟ACK是在RFC1122协议中定义的,协议指出,一个TCP实现应该实现延迟ACK,但是ACK不能被过度延迟,协议给出延迟AC ...

  7. TCP系列33—窗口管理&流控—7、Silly Window Syndrome(SWS)

    一.SWS介绍 前面我们已经通过示例看到如果接收端的应用层一直没有读取数据,那么window size就会慢慢变小最终可能变为0,此时我们假设一种场景,如果应用层读取少量数据(比如十几bytes),接 ...

  8. TCP系列29—窗口管理&流控—3、Nagle算法

    一.Nagle算法概述 之前我们介绍过,有一些交互式应用会传递大量的小包(称呼为tinygrams),这些小包的负载可能只有几个bytes,但是TCP和IP的基本头就有40bytes,如果大量传递这种 ...

  9. TCP系列30—窗口管理&流控—4、Cork算法

    一.Cork算法概述 Cork算法与Nagle算法类似,也有人把Cork算法称呼为super-Nagle.Nagle算法提出的背景是网络因为大量小包小包而导致利用率低下产生网络拥塞,网络发生拥塞的时候 ...

随机推荐

  1. 【Spark】源码分析之RDD的生成及stage的切分

    一.概述 Spark源码整体的逻辑(spark1.3.1): 从saveAsTextFile()方法入手 -->saveAsTextFile()  --> saveAsHadoopFile ...

  2. 报错: Name node is in safe mode

    将本地文件拷贝到hdfs上去,结果上错误:Name node is in safe mode 这是因为在分布式文件系统启动的时候,开始的时候会有安全模式,当分布式文件系统处于安全模式的情况下,文件系统 ...

  3. JZ2440开发板:用按键点亮LED灯(学习笔记)

    本文是对韦东山嵌入式第一期学习的记录之一,如有您需要查找的信息,可以继续往下阅读. 想要用按键点亮LED灯,就需要知道按键和LED灯的相关信息,这样才可以进行之后的操作.阅读JZ2440的原理图,可以 ...

  4. go学习笔记-错误处理

    错误处理 通过内置的错误接口提供了非常简单的错误处理机制. error类型是一个接口类型 type error interface { Error() string } 可以在编码中通过实现 erro ...

  5. window下创建虚拟环境

    一. windows下创建虚拟环境 1. 终端下执行命令:python -m pip install -upgrade pip 2. pip install virtualenv 3. 在本地创建一个 ...

  6. MySQL入门第三天(上)——函数与视图

    一.MySQL函数 同样的,完整的函数可以参照开源中国的手册:http://tool.oschina.net/apidocs/apidoc?api=mysql-5.1-zh 1.字符函数 CONCAT ...

  7. 下载地图瓦片(包括各种格式的,Openstreetmap,googlemap, bingmap)

    参考第三方开源库Brutile 个人的程序托管在github上

  8. 成都Uber优步司机奖励政策(1月24日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  9. C#防止程序重新运行

    //禁止重复运行 bool ret; Mutex mutex = new Mutex(true, Application.ProductName, out ret); if (ret) { Appli ...

  10. VINS(二)Feature Detection and Tracking

    系统入口是feature_tracker_node.cpp文件中的main函数 1. 首先创建feature_tracker节点,从配置文件中读取信息(parameters.cpp),包括: ROS中 ...