TCP定时器 之 延迟确认定时器
TCP在收到数据段但是无需马上确认时设定,如果在超时时间之内有数据要发送到对端,则确认会随着数据一起发送,即捎带ACK,如果达到超时时间则执行定时器回调立即发送ack;
启动定时器:
延迟确认定时器调用inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, xx, xxx)函数进行启动,启动时间点包含以下三个;
1. 建立连接时,客户端的第三次握手可能会被延迟确认,如果有数据要输出、设置了TCP_DEFER_ACCEPT选项或者不在快速确认模式下,则启动延迟确认定时器;
static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
const struct tcphdr *th)
{
if (sk->sk_write_pending ||
icsk->icsk_accept_queue.rskq_defer_accept ||
icsk->icsk_ack.pingpong) {
/* Save one ACK. Data will be ready after
* several ticks, if write_pending is set.
*
* It may be deleted, but with this feature tcpdumps
* look so _wonderfully_ clever, that I was not able
* to stand against the temptation 8) --ANK
*/
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 ;
} else {
tcp_send_ack(sk);
}
}
2. 在立即发送确认时,如果分配内存失败,则启动延迟确认定时器;
/* This routine sends an ack and also updates the window. */
/* TCPTODO */
void tcp_send_ack(struct sock *sk)
{
struct sk_buff *buff; /* If we have been reset, we may not send again. */
if (sk->sk_state == TCP_CLOSE)
return; tcp_ca_event(sk, CA_EVENT_NON_DELAYED_ACK); /* We are not putting this on the write queue, so
* tcp_transmit_skb() will set the ownership to this
* sock.
*/
buff = alloc_skb(MAX_TCP_HEADER,
sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN));
if (unlikely(!buff)) {
inet_csk_schedule_ack(sk);
inet_csk(sk)->icsk_ack.ato = TCP_ATO_MIN;
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
TCP_DELACK_MAX, TCP_RTO_MAX);
return;
} /* Reserve space for headers and prepare control bits. */
skb_reserve(buff, MAX_TCP_HEADER);
tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK); /* We do not want pure acks influencing TCP Small Queues or fq/pacing
* too much.
* SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784
*/
skb_set_tcp_pure_ack(buff); /* Send it off, this clears delayed acks for us. */
skb_mstamp_get(&buff->skb_mstamp);
tcp_transmit_skb(sk, buff, , (__force gfp_t));
}
3. 未启用tcp_low_latency情况下,当有进程正在读取TCP流时,此时prequeue队列存在TCP段且消耗的内存未达到上限,且没有ACK需要发送时,启动延迟确认定时器;
bool tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
if (skb_queue_len(&tp->ucopy.prequeue) == ) {
wake_up_interruptible_sync_poll(sk_sleep(sk),
POLLIN | POLLRDNORM | POLLRDBAND);
if (!inet_csk_ack_scheduled(sk))
inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
( * tcp_rto_min(sk)) / ,
TCP_RTO_MAX);
}
return true;
}
定时器回调函数:
tcp_delack_timer函数进行必要的状态检查,处理队列中的数据包,如果有ack要发送,则使用即使ack模式立即发送ack;
/**
* tcp_delack_timer() - The TCP delayed ACK timeout handler
* @data: Pointer to the current socket. (gets casted to struct sock *)
*
* This function gets (indirectly) called when the kernel timer for a TCP packet
* of this socket expires. Calls tcp_delack_timer_handler() to do the actual work.
*
* Returns: Nothing (void)
*/
static void tcp_delack_timer(unsigned long data)
{
struct sock *sk = (struct sock *)data; bh_lock_sock(sk);
/* 传输控制块未被锁定 */
if (!sock_owned_by_user(sk)) {
/* 调用超时处理函数 */
tcp_delack_timer_handler(sk);
} else {
/* 打阻塞标记告知ack要立即发送*/
inet_csk(sk)->icsk_ack.blocked = ;
__NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
/* deleguate our work to tcp_release_cb() */
/* 交给tcp_release_cb处理超时回调 */
if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags))
sock_hold(sk);
}
bh_unlock_sock(sk);
sock_put(sk);
}
/* Called with BH disabled */
void tcp_delack_timer_handler(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk); sk_mem_reclaim_partial(sk); /* 关闭或者监听状态 || 未启动延迟ack定时器*/
if ((( << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN)) ||
!(icsk->icsk_ack.pending & ICSK_ACK_TIMER))
goto out; /* 尚未达到超时时间,重新设定定时器 */
if (time_after(icsk->icsk_ack.timeout, jiffies)) {
sk_reset_timer(sk, &icsk->icsk_delack_timer, icsk->icsk_ack.timeout);
goto out;
} /* 清除延迟ack标记 */
icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER; /* prequeue队列不为空 */
if (!skb_queue_empty(&tp->ucopy.prequeue)) {
struct sk_buff *skb; __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSCHEDULERFAILED); /* 数据包出队,调用接收函数tcp_v4_do_rcv处理包 */
while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL)
sk_backlog_rcv(sk, skb); /* 消耗的内存清0 */
tp->ucopy.memory = ;
} /* 有ack需要发送 */
if (inet_csk_ack_scheduled(sk)) { /* 即时ack模式 */
if (!icsk->icsk_ack.pingpong) {
/* Delayed ACK missed: inflate ATO. */
/* 计算超时时间 */
icsk->icsk_ack.ato = min(icsk->icsk_ack.ato << , icsk->icsk_rto);
}
/* 延迟ack模式 */
else {
/* Delayed ACK missed: leave pingpong mode and
* deflate ATO.
*/
/* 设置为即时ack模式 */
icsk->icsk_ack.pingpong = ; /* 重置超时时间 */
icsk->icsk_ack.ato = TCP_ATO_MIN;
} /* 发送ack */
tcp_send_ack(sk);
__NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKS);
} out:
if (tcp_under_memory_pressure(sk))
sk_mem_reclaim(sk);
}
TCP定时器 之 延迟确认定时器的更多相关文章
- TCP Nagle算法&&延迟确认机制
TCP Nagle算法&&延迟确认机制 收藏 秋风醉了 发表于 3年前 阅读 1367 收藏 0 点赞 0 评论 0 [腾讯云]买域名送云解析+SSL证书+建站!>>> ...
- TCP的ACK确认系列 — 延迟确认
主要内容:TCP的延迟确认.延迟确认定时器的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 延迟确认模式 发送方在发送数据包时,如果发送的数据包有 ...
- Linux下TCP延迟确认(Delayed Ack)机制导致的时延问题分析
版权声明:本文由潘安群原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/105 来源:腾云阁 https://www.qclo ...
- UNIX网络编程——SOCKET API和TCP STATE的对应关系_三次握手_四次挥手及TCP延迟确认
在socket系统调用中,如何完成三次握手和四次挥手: SOCK_DGRAM即UDP中的connect操作知识在内核中注册对方机器的IP和PORT信息,并没有建立连接的过程,即没有发包,close也不 ...
- TCP的ACK原理和延迟确认机制
某天晚上睡觉前突然想到 tcp的ACK确认是单独发的还是和报文一起发的,下面看一下别人的解答 一.ACK定义TCP协议中,接收方成功接收到数据后,会回复一个ACK数据包,表示已经确认接收到ACK确认号 ...
- TCP/IP之坚持定时器、报活定时器
TCP中的四个定时器: 1.超时定时器(最复杂的一个) 2.坚持定时器 3.保活定时器 4.2MSL定时器 坚持定时器用于防止通告窗口为0以后c/s双方相互等待死锁的情况:而保活定时器则用于处理半开发 ...
- Linux驱动技术(七) _内核定时器与延迟工作
内核定时器 软件上的定时器最终要依靠硬件时钟来实现,简单的说,内核会在时钟中断发生后检测各个注册到内核的定时器是否到期,如果到期,就回调相应的注册函数,将其作为中断底半部来执行.实际上,时钟中断处理程 ...
- TCP Nagle算法以及延迟确认(即延迟回复ACK)的学习
TCP/IP协议中,无论发送多少数据,总是要在数据前面加上协议头,同时,对方接收到数据,也需要发送ACK表示确认.为了尽可能的利用网络带宽,TCP总是希望尽可能的发送足够大的数据. (一个连TCP接会 ...
- JMeter定时器设置延迟与同步
JMeter定时器一般用来设置延迟与同步.它的作用域和优先级如下: 定时器的优先级高于Sampler. 在同一作用域(比如控制器下)有多个定时器存在,每个定时器都会执行. 在某一Sampler节点下的 ...
随机推荐
- Apache+tomcat ajp模式转发域名
本示例使用Apache24 和 tomcat7.0.62 本机IP:192.168.10.38 要实现的效果 访问来源 192.168.10.38 ---->apache ----&g ...
- 北大 ACM highways问题研究(最小生成树)
#include<stdlib.h> #include<stdio.h> #include<queue> struct vertex//代表一个村庄 { int m ...
- LeetCode——回文链表
题目 给定一个链表的头节点head,请判断该链表是否为回 文结构. 例如: 1->2->1,返回true. 1->2->2->1,返回true. 15->6-> ...
- shell 实用脚本
功能 将当前目录下文件拷贝至另一目录下,且拷贝前先备份 #!/bin/sh #脚本功能 #覆盖文件前先备份 cfsuffix=$(date +%Y%m%d); #备份文件后缀 ]; then #输入参 ...
- Cuda9.0安装
CUDA 9.0安装笔记 最近实验室新购买两块K80的GPU.作为好奇的小猪,当然会自报奋勇去配置环境.在这篇博客中将会介绍在centos7下配置CUDA 9.0的步骤. 1. 什么是CUDA? 引用 ...
- php页面加载完毕后再显示购买按钮
php页面加载完毕后再显示购买按钮 $document.ready(function(){ $("#buybotton").show()})
- Scale-up and Scale-out(转载)
原地址:http://www.cnblogs.com/spork/archive/2009/12/29/1634766.html 来自原小站,曾经迷糊过的东西,表上来,希望对正在迷糊或即将迷糊的人有帮 ...
- Elasticsearch在Centos 7上的安装与配置
https://segmentfault.com/a/1190000011899522 https://blog.csdn.net/xxxxxx91116/article/details/171362 ...
- nginx第二天
nginx配置文件 配置文件结构 全局配置(user.worker_processes.error_log.pid) events(网络连接相关,worker_connections) http(最重 ...
- 搭建web服务器---Apache服务器
一.安装Apache 尽管xampp appserv等提供了方便的集成环境,但实际上部署环境还是下载单独安装的Apache,以避免那些安全问题 二.加载php解析模块,并指定模块处理文件的类型 编辑h ...