tcp的发送端一个小包就能打破对端的delay_ack么?
3.10內(nèi)核,反向合入4.9的bbr。
最近分析bbr的時候,收集了線上的一些報文,其中有一個疑問一直在我腦海里面,如下:
本身處于delay_ack狀態(tài)的客戶端,大概40ms回復(fù)一個delay_ack,當(dāng)收到一個490字節(jié)的小包之后,立刻回復(fù)了ack。且不止出現(xiàn),是有規(guī)律的出現(xiàn):
我是如何確定這個ack一定是打破了delay_ack的呢,除了在時間上和發(fā)包的時間相隔很短,我還特意確認(rèn)了一下,之后報文的ack是否立刻回復(fù)的,結(jié)果確定
都是立刻回復(fù)的,也就是進(jìn)入了quick_ack的模式,回復(fù)快速ack的數(shù)量也剛好是16,因?yàn)?tcp_init_nondata_skb(buff, tcp_acceptable_seq(sk), TCPHDR_ACK);
中,對shinfo->gso_segs = 1;這樣在減少quick閾值的時候,每次?tcp_event_ack_sent 只是將quick減去1,這樣就是說,一旦打破delay_ack,那么至少兩個,
至多16個quick ack,也就是符合代碼:
icsk->icsk_ack.quick = min(quickacks, TCP_MAX_QUICKACKS);根據(jù)接收窗口和mss,以及
/* Maximal number of ACKs sent quickly to accelerate slow-start. */ #define TCP_MAX_QUICKACKS???16U 我鐵定確定了這個是打破了原本的delay_ack.它既不屬于亂序報文,又沒有out of window,且收包窗口也沒有變化,也不是收到一個已經(jīng)被ack過的報文, 且亂序隊列中并沒有數(shù)據(jù),為啥它就能打破delay_ack呢?你說它到底滿足哪一條呢? /** 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 */tcp_send_ack(sk);} else {/* Else, send delayed ack. */tcp_send_delayed_ack(sk);} }?然后我搜索代碼,看什么時候調(diào)用?tcp_enter_quickack_mode,發(fā)現(xiàn)沒有收獲,這個包不滿足條件。
這個包的神奇之處在哪?走查了delay_ack打破的條件,沒法理解這個代碼邏輯,關(guān)于delay_ack的出現(xiàn)場景,在另一篇博客中有描述《https://www.cnblogs.com/10087622blog/p/10315410.html》
我點(diǎn)擊這個報文詳細(xì)分析:
發(fā)現(xiàn)它和其他報文的區(qū)別是,它帶了push標(biāo)志,帶了走查了代碼,也沒看出來,為啥push標(biāo)志的報文會在delay_ack的情況下,能立刻發(fā)送ack。
最后,再回到報文,看到一點(diǎn), 那就是這個小包與上一個ack之間的間隔為230ms左右,直覺感覺這個時間偏大,然后走查收包的代碼:
/* There is something which you must keep in mind when you analyze the* behavior of the tp->ato delayed ack timeout interval. When a* connection starts up, we want to ack as quickly as possible. The* problem is that "good" TCP's do slow start at the beginning of data* transmission. The means that until we send the first few ACK's the* sender will sit on his end and only queue most of his data, because* he can only send snd_cwnd unacked packets at any given time. For* each ACK we send, he increments snd_cwnd and transmits more of his* queue. -DaveM*/ static void tcp_event_data_recv(struct sock *sk, struct sk_buff *skb) {struct tcp_sock *tp = tcp_sk(sk);struct inet_connection_sock *icsk = inet_csk(sk);u32 now;inet_csk_schedule_ack(sk);tcp_measure_rcv_mss(sk, skb);tcp_rcv_rtt_measure(tp);now = tcp_time_stamp;if (!icsk->icsk_ack.ato) {/* The _first_ data packet received, initialize* delayed ACK engine.*/tcp_incr_quickack(sk);icsk->icsk_ack.ato = TCP_ATO_MIN;} else {int m = now - icsk->icsk_ack.lrcvtime;if (m <= TCP_ATO_MIN / 2) {/* The fastest case is the first. */icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + TCP_ATO_MIN / 2;} else if (m < icsk->icsk_ack.ato) {icsk->icsk_ack.ato = (icsk->icsk_ack.ato >> 1) + m;if (icsk->icsk_ack.ato > icsk->icsk_rto)icsk->icsk_ack.ato = icsk->icsk_rto;} else if (m > icsk->icsk_rto) {---------------------------進(jìn)入這個流程/* Too long gap. Apparently sender failed to* restart window, so that we send ACKs quickly.*/tcp_incr_quickack(sk);----------------------------------這個修改了quickack的發(fā)包數(shù)量sk_mem_reclaim(sk);}}icsk->icsk_ack.lrcvtime = now;tcp_ecn_check_ce(tp, skb);if (skb->len >= 128)tcp_grow_window(sk, skb); }正是因?yàn)榘l(fā)包的間隔大于了?icsk->icsk_rto,所以接收端覺得很長時間沒有收到包了,那么盡快給對方回復(fù)ack。icsk->icsk_ack.quick 已經(jīng)大于0了。
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);return (dst && dst_metric(dst, RTAX_QUICKACK)) ||(icsk->icsk_ack.quick && !icsk->icsk_ack.pingpong); }那么還需要一個條件就是,icsk->icsk_ack.pingpong 要為0,才行,否則單獨(dú)增加?icsk->icsk_ack.quick 的值并不能保證立刻回復(fù)ack。
而我們目前這個流,明顯是一個單向的發(fā)包流,并不是pingpong模式,所以這個值肯定為0,那么我們就滿足了?tcp_in_quickack_mode 的條件,
打破了本端的delay_ack模式。
?
總結(jié):
我只是搜索了tcp_enter_quickack_mode 的代碼流程,沒有注意到?tcp_incr_quickack 的調(diào)用,導(dǎo)致這個問題查了小半天。業(yè)務(wù)不精。
如果連續(xù)兩個小包,加起來超過mss了,則可能會觸發(fā)對端在delay_ack模式下立即回復(fù)ack,但是如果一個小包就打破了對端的delay_ack,則需要關(guān)注這個
小包的發(fā)包間隔了。
那么問題來了,為什么會相隔這么長時間發(fā)送小包?后面會繼續(xù)探討。
?
轉(zhuǎn)載于:https://www.cnblogs.com/10087622blog/p/10423118.html
總結(jié)
以上是生活随笔為你收集整理的tcp的发送端一个小包就能打破对端的delay_ack么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: win7可以安装sqlserver200
- 下一篇: python locust 能压测数据库