6.2 TCP滑动窗口

TCP滑动窗口是实现流量控制的关键,本文深入探讨了窗口的生成、维护和消费。初始窗口在连接建立时设置,窗口维护涉及接收数据后的更新,而窗口消费则关乎数据发送和接收的判断。通过理解这三个方面,可以更好地理解TCP协议的窗口机制。
摘要由CSDN通过智能技术生成

  TCP滑动窗口的功能是实现流量控制。数据接收方只接收seq落入窗口范围内的数据;发送方也不会发送窗口之外的数据,一旦发现窗口太小则会停止发送直到窗口变大,这样TCP数据接收方就能通过窗口通告来控制数据发送方发送数据的速度。窗口的值存储在TCP报文段的window字段中,大小为16bit,即窗口的最大值是65535。如果使用窗口扩大选项(后续讨论),则通告窗口的值为window左移窗口扩大因子个位数。

  关于滑动窗口其实只有三个问题需要明晰:

(1)窗口(包括初始窗口)如何生成(即生产)

(2)窗口信息如何更新(即维护)

(3)窗口如何被使用(即消费)

  下面一一解答。

6.2.1 窗口生产

  初始窗口的设置在发送SYN和SYN|ACK时进行:


 2752 void tcp_connect_init(struct sock *sk)
2753 {
2754     const struct dst_entry *dst = __sk_dst_get(sk);
2755     struct tcp_sock *tp = tcp_sk(sk);
2756     __u8 rcv_wscale;     
...
2789     tcp_select_initial_window(tcp_full_space(sk),
2790                   tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphd     r) : 0), //MSS - 时间戳选项大小
2791                   &tp->rcv_wnd,
2792                   &tp->window_clamp,
2793                   sysctl_tcp_window_scaling,
2794                   &rcv_wscale,
2795                   dst_metric(dst, RTAX_INITRWND));
2796 
2797     tp->rx_opt.rcv_wscale = rcv_wscale;
2798     tp->rcv_ssthresh = tp->rcv_wnd; ... 
   构建SYN|ACK时:
 2654 struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
2655                 struct request_sock *req,
2656                 struct tcp_fastopen_cookie *foc)
2657 {
...
2692         /* tcp_full_space because it is guaranteed to be the first packet */
2693         tcp_select_initial_window(tcp_full_space(sk),
2694             mss - (ireq->tstamp_ok ? TCPOLEN_TSTAMP_ALIGNED : 0), //MSS - 时间戳选项大小
2695             &req->rcv_wnd,
2696             &req->window_clamp,
2697             ireq->wscale_ok,
2698             &rcv_wscale,
2699             dst_metric(dst, RTAX_INITRWND));
2700         ireq->rcv_wscale = rcv_wscale;
...
  这样看来 计算初始窗口大小的功能是由tcp_select_initial_window函数完成的:
 191 void tcp_select_initial_window(int __space, __u32 mss,
 192                    __u32 *rcv_wnd, __u32 *window_clamp,
 193                    int wscale_ok, __u8 *rcv_wscale,
 194                    __u32 init_rcv_wnd)
 195 {   
 196     unsigned int space = (__space < 0 ? 0 : __space); //__space是TCP根据接收缓存的大小计算出来的
 197         
 198     /* If no clamp set the clamp to the max possible scaled window */
 199     if (*window_clamp == 0)    //*window_clamp的值是通告给对端的最大的窗口值
 200         (*window_clamp) = (65535 << 14); //窗口扩大因子最大为14
 201     space = min(*window_clamp, space);
 202                   
 203     /* Quantize space offering to a multiple of mss if possible. */
 204     if (space > mss)
 205         space = (space / mss) * mss; //将space整理为mss的整数倍
...
 215     if (sysctl_tcp_workaround_signed_windows) //使用有符号的接收窗口
 216         (*rcv_wnd) = min(space, MAX_TCP_WINDOW);  //设置通告窗口小于32767,否则可能会导致一些不稳定的TCP协议实现的崩溃
 217     else
 218         (*rcv_wnd) = space; //将通告窗口的值设置为space
 219
 220     (*rcv_wscale) = 0;
 221     if (wscale_ok) {//开启窗口扩大选项
 222         /* Set window scaling on max possible window
 223          * See RFC1323 for an explanation of the limit to 14
 224          */
 225         space = max_t(u32, sysctl_tcp_rmem[2], sysctl_rmem_max); //接收缓存最大空间
 226         space = min_t(u32, space, *window_clamp);
 227         while (space > 65535 && (*rcv_wscale) < 14) { //计算窗口扩大因子
 228             space >>= 1;
 229             (*rcv_wscale)++;
 230         }
 231     }
 232
 233     /* Set initial window to a value enough for senders starting with
 234      * initial congestion window of TCP_DEFAULT_INIT_RCVWND. Place
 235      * a limit on the initial window when mss is larger than 1460.
 236      */
 237     if (mss > (1 << *rcv_wscale)) {
 238         int init_cwnd = TCP_DEFAULT_INIT_RCVWND;
 239         if (mss > 1460)
 240             init_cwnd =
 241             max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2);
 242         /* when initializing use the value from init_rcv_wnd
 243          * rather than the default from above
 244          */
 245         if (init_rcv_wnd)
 246             *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss);
 247         else
 248             *rcv_wnd = min(*rcv_wnd, init_cwnd * mss);
 249     }
 250
 251     /* Set the clamp no higher than max representable value */
 252     (*window_clamp) = min(65535U << (*rcv_wscale), *window_clamp);
 253 }
   tcp_full_space函数返回可用空间的大小:
 1056 static inline int tcp_win_from_space(int space)
1057 {
1058     return sysctl_tcp_adv_win_scale<=0 ?
1059         (space>>(-sysctl_tcp_adv_win_scale)) :
1060         space - (space>>sysctl_tcp_adv_win_scale);
1061 }
...
1070 static inline in
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值