tcp那个孤独的小包到底怎么回事?
内核3.10,接《tcp的发送端一个小包就能打破对端的delay_ack么?》
我们继续来分析这个没满mss的小包,

可以看到,由于受到syn ack这端是发包端,所以该发送链路协商的mss为 1460,而接收的时候,mss只能为1412.
我们来找下规律,看下小包发送有没有其他时间规律:

可以看到,小包就是下面那排小点,其中总长度为490,很明显的一排。
图形还是比较好找规律,可以大概看出,小包的发送间隔几乎是固定的,间隔为400ms。凭着对tcp的了解,协议栈肯定不会有固定400ms发送一个固定大小的小包的
行为,所以这个应该是用户态行为。
用户态如何制造这种情况,我们再来看两个时间间隔内的报文总长度:
对应的seq分别为:1052063,1576351,2100639,2624927,3149215,3673503,4197791,4722079,5246367。
可以算出,两个小包之间的seq相差刚好为524288字节,也就是512k,而从下图的报文时间分布来看,发包的时候,是一种burst现象,也就是有包赶紧发,有的时间段是没有包的。

所以得出这样的结论:每400ms,用户态进程发送512k的报文,其中最后一个报文,是不满mss的,这个不满mss的报文,因为某种原因,
等待了一会,发现后面没有报文继续发送,所以还是发送出去了,前面等待是因为不满mss,等待超时之后,只能发送出去,发送出去之后,因为没有新的数据要下发,所以形成了一个明显的前后间隔。
那什么样的情况,不满mss的报文需要等待呢?熟悉tcp的兄弟肯定会想起来,这个就像nagle啊。
我们来看这样一个流程,在一个establish的链路中,在收到ack之后,会先调用tcp_ack处理这个ack,尽可能地清理到之前因为未收到ack而保存在在发送队列中的skb,
void tcp_rcv_established(struct sock *sk, struct sk_buff *skb,
const struct tcphdr *th, unsigned int len)
{
。。。 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.
*/
tcp_ack(sk, skb, );//入向ack处理
__kfree_skb(skb);
tcp_data_snd_check(sk);---------------检查是不是有数据需要发送,收到ack是触发数据发送的三大原因之一,其余两个为用户调用发送以及定时器超时后发送
return;
}
处理完ack之后,也就是清理了那些ack的skb之后,需要再检查是否有数据发送,因为一个有效的ack除了清理我们保存的skb外,也就是调用tcp_clean_rtx_queue,还可能为我们提供了新的窗口信息:
static inline void tcp_data_snd_check(struct sock *sk)
{
tcp_push_pending_frames(sk);
。。。
} static inline void tcp_push_pending_frames(struct sock *sk)
{
if (tcp_send_head(sk)) {
struct tcp_sock *tp = tcp_sk(sk); __tcp_push_pending_frames(sk, tcp_current_mss(sk), tp->nonagle);
}
} void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
int nonagle)
{
....... if (tcp_write_xmit(sk, cur_mss, nonagle, ,
sk_gfp_atomic(sk, GFP_ATOMIC)))
tcp_check_probe_timer(sk);--------------------------tcp_write_xmit返回1,则设置timer
}
最后进入到熟悉的tcp_write_xmit 函数,
static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
int push_one, gfp_t gfp)
{
。。。。。 if (tso_segs == ) {------走的这个流程
if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
(tcp_skb_is_last(sk, skb) ?-----------------确实是最后一个报文,因为其他报文因为nginx限速没有下来
nonagle : TCP_NAGLE_PUSH))))
break;
} else {
if (!push_one &&
tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
max_segs))
break;
}
。。。。
}
break出去之后,并没有发包,而是等待,因为
static inline void tcp_check_probe_timer(struct sock *sk)
{
const struct tcp_sock *tp = tcp_sk(sk);
const struct inet_connection_sock *icsk = inet_csk(sk); if (!tp->packets_out && !icsk->icsk_pending)
inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
icsk->icsk_rto, TCP_RTO_MAX);
}
主要就是设置定时器,ICSK_TIME_PROBE0,对应的超时时间为rto,
static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
{
return usecs_to_jiffies((tp->srtt_us >> ) + tp->rttvar_us);
}
tp->rttvar_us 其实不能小于 200ms,由于tp->srtt_us存储的是平滑rtt的8倍(为了不进行float计算,因为平滑的时候,是取的7/8的老rtt+1/8新rtt),所以需要>>3,按照当前流来说,
在设置rto之前,前面都是有多个40ms的delay_ack作为rtt,所以最终的定时器大概就是在240ms左右。和ttl图形是吻合的。当然,由于第一个小包之前的rtt都不大,平滑之后大概20ms左右,
所以第一个小包离收到ack之后的rto大概在220ms,也是符合图形的。
tcp那个孤独的小包到底怎么回事?的更多相关文章
- 表连接到底咋回事,就是产生中间结果啊!用于给select/insert等操作用
1.表连接到底咋回事,就是产生中间结果啊!用于给select/insert等操作用啊. 2.表连接产生的结果用于select/insert用 3.表连接产生的结果用于select/insert用 比如 ...
- Spring Boot Starters到底怎么回事?
前言 上周看了一篇.你一直在用的Spring Boot Starters究竟是怎么回事(https://www.cnblogs.com/fengzheng/p/10947585.html) 感觉终 ...
- Java 的Integer、int与new Integer到底怎么回事?
先做一些总结,询问了些经验比较多的师傅,在这里表示感谢,然后自己总结下,今天的收获分享给大家: 1. int 和Integer在进行比较的时候,Integer会进行拆箱,转为int值与int进行比较. ...
- java中自动插入一个默认的构造函数,这到底怎么回事?
1.2 当没有任何构造函数,java编译器,会插入一个默认的构造函数 见下面的例子: class Line { double x = 0.02; double y; } publ ...
- Nginx使用教程(三):Nginx配置性能优化之I/O和TCP配置
配置Nginx I/O <br\> Sendfile 当应用程序传输文件时,内核首先缓冲数据,然后将数据发送到应用程序缓冲区. 应用程序反过来将数据发送到目的地. Sendfile方法是一 ...
- Swift 函数调用到底写不写参数名
最近真正开始学 Swift,在调用函数的时候遇到一个问题:到底写不写函数名? 我们来看两个个例子: // 1 func test(a: Int, b: Int) ->Int { return a ...
- python编程系列---白痴女朋友(我没有女朋友!)看了都能懂的TCP/IP协议介绍
前言 早期的计算机网络,都是由各厂商自己规定一套协议,IBM.Apple和Microsoft都有各自的网络协议,互不兼容:为了把全世界的所有不同类型的计算机都连接起来,就必须规定一套全球通用的协议,为 ...
- 2.jdk1.8+springboot中http1.1之tcp连接复用实现
接上篇:https://www.cnblogs.com/Hleaves/p/11284316.html 环境:jdk1.8 + springboot 2.1.1.RELEASE + feign-hys ...
- C/C++ 笔试题
/////转自http://blog.csdn.net/suxinpingtao51/article/details/8015147#userconsent# 微软亚洲技术中心的面试题!!! 1.进程 ...
随机推荐
- 《Attention Augmented Convolutional Networks》注意力的神经网络
paper: <Attention Augmented Convolutional Networks> https://arxiv.org/pdf/1904.09925.pdf 这篇文章是 ...
- Visual Studio安装Visual Assist的办法(兼容VS2010至VS2017)
Visual Assist可以说是一个码代码的高效帮手,有了它,敲起代码速度杠杠的,但是有时候安装破解老是出问题,这一次,我自己尝试了几次,跟着网上的教程做了做,改了改,结合之前安装的经验,最后总算安 ...
- 记录一次MyEclipse打开jsp文件出现Error的解决办法
今天正在忙着写项目,在打开一个项目内的jsp文件时发现我亲爱的代码消失了. 最后从网上找到了解决办法,希望可以帮到有需要的人,也作为个人记录 第一步:找到安装路径MyEclipse\configura ...
- 关于eth0 eth0:1 和eth0.1关系介绍
eth0 eth0:1 和eth0.1三者的关系对应于物理网卡.子网卡.虚拟VLAN网卡的关系:物理网卡:物理网卡这里指的是服务器上实际的网络接口设备,这里我服务器上双网卡,在系统中看到的2个物理网卡 ...
- Java中所涉及到的设计模式小记
一.JAVA设计模式一共有23中.其中这23中大体可以分为3类,具体分法如下所示: 1.创建型模式:涉及到的设计模式共5种,分别是: 工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 2.结 ...
- pymsql简单的使用
不废话直接上代码: import pymysql class MysqlConnection: ''' 单例模式获取数据库链接实例 ''' _instance = None def __new__(c ...
- jQuery使用CDN加速
使用新浪.百度.谷歌和微软的CDN加速jQuery 随着jQuery的版本更新,体积也越来越大,如果把jQuery放在自己的服务器上,会消耗不少的流量.而谷歌和百度等互联网公司为了方便开发者,提供了C ...
- JavaScript最后的课程笔记
一.快捷位置和尺寸 DOM已经提供给我们计算后的样式,但是还觉得不方便,所以DOM又提供给我们一些API: ele.offsetLeft ele.offsetTop ele.offsetWidth e ...
- python SSH客户端的交互式和非交互方式
使用python中有一个paramiko模块来实现python SSH客户端,与SSH服务器交互时,需要注意有交互式和非交互式的区别. 只执行单条命令,之后就断开链接,可以使用非交互方式.执行多条命令 ...
- c# 观察者模式 匿名方法与Lambda
//匿名方法 //和委托搭配使用 //方便我们快速对委托进行传参 //不需要我们去定义一个新的函数 //直接用delegate关键字代替方法名,后面跟上参数列表与方法体 //delegate(参数列表 ...