TCP发送窗口更新tcp_ack_update_window
在tcp_ack接收ACK处理函数中,如果确认当前走慢速路径,那么会调用tcp_ack_update_window函数检查窗口是否需要更新并更新之,并且更新未确认数据的位置,即更新窗口左边沿;
1 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) 2 { 3 /* 快速路径&& ack确认了新数据 */ 4 if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { 5 ; 6 } 7 /* 慢速路径 */ 8 else { 9 /* 更新发送窗口 */ 10 flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); 11 } 12 }
tcp_ack_update_window执行窗口更新主流程,函数首先根据窗口扩大因子计算实际的窗口大小,然后判断是否需要更新窗口,若需要则对窗口进行更新,注意,只有当窗口不相等的情况下才会实际更新窗口,否则只更新最后一次窗口更新ack序号;窗口更新需要更新窗口,同步MSS,检查是否开启快速路径等;函数最后还将设置未确认数据位置,即窗口左边沿;
1 /* Update our send window. 2 * 3 * Window update algorithm, described in RFC793/RFC1122 (used in linux-2.2 4 * and in FreeBSD. NetBSD's one is even worse.) is wrong. 5 */ 6 static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 ack, 7 u32 ack_seq) 8 { 9 struct tcp_sock *tp = tcp_sk(sk); 10 int flag = 0; 11 u32 nwin = ntohs(tcp_hdr(skb)->window); 12 13 /* 根据扩大因子计算窗口大小 */ 14 if (likely(!tcp_hdr(skb)->syn)) 15 nwin <<= tp->rx_opt.snd_wscale; 16 17 /* 需要更新窗口的话 */ 18 if (tcp_may_update_window(tp, ack, ack_seq, nwin)) { 19 /* 窗口更新标记 */ 20 flag |= FLAG_WIN_UPDATE; 21 22 /* 记录窗口更新的ack序号 */ 23 tcp_update_wl(tp, ack_seq); 24 25 26 /* 发送窗口与通告窗口不等时 */ 27 if (tp->snd_wnd != nwin) { 28 29 /* 更新发送窗口*/ 30 tp->snd_wnd = nwin; 31 32 /* Note, it is the only place, where 33 * fast path is recovered for sending TCP. 34 */ 35 /* 判断是否开启快路标志 */ 36 tp->pred_flags = 0; 37 tcp_fast_path_check(sk); 38 39 /* 有数据要发送 */ 40 if (tcp_send_head(sk)) 41 tcp_slow_start_after_idle_check(sk); 42 43 /* 窗口大于以前记录的最大窗口 */ 44 if (nwin > tp->max_window) { 45 /* 更新最大窗口 */ 46 tp->max_window = nwin; 47 /* 更新mss */ 48 tcp_sync_mss(sk, inet_csk(sk)->icsk_pmtu_cookie); 49 } 50 } 51 } 52 53 /* 更新未确认的数据位置,即窗口左边沿 */ 54 tcp_snd_una_update(tp, ack); 55 56 return flag; 57 }
tcp_may_update_window用于判断窗口是否需要更新,满足以下条件之一则更新:
(1) ACK确认了新的数据;
(2) 未满足(1),ACK未确认数据,通过上面snd_una<=ack的条件,此时只能是snd_una=ack,即未确认新数据,是个重复ack,但是这个ack的序号比之前更新窗口的序号要新,则需要更新snd_wl1;
(3) 未满足(1)(2),未确认数据,ack需要也未更新,但是窗口有所改变,则说明单单发送了一个窗口更新通知;
1 /* Check that window update is acceptable. 2 * The function assumes that snd_una<=ack<=snd_next. 3 */ 4 static inline bool tcp_may_update_window(const struct tcp_sock *tp, 5 const u32 ack, const u32 ack_seq, 6 const u32 nwin) 7 { 8 /* 9 更新条件 10 ack确认序号确认了数据,意味着窗口要收缩 11 ack确认序号未确认新数据,ack序号比上一个更新窗口ack序号要新 12 ack序号与上一个更新装ack序号一致,但是窗口比以前的窗口大 13 */ 14 return after(ack, tp->snd_una) || 15 after(ack_seq, tp->snd_wl1) || 16 (ack_seq == tp->snd_wl1 && nwin > tp->snd_wnd); 17 }