TCP拥塞控制慢启动窗口设置===》另外一篇文章
来源:互联网 发布:贵阳市发改委数据铁笼 编辑:程序博客网 时间:2024/06/05 09:37
拥塞控制及慢启动
通塞控制:Congestion Control
简单的说,就是TCP传输过程中,为了避免一下子将网络冲爆,引入的机制。而慢启动,顾名思义,一开始慢慢传,发现没有问题,再增加传输速度。而一旦发现传输有超时,协议会认为网络拥堵,又降低传输速度。
起始的传输速度,就是由初始拥塞窗口,initial congestion window,简称initcwnd参数控制的。
alikernel 2.6.32的内核,initcwnd的初始化,在net/ipv4/tcp_input.c:
__u32 tcp_init_cwnd(struct tcp_sock *tp, struct dst_entry *dst){ __u32 cwnd = (dst ? dst_metric(dst, RTAX_INITCWND) : 0); //可以被调整 if (!cwnd) cwnd = TCP_INIT_CWND; //默认的值 return min_t(__u32, cwnd, tp->snd_cwnd_clamp);}
在include/net/tcp.h定义了TCP_INIT_CWND这个值为10:
/* TCP initial congestion window */#define TCP_INIT_CWND 10
这里的10代表一开始可以传输10*MSS的数据。
我们的MSS=1460,因此初始可以传输14600字节,大约15KB的数据。
设置方式
ip route命令支持调整指定路由的initcwnd:
#ip route change 10.0.0.0/8 via 10.83.251.247 dev bond0 initcwnd 30#ip route list 10.0.0.0/810.0.0.0/8 via 10.83.251.247 dev bond0 initcwnd 30
它是通过netlink的接口,调整了上述dst_entry里边的RTAX_INITCWND,覆盖了默认的值。
场景
客户端访问server端,每次需要返回的数据大约20-30KB,而默认的initcwnd,初始只能一次性传输15KB左右,这就涉及到要分两次传输,多一个round trip的时间。
用户希望能够提升访问速度,让server端能够一次将所有数据传输过来,而不是分两次。因此,就涉及到调整初始拥塞窗口的调整。
问题
发现,调整了server/clinet端的initcwnd后,传输速度并没有改善。
curl测试
通过curl测试的方式,可以看到:
curl -w %{time_starttransfer}:%{time_total}:%{size_download}"\n" 'http://url/'0.287:0.417:30601
time_starttransfer:从开始到第一个字节开始传输的时间
time_total:整个操作持续的时间
size_download:传输的字节数
这里看到total时间比开始传输的时间大不少,应该是分两次传输的。
而对于小包,结果应该是这样的:这里传输4574B<10*MSS,在慢启动初始阶段就传完了,total时间和开始传输的时间基本是一致的。
curl -w %{time_starttransfer}:%{time_total}:%{size_download}"\n" 'http://url/'0.288::0.288::4574
抓包分析
并且,通过抓包也能够证明,initcwnd依旧是10,如图:
10.137.18.42为client端
10.98.60.74为server端
server端建立好连接并收到HTTP GET请求之后,开始发送数据:
在17:49:17.22xxxxx这个时间点,连续发送了1514+4410+2491+5858=10*MSS
然后在下个时间点17:49:17.35xxxxx,单独发送了1514+9531=8*MSS
问题分析
还是看这个抓包文件,发现,clinet和server建立好连接以后,它的Window为14720=10*MSS,因此发送端还是只能发送10*MSS的包过来,因此,这个设置在这里没有生效。
2.6.32内核window初始化
2.6.32版本的alikernel中,将这个receive window设置成TCP_DEFAULT_INIT_RCVWND,这个值定义为10,不可更改,因此改大了initcwnd,还是不生效。
net/ipv4/tcp_output.c
void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale){ unsigned int space = (__space < 0 ? 0 : __space);........ /* Set initial window to a value enough for senders starting with * initial congestion window of TCP_DEFAULT_INIT_RCVWND. Place * a limit on the initial window when mss is larger than 1460. */ if (mss > (1 << *rcv_wscale)) { int init_cwnd = TCP_DEFAULT_INIT_RCVWND; if (mss > 1460) init_cwnd = max_t(u32, (1460 * TCP_DEFAULT_INIT_RCVWND) / mss, 2); *rcv_wnd = min(*rcv_wnd, init_cwnd * mss); }........}
3.10内核window初始化
在3.10版本的alikernel中,receive window的初始化方式已经做了改变:
void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, __u32 *window_clamp, int wscale_ok, __u8 *rcv_wscale, __u32 init_rcv_wnd){ unsigned int space = (__space < 0 ? 0 : __space);........ if (mss > (1 << *rcv_wscale)) { if (!init_rcv_wnd) /* Use default unless specified otherwise */ init_rcv_wnd = tcp_default_init_rwnd(mss); *rcv_wnd = min(*rcv_wnd, init_rcv_wnd * mss); }........}
这个函数传入了一个参数,init_rcv_wnd,如果没有设置,则通过tcp_default_init_rwnd函数拿到默认值,为两倍的TCP_INIT_CWND*2=20。可以看到默认的window做了优化,有10*MSS改成了20*MSS
u32 tcp_default_init_rwnd(u32 mss){ /* Initial receive window should be twice of TCP_INIT_CWND to * enable proper sending of new unsent data during fast recovery * (RFC 3517, Section 4, NextSeg() rule (2)). Further place a * limit when mss is larger than 1460. */ u32 init_rwnd = TCP_INIT_CWND * 2; if (mss > 1460) init_rwnd = max((1460 * init_rwnd) / mss, 2U); return init_rwnd;}
init_rcv_wnd的来源从调用端可以看到,是从dst的RTAX_INITRWND拿到的:
tcp_select_initial_window(tcp_full_space(sk), tp->advmss - (tp->rx_opt.ts_recent_stamp ? tp->tcp_header_len - sizeof(struct tcphdr) : 0), &tp->rcv_wnd, &tp->window_clamp, sysctl_tcp_window_scaling, &rcv_wscale, dst_metric(dst, RTAX_INITRWND));
之前我们知道,这是通过ip route设置的,因此可以猜测是新版的内核中,支持通过ip route修改initrwnd。查阅ip route的man page,确实多了一个initrwnd参数的设置:
3.10内核测试
server端initcwnd设置为30
client端内核为3.10,分两步测试:
1、不做任何修改
2、将initrwnd修改为30,ip route change 10.0.0.0/8 via 10.83.251.247 dev bond0 initrwnd 30
client端默认
抓包分析
截图如下:建立连接时,client的window为29200=20*MSS=20*1460
分两次,将大于20*MSS的数据发出去。
curl的结果:
time_first:286 ms
time_total:414 ms
total_byte:30333 bytes
client端initrnwd改为30
抓包分析
截图如下:建立连接时,client的window已经变成43200=30*MSS=30*1460,并且在21:31:36.97xxxx时间点,一下子将所有的数据包都发出去了,总数大于20*MSS
curl的结果:
time_first:285 ms
time_total:285 ms
total_byte:30452 bytes
从结果中,可以看到,3.10版本内核的client,可以通过设置initrwnd,真正实现提升慢启动的速度。并且,对于短连接应用,响应速度提升明显。(小于15KB的包,没有变化)
initcwnd对长连接的影响
我们知道,拥塞窗口会涨,并且涨的速度还挺快:
每当收到一个ACK,cwnd++; 线性上升
每当过了一个RTT,cwnd = cwnd*2; 指数上升
当然,有最大限制。
理论上,对于长连接,跑了一段时间后,cwnd肯定会涨到很高。但是当连接空闲一段时间后,又会重新回到慢启动过程,如下,tcp_cwnd_restart函数,重置cwnd。
static void tcp_event_data_sent(struct tcp_sock *tp, struct sk_buff *skb, struct sock *sk){ struct inet_connection_sock *icsk = inet_csk(sk); const u32 now = tcp_time_stamp; if (sysctl_tcp_slow_start_after_idle && (!tp->packets_out && (s32)(now - tp->lsndtime) > icsk->icsk_rto)) tcp_cwnd_restart(sk, __sk_dst_get(sk)); ......}
当tcp_slow_start_after_idle这个内核参数开启了(默认开启),并且一段时间没有包传输,则会重新进入慢启动,这段时间为 icsk->icsk_rto=inet_csk(sk)->icsk_rto,等于TCP_TIMEOUT_INIT,这个常量在include/net/tcp.h中定义为3s(2.6.32内核为3s,3.10内核为1s)
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */
因次,要让长连接不会重新进入慢启动,可以关闭tcp_slow_start_after_idle:
#sysctl -w net.ipv4.tcp_slow_start_after_idle=0
- TCP拥塞控制慢启动窗口设置===》另外一篇文章
- TCP拥塞控制慢启动窗口设置
- TCP 慢启动 拥塞控制
- TCP 慢启动 拥塞控制
- TCP:浅析拥塞控制窗口、慢启动、拥塞避免在linux内核中的实现
- tcp的拥塞窗口和慢启动
- TCP拥塞控制-慢启动、拥塞避免、快重传、快启动
- TCP拥塞控制-慢启动、拥塞避免、快重传、快启动
- TCP拥塞控制-慢启动、拥塞避免、快重传、快启动
- TCP慢启动与拥塞控制笔记
- TCP拥塞控制,慢启动算法
- TCP窗口拥塞控制:
- TCP拥塞控制中慢启动的过程
- TCP慢启动、拥塞控制、快速重传、快速恢复
- TCP-IP详解: 慢启动和拥塞控制
- 慢启动与拥塞窗口
- TCP/IP详解--拥塞控制 & 慢启动 快恢复 拥塞避免
- TCP/IP详解--拥塞控制 & 慢启动 快恢复 拥塞避免
- 关于TCP的一个发送机制的现象观察和记录
- javascript的事件流
- css设置内容垂直居中
- 计算机网络 之 局域网
- git add -A 和 git add . 的区别
- TCP拥塞控制慢启动窗口设置===》另外一篇文章
- vue
- JS 中alert总结
- Windows下git修改文件权限
- Eclipses 如何支持CMake Project
- javascript 全局变量是可怕的?
- 深搜倒水问题
- 剑指offer之空格替换java
- css3最新版中文参考手册在线浏览