TCP 糊涂窗口综合症(silly window syndrome)与 rate-based 流控
Posted dog250
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP 糊涂窗口综合症(silly window syndrome)与 rate-based 流控相关的知识,希望对你有一定的参考价值。
昨天同事给我看了一个有趣的 case,接收端抓包:
现象就是这样,但结论也很明显:
- ack 渐长,win 渐缩,有数据陆续到达 rcvbuff,但 app 尚未读取。
- ack 不变,win 渐长,没有新数据到达,app 持续读取数据。
据说这是个 ios 系统的 case。
该现象与 Linux TCP 并不匹配,Linux TCP 仅在下面的情况才会发送 win update:
- 窗口从 0 变为非 0,且…
- rwnd 腾出了一半的空间。
条件过于苛刻。做以下实验:
Linux server 端代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#define SIZE 16384
#define PORT 1234
void senddata(int csd)
char buff[SIZE];
int n, i;
for (i = 0; i < 8; i++)
write(csd, buff, sizeof(buff));
getchar();
int main()
int sockfd, csd, len;
struct sockaddr_in servaddr, cli;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(PORT);
bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
listen(sockfd, 5);
len = sizeof(cli);
while(1)
csd = accept(sockfd, (struct sockaddr *)&cli, &len);
senddata(csd);
close(csd);
Linux client 和 MacOS client 端代码:
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#define SIZE 2048
#define PORT 1234
void recvdata(int sd)
char buff[SIZE];
int n, i;
getchar();
for (i = 0; i < 64; i++)
read(sd, buff, sizeof(buff));
int main()
int sd, connfd;
struct sockaddr_in servaddr, cli;
sd = socket(AF_INET, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr("192.168.10.6");
servaddr.sin_port = htons(PORT);
connect(sd, (struct sockaddr*)&servaddr, sizeof(servaddr));
recvdata(sd);
close(sd);
运行 client 后敲回车,结果显示,MacOS client 在敲下会车后从 rcvbuff 读取了一定字节后就会发送 win update,而 Linux 不会。
OK,开始评谈。
早期,资源所限,为避免糊涂窗口综合症,TCP 采取一定措施规避,但在事情的反面反而阻碍了 rate-based 流控。
糊涂窗口综合症本质上包括下面因素:
- 接收端接收数据太慢。
- 发送端产生数据太慢。
- 担心网络上充斥小包。
但根据管道模型,理论上哪怕一次通告 1 字节 rwnd,只要通告速度足够快,依然可达任意带宽。问题是:
- 如果接收端接收速度足够快,窗口还糊涂吗?
- 既然接收端慢可以影响发送,为什么接收端快就不能呢?
如何从面向 rcvbuff 的流控转向面向应用程序接收能力的 rate-based 流控,问题就在于何时发送 win update。Linux TCP 在应用程序读取数据后发送 win update 的条件太苛刻。
如今早年的限制已不再:
- 内存足够大,可承受更大的意外突发。
- 带宽足够大,可容忍长报头损失的载荷率。
- 拥塞控制纷纷从 buffer-based 转向 rate-based。
Linux TCP 在 30 年后依然是很吝啬的实现,旨在节省资源以适应早期环境,MacOS 似乎好一点,但我还没 deep into Darwin 代码,不评说。
糊涂窗口综合症是慢速环境下的症状,减缓症状的措施在高速环境下必然掣肘。
看看 win update after read 的益处:
皮鞋不会胖
蜷缩着过活,总希望是大梦一场,
可我挺直了身子,却还是看不到光,
你说我不够努力,我说我没有梦想,
街头徘徊着梦,需要与酒邂逅。
浙江温州皮鞋湿,下雨进水不会胖。
以上是关于TCP 糊涂窗口综合症(silly window syndrome)与 rate-based 流控的主要内容,如果未能解决你的问题,请参考以下文章
TCP/IP传输层协议实现 - TCP的坚持定时器(lwip)
TCP如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)
详解TCP如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)