ACK的pingpong模式,用于TCP两端的套接口为交互模式时,数据在两个方向交叉发送,所以pingpong模式可减少单独ACK报文的发送。

开启pingpong模式

由于ACK的pingpong模式与quick模式互斥,应用层可通过setsockopt系统调用的TCP_QUICKACK选项来开启和关闭pingpong模式,即如果应用层开启了快速ACK模式,将清除pingpong模式,反之,如果关闭了快速ACK模式,将开启pingpong模式。需要注意的是,如果设置setsockopt中TCP_QUICKACK选项的值为偶数的话,如2,内核将关闭pingpong模式,检查是否需要发送ACK报文(tcp_cleanup_rbuf),之后将重新开启pingpong模式。相当于quick模式仅开启一次。

static int do_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen)
{switch (optname) {case TCP_QUICKACK:if (!val) {icsk->icsk_ack.pingpong = 1;} else {icsk->icsk_ack.pingpong = 0;if ((1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_CLOSE_WAIT) && inet_csk_ack_scheduled(sk)) {icsk->icsk_ack.pending |= ICSK_ACK_PUSHED;tcp_cleanup_rbuf(sk, 1);if (!(val & 1))icsk->icsk_ack.pingpong = 1;
}

如果当前数据报文的发送时间与最近一次接收到对端数据的时间之差值,小于ACK超时时间ATO(Ack TimeOut),意味着本地可在如此短的时间内回复对端数据,不需要单独的ACK确认报文,开启ACK的pingpong模式。

/* Congestion state accounting after a packet has been sent. */
static void tcp_event_data_sent(struct tcp_sock *tp, struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);const u32 now = tcp_jiffies32;if (tcp_packets_in_flight(tp) == 0)tcp_ca_event(sk, CA_EVENT_TX_START);tp->lsndtime = now;/* If it is a reply for ato after last received packet, enter pingpong mode. */if ((u32)(now - icsk->icsk_ack.lrcvtime) < icsk->icsk_ack.ato)icsk->icsk_ack.pingpong = 1;
}

在接收到对端发送的FIN报文之后,如果本端的TCP套接口状态处于TCP_SYN_RECV或者TCP_ESTABLISHED状态,开启ACK的pingpong模式,因为此时对端已经不再接收任何数据,不需要向其发送ACK确认报文。

void tcp_fin(struct sock *sk)
{struct tcp_sock *tp = tcp_sk(sk);inet_csk_schedule_ack(sk);sk->sk_shutdown |= RCV_SHUTDOWN;sock_set_flag(sk, SOCK_DONE);switch (sk->sk_state) {case TCP_SYN_RECV:case TCP_ESTABLISHED:tcp_set_state(sk, TCP_CLOSE_WAIT);    /* Move to CLOSE_WAIT */inet_csk(sk)->icsk_ack.pingpong = 1;break;
}

pingpong模式关闭

前节已经说明ACK的quick模式与pingpong模式互斥,所以通过setsockopt开启quick模式时,将退出pingpong模式;或者在内核中开启quick模式时,pingpong模式将关闭,将函数tcp_enter_quickack_mode。quick模式的启动可参考:https://blog.csdn.net/sinat_20184565/article/details/90085616。在接收到重传报文、乱序报文等的情况下进入quick模式,以便快速回复对端ACK。

static void tcp_enter_quickack_mode(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);tcp_incr_quickack(sk);icsk->icsk_ack.pingpong = 0;icsk->icsk_ack.ato = TCP_ATO_MIN;
}

在延迟ACK定时器的超时处理函数中,如果检查到套接口开启了pingpong模式,将执行关闭。可见在ACK超时之前,本地并没有发送任何数据到对端,表明套接口可能并非交互式应用。

void tcp_delack_timer_handler(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER;if (inet_csk_ack_scheduled(sk)) {if (!icsk->icsk_ack.pingpong) {icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << 1, icsk->icsk_rto);   /* Delayed ACK missed: inflate ATO. */} else {/* Delayed ACK missed: leave pingpong mode and deflate ATO. */icsk->icsk_ack.pingpong = 0;icsk->icsk_ack.ato      = TCP_ATO_MIN;}tcp_mstamp_refresh(tcp_sk(sk));tcp_send_ack(sk);}
}

pingpong模式判断

在TCP三次握手过程中,当客户端套接口接收到服务端回复的SYN+ACK报文后,如果客户端套接口设置了ACK的pingpong模式,表明马上会有数据发送,将延后ACK的回复,等待和数据一起发送。

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th)
{struct inet_connection_sock *icsk = inet_csk(sk);struct tcp_sock *tp = tcp_sk(sk);if (th->ack) {if (sk->sk_write_pending || icsk->icsk_accept_queue.rskq_defer_accept || icsk->icsk_ack.pingpong) {inet_csk_schedule_ack(sk);tcp_enter_quickack_mode(sk);inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX);
discard:tcp_drop(sk, skb);return 0;} else {tcp_send_ack(sk);}return -1;}
}

