阐明:
转载https://plantegg.github.io/20…
外围
linux 2.4 会主动调整tcp recv send buffer 大小, 无需手动设置;
前言
本文心愿解析分明,当咱们在代码中写下 socket.setSendBufferSize 和 sysctl 看到的rmem/wmem零碎参数以及最终咱们在TCP经常谈到的接管发送窗口的关系,以及他们怎么影响TCP传输的性能,同时如何通过图形来展现哪里是传输瓶颈。
拥塞窗口相干文章比拟多,他们跟带宽严密相干,所以大家比拟好判断,反而是接管、发送窗口一旦呈现瓶颈,就没这么好判断了。
先明确一下:文章题目中所说的Buffer指的是sysctl中的 rmem或者wmem,如果是代码中指定的话对应着SO_SNDBUF或者SO_RCVBUF,从TCP的概念来看对应着发送窗口或者接管窗口
最初补充各种场景下的传输案例,一站式将影响传输速度的各种起因都拿下,值得珍藏。
本文次要剖析rt、buffer如何影响TCP的传输性能,更多其余因素影响TCP性能的案例见:TCP传输速度案例剖析
TCP性能和发送接管Buffer的关系
先从碰到的一个理论问题看起:
利用通过专线跨网络拜访云上的服务,专线100M,时延20ms,一个SQL查问了22M数据,后果花了大略25秒,这太慢了,不失常。
如果通过云上client拜访云上服务那么1-2秒就返回了(不跨网络服务是失常的,阐明服务自身没有问题)。
如果通过http或者scp从云下向云上传输这22M的数据大略两秒钟也传送结束了(阐明网络带宽不是瓶颈),
所以这里问题的起因基本上是咱们的服务在这种网络条件下有性能问题,须要找出为什么。
抓包剖析 tcpdump+wireshark
抓包剖析这22M的数据传输,如下图(wireshark 时序图),横轴是工夫,纵轴是sequence number:
image.png
粗一看没啥问题,因为工夫太长覆盖了问题。把这个图形放大,只看两头50ms内的传输状况(横轴是工夫,纵轴是sequence number,一个点代表一个包)
image.png
能够看到传输过程总有一个20ms的期待平台,这20ms没有发送任何包,换个角度,看看窗口尺寸图形:
image.png
从bytes in flight也大抵能算进去总的传输速度 16K*1000/20=800Kb/秒
咱们的利用代码中会默认设置 socketSendBuffer 为16K:
socket.setSendBufferSize(16*1024) //16K send buffer
原理解析
如果tcp发送buffer也就是SO_SNDBUF只有16K的话,这些包很快都收回去了,然而这16K的buffer不能立刻释放出来填新的内容进去,因为tcp要保障牢靠,万一两头丢包了呢。只有等到这16K中的某些包ack了,才会填充一些新包进来而后持续收回去。因为这里rt根本是20ms,也就是16K发送结束后,等了20ms才收到一些ack,这20ms利用、内核什么都不能做,所以就是如后面第二个图中的大略20ms的期待平台。这块请参考这篇文章
比方下图,wmem大小是8,收回1-8后,buffer不能开释,等到收到ack1-4后,开释1-4,buffer也就是开释了一半,这一半能够填充新的发送数据进来了。 下面的问题在于ack花了很久,导致buffer始终不能开释。
image.png
sendbuffer相当于发送仓库的大小,仓库的货物都发走后,不能立刻腾出来发新的货物,而是要等对方确认收到了(ack)能力腾出来发新的货物。 传输速度取决于发送仓库(sendbuffer)、接管仓库(recvbuffer)、路宽(带宽)的大小,如果发送仓库(sendbuffer)足够大了之后接下来的瓶颈就会是高速公路了(带宽、拥塞窗口)。而实际上这个案例中带宽够、接管仓库也够,然而发送仓库太小了,导致发送过程断断续续,所以十分慢。
如果是UDP,就没有牢靠的概念,有数据通通收回去,基本不关怀对方是否收到,也就不须要ack和这个发送buffer了。
几个发送buffer相干的内核参数
$sudo sysctl -a | egrep “rmem|wmem|tcp_mem|adv_win|moderate”
net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992 //core是给所有的协定应用的,
net.core.wmem_max = 212992
net.ipv4.tcp_adv_win_scale = 1 //
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_rmem = 4096 87380 6291456 //最小值 默认值 最大值】
net.ipv4.tcp_wmem = 4096 16384 4194304 //tcp这种就本人的专用选项就不必 core 外面的值了
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
vm.lowmem_reserve_ratio = 256 256 32
net.ipv4.tcp_mem = 88560 118080 177120
vm.lowmem_reserve_ratio = 256 256 32
net.ipv4.tcp_wmem 默认就是16K,而且内核是可能动静调整的,只不过咱们代码中这块的参数是很多年前从Cobra中继承过去的,初始指定了sendbuffer的大小。代码中设置了这个参数后就敞开了内核的动静调整性能,这就是为什么http或者scp都很快,因为他们的send buffer是动静调整的。
接管buffer是有开关能够动态控制的,发送buffer没有开关默认就是开启,敞开只能在代码层面来管制
net.ipv4.tcp_moderate_rcvbuf
解决方案
调整 socketSendBuffer 到256K,查问工夫从25秒降落到了4秒多,然而比实践带宽所须要的工夫略高
持续查看零碎 net.core.wmem_max 参数默认最大是130K,所以即便咱们代码中设置256K理论应用的也是130K,持续调大这个零碎参数后整个网络传输工夫大略2秒(跟100M带宽匹配了,scp传输22M数据也要2秒),整体查问工夫2.8秒。测试用的mysql client短连贯,如果代码中的是长连贯的话会块300-400ms(消掉了握手和慢启动阶段),这基本上是实践上最快速度了
image.png
如果调用setsockopt()设置了socket选项SO_SNDBUF,将敞开发送端缓冲的主动调节机制,tcp_wmem将被疏忽,SO_SNDBUF的最大值由net.core.wmem_max限度。
这个案例对于wmem的论断
默认状况下Linux零碎会主动调整这个buffer(net.ipv4.tcp_wmem), 也就是不举荐程序中被动去设置SO_SNDBUF,除非明确晓得设置的值是最优的。
从这里咱们能够看到,有些实践知识点尽管咱们晓得,然而在实践中很难分割起来,也就是常说的无奈学以致用,最开始看到抓包后果的时候比拟狐疑发送、接管窗口之类的,没有间接想到send buffer上,实践跟实际没分割上。
BDP 带宽时延积
BDP=rtt*(带宽/8)
这个 buffer 调到1M测试没有帮忙,从实践计算BDP(带宽时延积) 0.02秒(100MB/8)=250Kb 所以 SO_SNDBUF为256Kb的时候根本能跑满带宽了,再大也没有什么实际意义了 。也就是后面所说的仓库足够后瓶颈在带宽上了。
因为这里依据带宽、rtt计算失去的BDP是250K,BDP跑满后拥塞窗口(带宽、接管窗口和rt决定的)行将成为新的瓶颈,所以调大buffer没意义了。
接下来看看接管buffer(rmem)和接管窗口的关系
用这样一个案例下来验证接管窗口的作用:
有一个batch insert语句,整个一次要插入5532条记录,所有记录大小总共是376K,也就是这个sql语句自身是376K。
SO_RCVBUF很小的时候并且rtt很大对性能的影响
如果rtt是40ms,总共须要5-6秒钟:
image.png
根本能够看到server一旦空进去点窗口,client马上就发送数据,因为这点窗口太小,rtt是40ms,也就是一个rtt能力传3456字节的数据,整个带宽才用到80-90K,齐全没跑满。
image.png
比拟显著距离 40ms 一个期待台阶,台阶之间两个包大略3K数据,总的传输效率如下:
image.png
斜线越陡示意速度越快,从上图看整体SQL上传花了5.5秒,执行0.5秒。
此时对应的窗口尺寸:
image.png
窗口由最开始28K(20个1448)很快降到了不到4K的样子,而后根本游走在行将满的边缘,尽管读取慢,幸好rtt也大,导致最终也没有满。(这个是3.1的Linux,利用SO_RCVBUF设置的是8K,用一半来做接管窗口)
SO_RCVBUF很小的时候并且rtt很小对性能的影响
如果同样的语句在 rtt 是0.1ms的话
image.png
尽管显著看到接管窗口常常跑满,然而因为rtt很小,一旦窗口空进去很快就告诉到对方了,所以整个过小的接管窗口也没怎么影响到整体性能
image.png
如上图11.4秒整个SQL开始,到11.41秒SQL上传完毕,11.89秒执行结束(执行花了0.5秒),上传只花了0.01秒
接管窗口状况:
image.png
如图,接管窗口由最开始的28K降下来,而后始终在5880和满了之间跳动
image.png
从这里能够得出结论,接管窗口的大小对性能的影响,rtt越大影响越显著,当然这里还须要应用程序配合,如果应用程序始终不读走数据即便接管窗口再大也会堆满的。
SO_RCVBUF和tcp window full的坏case
image.png
上图中红色平台局部,进展了大略6秒钟没有发任何有内容的数据包,这6秒钟具体在做什么如下图所示,能够看到这个时候接管方的TCP Window Full,同时也能看到接管方(3306端口)的TCP Window Size是8192(8K),发送方(27545端口)是20480.
image.png
这个情况跟后面形容的recv buffer太小不一样,8K是很小,然而因为rtt也很小,所以server总是能很快就ack收到了,接管窗口也始终不容易达到full状态,然而一旦接管窗口达到了full状态,竟然须要惊人的6秒钟能力复原,这期待的工夫有点太长了。这里应该是利用读取数据太慢导致了耗时6秒才复原,所以最终这个申请执行会十分十分慢(工夫次要耗在了上传SQL而不是执行SQL).
理论起因不晓得,从读取TCP数据的逻辑来看这里没有显著的block,可能的起因:
request的SQL太大,Server(3306端口上的服务)从TCP读取SQL须要放到一块调配好的内存,内存不够的时候须要扩容,扩容有可能触发fgc,从图形来看,第一次满就卡顿了,而且每次满都卡顿,不像是这个起因
request申请一次发过来的是多个SQL,利用读取SQL后,将SQL分成多个,而后先执行第一个,第一个执行完后返回response,再读取第二个。图形中卡顿前没有response返回,所以也不是这个起因
……其它未知起因
接管方不读取数据导致的接管窗口满同时有丢包产生
服务端返回数据到client端,TCP协定栈ack这些包,然而应用层没读走包,这个时候 SO_RCVBUF 沉积满,client的TCP协定栈发送 ZeroWindow 标记给服务端。也就是接收端的 buffer 堆满了(然而服务端这个时候看到的bytes in fly是0,因为都ack了),这时服务端不能持续发数据,要等 ZeroWindow 复原。
那么接收端下层利用不读走包可能的起因:
利用代码卡顿、GC等等
利用代码逻辑上在做其它事件(比方Server将SQL分片到多个DB上,Server先读取第一个分片,如果第一个分片数据很大很大,解决也慢,那么即便第二个分片数据都返回到了TCP 的recv buffer,利用也没去读取其它分片的后果集,直到第一个分片读取结束。如果SQL带排序,那么Server会轮询读取多个分片,造成这种卡顿的概率小了很多)
image.png
上图这个流因为应用层不读取TCP数据,导致TCP接管Buffer满,进而接管窗口为0,server端不能再发送数据而卡住,然而ZeroWindow的探测包,client都有失常回复,所以1903秒之后接管方窗口不为0后(window update)传输复原。
image.png
这个截图和前一个相似,是在Server上(3003端口)抓到的包,不同的是接管窗口为0后,server端屡次探测(Server上抓包能看到),然而client端没有回复 ZeroWindow(也有可能是回复了,然而中间环节把ack包丢了,或者这个探测包client没收到),造成server端认为client死了、不可达之类,进而重复重传,重传超过15次之后,server端认为这个连贯死了,粗犷单方面断开(没有reset和fin,因为没必要,server认为网络连通性出了问题)。
等到1800秒后,client的接管窗口复原了,发个window update给server,这个时候server认为这个连贯曾经断开了,只能回复reset
网络不通,重传超过肯定的工夫(tcp_retries2)而后断开这个连贯是失常的,这里的问题是:
为什么这种场景下丢包了,而且是针对某个stream始终丢包
可能是因为这种场景下触发了中间环节的流量管控,成心丢包了(比方proxy、slb、交换机都有可能做这种选择性的丢包)
这里server认为连贯断开,没有发reset和fin,因为没必要,server认为网络连通性出了问题。client还不晓得server上这个连贯清理掉了,等client回复了一个window update,server早就认为这个连贯早断了,忽然收到一个update,莫名其妙,只能reset
接管窗口和SO_RCVBUF的关系
ss 查看socket buffer大小
初始接管窗口个别是 mss乘以初始cwnd(为了和慢启动逻辑兼容,不想一下子冲击到网络),如果没有设置SO_RCVBUF,那么会依据 net.ipv4.tcp_rmem 动态变化,如果设置了SO_RCVBUF,那么接管窗口要向上面形容的值聚拢。
初始cwnd能够大抵通过查看到:
ss -itmpn dst “10.81.212.8”
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 10.xx.xx.xxx:22 10.yy.yy.yyy:12345 users:((“sshd”,pid=1442,fd=3))
skmem:(r0,rb369280,t0,tb87040,f4096,w0,o0,bl0,d92)
Here we can see this socket has Receive Buffer 369280 bytes, and Transmit Buffer 87040 bytes.Keep in mind the kernel will double any socket buffer allocation for overhead.
So a process asks for 256 KiB buffer with setsockopt(SO_RCVBUF) then it will get 512 KiB buffer space. This is described on man 7 tcp.
初始窗口计算的代码逻辑,重点在17行:
/* TCP initial congestion window as per rfc6928 */ #define TCP_INIT_CWND 10 /* 3. Try to fixup all. It is made immediately after connection enters established state. */ void tcp_init_buffer_space(struct sock *sk) { int tcp_app_win = sock_net(sk)->ipv4.sysctl_tcp_app_win; struct tcp_sock *tp = tcp_sk(sk); int maxwin; if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) tcp_sndbuf_expand(sk); //初始最大接管窗口计算过程 tp->rcvq_space.space = min_t(u32, tp->rcv_wnd, TCP_INIT_CWND * tp->advmss); tcp_mstamp_refresh(tp); tp->rcvq_space.time = tp->tcp_mstamp; tp->rcvq_space.seq = tp->copied_seq; maxwin = tcp_full_space(sk); if (tp->window_clamp >= maxwin) { tp->window_clamp = maxwin; if (tcp_app_win && maxwin > 4 * tp->advmss) tp->window_clamp = max(maxwin - (maxwin >> tcp_app_win), 4 * tp->advmss); } /* Force reservation of one segment. */ if (tcp_app_win && tp->window_clamp > 2 * tp->advmss && tp->window_clamp + tp->advmss > maxwin) tp->window_clamp = max(2 * tp->advmss, maxwin - tp->advmss); tp->rcv_ssthresh = min(tp->rcv_ssthresh, tp->window_clamp); tp->snd_cwnd_stamp = tcp_jiffies32;
}
传输过程中,最大接管窗口会动静调整,当指定了SO_RCVBUF后,理论buffer是两倍SO_RCVBUF,然而要分出一部分(2^net.ipv4.tcp_adv_win_scale)来作为乱序报文缓存。
net.ipv4.tcp_adv_win_scale = 2 //2.6内核,3.1中这个值默认是1
如果SO_RCVBUF是8K,总共就是16K,而后分出2^2分之一,也就是4分之一,还剩12K当做接管窗口;如果设置的32K,那么接管窗口是48K
static inline int tcp_win_from_space(const struct sock sk, int space)
{//space 传入的时候就曾经是 2SO_RCVBUF了
int tcp_adv_win_scale = sock_net(sk)->ipv4.sysctl_tcp_adv_win_scale;
return tcp_adv_win_scale <= 0 ? (space>>(-tcp_adv_win_scale)) : space - (space>>tcp_adv_win_scale); //sysctl参数tcp_adv_win_scale
}
接管窗口有最大接管窗口和以后可用接管窗口。
一般来说一次中断根本都会将 buffer 中的包都取走。
image.png
绿线是最大接管窗口动静调整的过程,最开始是146010,握手结束后稍微调整到147210(可利用body减少了12),随着数据的传输开始跳涨
image.png
上图是四个batch insert语句,能够看到绿色接管窗口随着数据的传输越来越大,图中蓝色竖直局部根本示意SQL上传,两个蓝色竖直条的距离代表这个insert在服务器上真正的执行工夫。这图十分平缓,示意上传没有任何瓶颈.
设置 SO_RCVBUF 后通过wireshark察看到的接管窗口根本
下图是设置了 SO_RCVBUF 为8192的理论状况:
image.png
从最开始的14720,执行第一个create table语句后降到14330,到真正执行batch insert就降到了8192*1.5. 而后始终放弃在这个值
从kernel来看buffer相干信息
kernel相干参数
sudo sysctl -a | egrep “rmem|wmem|tcp_mem|adv_win|moderate”
net.core.rmem_default = 212992
net.core.rmem_max = 212992
net.core.wmem_default = 212992 //core是给所有的协定应用的,
net.core.wmem_max = 212992
net.ipv4.tcp_adv_win_scale = 1
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_rmem = 4096 87380 6291456
net.ipv4.tcp_wmem = 4096 16384 4194304 //tcp有本人的专用选项就不必 core 外面的值了
net.ipv4.udp_rmem_min = 4096
net.ipv4.udp_wmem_min = 4096
vm.lowmem_reserve_ratio = 256 256 32
net.ipv4.tcp_mem = 88560 118080 177120
发送buffer零碎比拟好主动调节,依附发送数据大小和rt延时大小,能够相应地进行调整;然而承受buffer就不肯定了,承受buffer的应用取决于收到的数据快慢和利用读走数据的速度,只能是OS依据零碎内存的压力来调整承受buffer。零碎内存的压力取决于 net.ipv4.tcp_mem.
须要特地留神:tcp_wmem 和 tcp_rmem 的单位是字节,而 tcp_mem 的单位的页面
image.png
kernel相干源码
从内核代码来看如果利用代码设置了sndbuf(比方java代码中:socket.setOption(sndbuf, socketSendBuffer))那么理论会调配socketSendBuffer*2的大小进去
image.png
比方利用代码有如下设置:
protected int socketRecvBuffer = 32 * 1024; //接管32K
protected int socketSendBuffer = 64 * 1024; //发送64K,理论会调配128K
// If bufs set 0, using '/etc/sysctl.conf' system settings on default // refer: net.ipv4.tcp_wmem / net.ipv4.tcp_rmem if (socketRecvBuffer > 0) { socket.setReceiveBufferSize(socketRecvBuffer); } if (socketSendBuffer > 0) { socket.setSendBufferSize(socketSendBuffer); }
理论会看到这样的:
tcp ESTAB 45 0 10.0.186.140:3306 10.0.186.70:26494 skmem:(r768,rb65536,t0,tb131072,f3328,w0,o0,bl0,d0)
tcp ESTAB 0 0 10.0.186.140:3306 10.0.186.70:26546 skmem:(r0,rb65536,t0,tb131072,f4096,w0,o0,bl0,d0)
为什么kernel要double 接管和发送buffer能够参考man7中的socket帮忙信息
image.png
tcp包发送流程
image.png
(图片来自)
用tc结构延时和带宽限度的模仿重现环境
sudo tc qdisc del dev eth0 root netem delay 20ms
sudo tc qdisc add dev eth0 root tbf rate 500kbit latency 50ms burst 15kb
内核观测tcp_mem是否有余
因 tcp_mem 达到限度而无奈发包或者产生抖动的问题,咱们也是能够观测到的。为了不便地观测这类问题,Linux 内核外面预置了动态观测点:sock_exceed_buf_limit(须要 4.16+ 的内核版本)。
$ echo 1 > /sys/kernel/debug/tracing/events/sock/sock_exceed_buf_limit/enable
而后去看是否有该事件产生:
$ cat /sys/kernel/debug/tracing/trace_pipe
如果有日志输入(即产生了该事件),就意味着你须要调大 tcp_mem 了,或者是须要断开一些 TCP 连贯了。
或者通过systemtap来察看
如下是tcp_sendmsg流程,sk_stream_wait_memory就是tcp_wmem不够的时候触发期待:
image.png
如果sendbuffer不够就会卡在上图中的第一步 sk_stream_wait_memory, 通过systemtap脚本能够验证:
#!/usr/bin/stap
# Simple probe to detect when a process is waiting for more socket send # buffer memory. Usually means the process is doing writes larger than the # socket send buffer size or there is a slow receiver at the other side. # Increasing the socket's send buffer size might help decrease application # latencies, but it might also make it worse, so buyer beware.
probe kernel.function(“sk_stream_wait_memory”)
{
printf("%u: %s(%d) blocked on full send buffern", gettimeofday_us(), execname(), pid())
}
probe kernel.function(“sk_stream_wait_memory”).return
{
printf("%u: %s(%d) recovered from full send buffern", gettimeofday_us(), execname(), pid())
}
Typical output: timestamp in microseconds: procname(pid) event
1218230114875167: python(17631) blocked on full send buffer
1218230114876196: python(17631) recovered from full send buffer
1218230114876271: python(17631) blocked on full send buffer
1218230114876479: python(17631) recovered from full send buffer
其它案例剖析
从如下案例能够看到在时延5ms和1ms的时候,别离执行雷同的SQL,SQL查问后果13M,耗时别离为4.6和0.8秒
$time mysql -h127.1 -e “select * from test;” >/tmp/result.txt
real 0m3.078s
user 0m0.273s
sys 0m0.028s
$ping -c 1 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=5.01 ms
— 127.0.0.1 ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.018/5.018/5.018/0.000 ms
$ls -lh /tmp/result.txt
-rw-rw-r– 1 admin admin 13M Mar 12 12:51 /tmp/result.txt
//减小时延后持续测试
$ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=1.01 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=1.02 ms
^C
— 127.0.0.1 ping statistics —
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.016/1.019/1.022/0.003 ms
$time mysql -h127.1 -e “select * from test;” >/tmp/result.txt
real 0m0.838s
user 0m0.271s
sys 0m0.030s
//通过ss能够看到这个连贯的buffer 大小相干信息,3306端口socket的send buffer为32K;
//7226为客户端,发送buffer为128K,OS默认参数
tcp ESTAB 0 0 127.0.0.1:7226 127.0.0.1:3306 skmem:(r0,rb131072,t2,tb2626560,f24576,w0,o0,bl0,d0)
tcp ESTAB 0 20480 127.0.0.1:3306 127.0.0.1:7226 skmem:(r0,rb16384,t0,tb32768,f1792,w26880,o0,bl0,d0)
在这个案例中 send buffer为32K(代码中设置的16K,内核会再翻倍,所以是32K),如果时延5毫秒时,一秒钟最多执行200次来回,也就是一秒钟能传输:200*32K=6.4M,总大小为13M,也就是最快须要2秒钟能力传输行完,另外MySQL innodb执行耗时0.5ms,也就是极限速度也就是2.5秒+了。
这个场景下想要快得缩小rt或者减少send buffer, 减少接收端的buffer没有意义,比方如下代码减少client的 –net-buffer-length=163840000 没有任何帮忙
time mysql –net-buffer-length=163840000 -h127.1 -e “select * from test;” >/tmp/result.txt
总结
一般来说相对不要在程序中手工设置SO_SNDBUF和SO_RCVBUF,内核主动调整比你做的要好;
SO_SNDBUF个别会比发送滑动窗口要大,因为发送进来并且ack了的能力从SO_SNDBUF中开释;
代码中设置的SO_SNDBUF和SO_RCVBUF在内核中会翻倍调配;
TCP接管窗口跟SO_RCVBUF关系很简单;
SO_RCVBUF太小并且rtt很大的时候会重大影响性能;
接管窗口比发送窗口简单多了;
发送窗口/SO_SNDBUF–发送仓库,带宽/拥塞窗口–马路通顺水平,接管窗口/SO_RCVBUF–接管仓库;
发送仓库、马路宽度、长度(rt)、接管仓库一起决定了传输速度–类比一下快递过程。
总之记住一句话:不要设置socket的SO_SNDBUF和SO_RCVBUF
对于传输速度的总结:窗口要足够大,包含发送、接管、拥塞窗口等,天然就能将BDP跑满
相干和参考文章
用stap从内核角度来剖析buffer、rt和速度
经典的 nagle 和 dalay ack对性能的影响 就是要你懂 TCP– 最经典的TCP性能问题
对于TCP 半连贯队列和全连贯队列
MSS和MTU导致的喜剧
双11通过网络优化晋升10倍性能
就是要你懂TCP的握手和挥手