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如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)

详解TCP如何确保可靠传输(确认应答,重传机制,滑动窗口,流量控制)

计网传输层(TCPUDP可靠传输流量控制......)

计网传输层(TCPUDP可靠传输流量控制......)