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 的益处:
在这里插入图片描述

皮鞋不会胖
蜷缩着过活,总希望是大梦一场,
可我挺直了身子,却还是看不到光,
你说我不够努力,我说我没有梦想,
街头徘徊着梦,需要与酒邂逅。
浙江温州皮鞋湿,下雨进水不会胖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值