在应用层读取套接口的数据之后,如果ACK的pending状态为ICSK_ACK_PUSHED,并且pingpong模式处于关闭状态,才有可能执行一次ACK发送操作。在以上的setsockopt函数中,如果开启quick模式,内核调用函数tcp_cleanup_rbuf(sk, 1),传入的copied参数为1,所以只要套接口的接收缓存sk_rmem_alloc为空(读取完所有数据),内核将执行一次ACK发送。

static void tcp_cleanup_rbuf(struct sock *sk, int copied)
{struct tcp_sock *tp = tcp_sk(sk);bool time_to_ack = false;struct sk_buff *skb = skb_peek(&sk->sk_receive_queue);if (inet_csk_ack_scheduled(sk)) {const struct inet_connection_sock *icsk = inet_csk(sk);/* Delayed ACKs frequently hit locked sockets during bulk receive. */if (icsk->icsk_ack.blocked ||tp->rcv_nxt - tp->rcv_wup > icsk->icsk_ack.rcv_mss ||/** If this read emptied read buffer, we send ACK, if connection is not bidirectional, user drained* receive buffer and there was a small segment in queue.*/(copied > 0 &&((icsk->icsk_ack.pending & ICSK_ACK_PUSHED2) || ((icsk->icsk_ack.pending & ICSK_ACK_PUSHED) && !icsk->icsk_ack.pingpong)) &&!atomic_read(&sk->sk_rmem_alloc)))time_to_ack = true;
}

在套接口进入延迟ACK模式时,如果套接口的ACK处于pingpong模式,意味着可以承受最长时间的ACK发送延迟,以保证尽可能的等待本地发送数据,避免单独的ACK报文发送。将ATO时间上限设置为TCP_DELACK_MAX(200毫秒)。

void tcp_send_delayed_ack(struct sock *sk)
{struct inet_connection_sock *icsk = inet_csk(sk);int ato = icsk->icsk_ack.ato;if (ato > TCP_DELACK_MIN) {const struct tcp_sock *tp = tcp_sk(sk);int max_ato = HZ / 2;if (icsk->icsk_ack.pingpong || (icsk->icsk_ack.pending & ICSK_ACK_PUSHED))max_ato = TCP_DELACK_MAX;if (tp->srtt_us) {int rtt = max_t(int, usecs_to_jiffies(tp->srtt_us >> 3), TCP_DELACK_MIN);if (rtt < max_ato)max_ato = rtt;}ato = min(ato, max_ato);}timeout = jiffies + ato;icsk->icsk_ack.pending |= ICSK_ACK_SCHED | ICSK_ACK_TIMER;icsk->icsk_ack.timeout = timeout;sk_reset_timer(sk, &icsk->icsk_delack_timer, timeout);
}

内核版本 4.15

TCP ACK的pingpong交互模式相关推荐

  1. libjingle源码解析(4)-【PseudoTcp】建立UDP之上的TCP(2):对交互数据流的处理

    对交互数据流的处理 TCP包含两类数据流,交互数据流和成块数据流.交互数据流的特点是每个报文数据字节数比较小,大部分是10字节一下,而成块数据流的特点是大部分报文是满长度的,一般能达到MSS. 本文先 ...

  2. matplotlib交互模式

    Matpotlib交互模式 在运行python程序时有时候需要生成以下的 动态图模式 来显示程序运行的结果 此时需要使用matplotlib的 交互模式 ,在Ipython中时默认使用交互模式的. 在 ...

  3. Ext学习-前后交互模式介绍

    在前后台交互模式的介绍中,实际上就是Store中Proxy相关的内容,比如Ajax提交. 所以详细的文档请参考: Ext学习-基础概念,核心思想介绍 中关于数据模型和MVC结构部分. 作者:sdjnz ...

  4. (深入理解)matplotlib的交互模式(block,interactive,ion,ioff,draw,show,plot等的区别)

    文章目录 interactive,ion,ioff draw,show,plot draw show,plot 拓展 import matplotlib.pyplot as plt import nu ...

  5. python入门(一):进入python的交互模式、pip的使用和数据类型

    环境安装: https://www.python.org/ pycharm 社区版路径: http://www.jetbrains.com/pycharm/download/download-than ...

  6. fdisk命令非交互模式及parted的mkpart命令第一个参数说明

    fdisk命令非交互模式: 将要在fdisk命令行输入的命令写入一个文本文件,比如叫做fdisk.txt 比如创建一个100M的第一个主分区,,fdisk.txt的内容如下: n p 1 0 +100 ...

  7. Python绘图之matplotlib基础教程:matplotlib库图表绘制中常规设置大全(交互模式、清除原有图像、设置横坐标显示文字/旋转角度、添加图例、绘图布局自动调整、图像显示、图像暂停)

    Python绘图之matplotlib基础教程:matplotlib库图表绘制中常规设置大全(交互模式.清除原有图像.设置横坐标显示文字/旋转角度.添加图例.绘图布局自动调整.图像显示.图像暂停) 目 ...

  8. 开发函数计算的正确姿势——使用交互模式安装依赖

    前言 首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函数计算 ...

  9. python matplotlib.pyplot plt.ioff()函数(关闭交互模式用于阻塞程序,不让图片关闭)

    感觉ioff()函数就是个全局设置的东东,启动它,则图形绘制到最后窗口不会关闭(检测到有新的绘制就会更新窗口内容),否则因为开启了ion()交互模式,窗口绘制完就默认关闭 def ioff():&qu ...

  10. python 3 廖雪峰博客笔记(三) 命令行模式与交互模式

    python 的代码一般保存为 .py结尾的文本文件格式 比如 add.py 里写下如下内容 100 + 200 执行 add.py有两种方式: 1. 命令行方式:将python代码写入脚本中执行 p ...

最新文章

  1. Spring使用到了那些接口/第三方框架
  2. ES6标准学习: 4、数组的扩展
  3. echarts X轴数据显示不全问题
  4. Android框架攻击之Fragment注入
  5. 如何快速清空Linux中的大文件?
  6. excel中如何取消自动超链接?
  7. linux: chmod,chown命令详解
  8. Python--map用法
  9. C#报表控件ReportViewer rdlc 例(1) .
  10. 2月第三周各国家.NET域名排名Top10:中国第三
  11. ubuntu下使用vi是方向键变乱码 退格键不能使用的解决方法
  12. 液晶拼接处理器_液晶拼接屏方案的制作和规划
  13. Java多线程Queue_Java多线程-BlockingQueue-ArrayBlockingQueue-LinkedBlockingQueue
  14. java验证码功能一般怎么做的_java制作简单验证码功能
  15. 使用Eclispe 查看api技巧
  16. 制作CDKEY:CDKEY不宜包含生效时间
  17. PMP考试中一些解题思路
  18. Unity 3d Homework 5 打飞碟游戏实现
  19. C语言 利用函数计算素数个数并求和
  20. 南邮-2043(有才华的罗老师)

热门文章

  1. python绘制贝塞尔曲线_贝塞尔曲线数学原理及Python实现
  2. springcloud架构特点_董事长挖来一位京东T9架构师,送我们两份微服务文档,实在太香了...
  3. tensorflow 1.14 + cuda10.1 在Ubuntu 16.04上
  4. 第二章 ZeroMQ进阶
  5. 浅谈Netty相关概念
  6. C++11的std::declval与decltype
  7. oracle_sqlserver和mysql获取表外键的方法_MYSQL教程如何获取SqlServer2005表结构(字段,主键,外键,递增,描述)...
  8. CString,string,char*之间的转换
  9. 全站最全实战的Java项目(附源码)
  10. 一次通过PMP认证考试的心得分享