tcpack--3快速确认模式- ack状态发送&清除
ACK发送状态的转换图
ACK的发送状态清除
当成功发送ACK时,会删除延迟确认定时器,同时清零ACK的发送状态标志icsk->icsk_ack.pending
ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。
在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。当额度用完时,就进入延迟确认模式。
static int tcp_transmit_skb (struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask) { ------------------------------ if (likely(tcb->tcp_flags & TCPHDR_ACK)) tcp_event_ack_sent(sk, tcp_skb_pcount(skb)); /* ACK发送事件的处理 */ ------------------------------------------------ } //ACK发送事件主要做了:更新快速确认模式中的ACK额度,删除ACK延迟定时器,清零icsk->icsk_ack.pending。 /* Account for an ACK we sent. */ static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts) { tcp_dec_quickack_mode(sk, pkts);// 更新快速确认模式的ACK额度 inet_csk_clear_xmit_timer(sk, ICSK_TIME_DACK);//删除ACK延迟定时器 }
static inline void tcp_dec_quickack_mode (struct sock *sk, const unsigned int pkts) { struct inet_connection_sock *icsk = inet_csk(sk); if (icsk->icsk_ack.quick) { /* 如果额度不为0 */ if (pkts >= icsk->icsk_ack.quick) { icsk->icsk_ack.quick = 0; /* Leaving quickack mode we deflate ATO. */ icsk->icsk_ack.ato = TCP_ATO_MIN; } else icsk->icsk_ack.quick -= pkts; }
每次发送一个ACK包,quick即被减1。
快速确认:
- tcp_enter_quickack_mode: 进入快速确认模式,设置快速确认模式标志,设置在快速确认模式中可以发送的ACK数量
/* 在快速确认模式中,可以发送的ACK数量是有限制的,具体额度为icsk->icsk_ack.quick。 所以进入快速确认模式时,需要设置可以快速发送的ACK数量,一般允许快速确认半个接收窗口的数据量,但最多不能超过16个,最少为2个。 */ static void tcp_enter_quickack_mode (struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); tcp_incr_quickack(sk); /* 设置在快速确认模式中可以发送的ACK数量 */ icsk->icsk_ack.pingpong = 0; /* 快速确认模式的标志 */ icsk->icsk_ack.ato = TCP_ATO_MIN; /* ACK超时时间 */ }
static void tcp_incr_quickack(struct sock *sk) {/* Maximal number of ACKs sent quickly to accelerate slow-start. */ #define TCP_MAX_QUICKACKS 16U struct inet_connection_sock *icsk = inet_csk(sk); unsigned int quickacks = tcp_sk(sk)->rcv_wnd / (2 * icsk->icsk_ack.rcv_mss); if (quickacks == 0) quickacks = 2; if (quickacks > icsk->icsk_ack.quick) icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS); }
- tcp_in_quickack_mode:检查是否处于快速确认模式。如果设置了快速确认标志,且快速确认模式中可以发送的ACK数量不为0,就判断连接处于快速确认模式中
-
要求满足两个条件才能算是quickack模式:
1、pingpong被设置为0。
2、快速确认数(quick)必须为非0。
struct {
__u8 pending; /* ACK is pending */
__u8 quick; /* Scheduled number of quick acks */
__u8 pingpong; /* The session is interactive */
__u8 retry; /* Number of attempts */
__u32 ato; /* Predicted tick of soft clock */
unsigned long timeout; /* Currently scheduled timeout */
__u32 lrcvtime; /* timestamp of last received data packet */
__u16 last_seg_size; /* Size of last incoming segment */
__u16 rcv_mss; /* MSS used for delayed ACK decisions */
} icsk_ack;
- quick这个属性其代码中的注释为:scheduled number of quick acks,即快速确认的包数量,每次进入quickack模式,quick被初始化为接收窗口除以2倍MSS值;
- 每次发送一个ACK包,quick即被减1。
/* Send ACKs quickly, if "quick" count is not exhausted * and the session is not interactive. */ static bool tcp_in_quickack_mode(struct sock *sk) { const struct inet_connection_sock *icsk = inet_csk(sk); const struct dst_entry *dst = __sk_dst_get(sk); //如果快速确认模式中可以发送的ACK数量不为0,且设置了快速确认标志 且 dst 路由存在 return (dst && dst_metric(dst, RTAX_QUICKACK)) || (icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong); }
- 在 tcp_rcv_state_process 以及tcp_rcv_established中 处理完接收到的报文处理完之后;会调用_tcp_ack_snd_check()来发送快速确认或延迟确认
tcp_ack_snd_check()会检查是否需要发送ACK,以及是使用快速确认还是延迟确认。
/* static inline int inet_csk_ack_scheduled(const struct sock *sk) { return inet_csk(sk)->icsk_ack.pending & ICSK_ACK_SCHED; } */ static inline void tcp_ack_snd_check(struct sock *sk) { if (!inet_csk_ack_scheduled(sk)) {//如果没有ACK需要发送 /* We sent a data segment already. */ return; } __tcp_ack_snd_check(sk, 1); }
对于以下情况可以立即发送ACK,即进行快速确认:
1. 接收缓冲区中有一个以上的全尺寸数据段仍然是NOT ACKed,并且接收窗口变大了。所以一般收到了两个数据包后,会发送ACK,而不是对每个数据包都进行确认。
2. 此时处于快速确认模式中。
3. 乱序队列不为空。
/* * Check if sending an ack is needed. */ static void __tcp_ack_snd_check(struct sock *sk, int ofo_possible) { struct tcp_sock *tp = tcp_sk(sk); /* More than one full frame received... */ if (((tp->rcv_nxt - tp->rcv_wup) > inet_csk(sk)->icsk_ack.rcv_mss && /* ... and right edge of window advances far enough. * (tcp_recvmsg() will send ACK otherwise). Or... */ __tcp_select_window(sk) >= tp->rcv_wnd) || /* We ACK each frame or... */ tcp_in_quickack_mode(sk) || /* We have out of order data. */ (ofo_possible && !RB_EMPTY_ROOT(&tp->out_of_order_queue))) { /* Then ack it now ACK的发送函数为tcp_send_ack(),如果发送失败会启动ACK延迟定时器。 */ tcp_send_ack(sk); } else { /* Else, send delayed ack. */ tcp_send_delayed_ack(sk); } }
TCP_QUICKACK选项
TCP_QUICKACK用于让本端立即发送ACK,而不进行延迟确认。需要注意的是,这个选项并不是可持续的,之后还是有可能进入延迟确认模式的。
所以如果需要一直进行快速确认,要在每次调用接收函数后都进行选项设置。
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!