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 }

 

posted @ 2019-10-28 10:09  AlexAlex  阅读(4937)  评论(0编辑  收藏  举报