TCP的滑动窗口是一个很重要的概念,也是很晦涩的一个知识点。下面就大概介绍下TCP滑动窗口为什么出现?它是怎么工作的的?
什么是TCP窗口
首先,要理解,client和server各自协议栈都有自己的buffer,应用层读写数据的源都是协议栈buffer里。以接收端为例,应用程序调用read()时,会从buffer里移走数据到用户空间,应用程序读的速度越快(read(1024)必然比read(1)要快),那么buffer里的内容消费的越快,buffer也会越空。那么TCP就可以告诉client,我现在很闲,你可以发送更多的数据来。"更多"是多少?这就说窗口,窗口就是量化接收端和服务端当前能处理数据的能力。
TCP窗口是如何工作的
client和server端建立连接后,client会告诉server,自己的"接收窗口"大小(自己能接收多少的数据,受上面所说的buffer影响),server端接收到client的"接收窗口"大小,就会变成server端自己的"发送窗口"大小。同样的,server端告诉client自己的"接收窗口"大小,就会变成客户端的"发送窗口"大小。
为了理解TCP的窗口大小是怎么样变化的,我们先需要理解它的含义。最简单的方式就是认为窗口大小"意味着接收方能接收数据的大小",这也是说接收端设备再应用程序读取buffer中数据之前,能从对端连接处理多少数据。比如说server端窗口大小是360,那么就意味着server端一次只能从客户端接收不超过360bytes的数据。当server端收到数据,它会将数据放到buffer里,然后server端必须对这份数据做两件事
1. server端必须发送一个 ACK 到client端来确认数据已经收到
2. server端必须处理这份数据,把它交给对应的应用程序
要区分上面两件事对理解窗口很重要,接收方收到数据后会确认,但是数据并不一定是里面就是从buffer里取出的,这是受应用层逻辑控制的。所以很有可能如果接收数据过快,而取出数据更慢,就会导致buffer满。一旦这种情况发生,窗口大小就开始调整来防止接收方负载过高。
正是因为窗口大小的调整可以用来调节数据传输的速率,所以就可以实现TCP的流控,在传输层的流控就是典型的例子,流控对于TCP的通信是很重要的,通过增大或者减小窗口的大小,client和server各自确保彼此设备发数据和收数据平衡。
通过TCP窗口实现流控
下面举一个例子,来看TCP窗口大小变化怎样实现流控。client端和server端已经三次握手建立TCP连接,总窗口大小是TCP建立连接时候确定的。黑色框代表client和server总的窗口大小,红色框代表实际可用的窗口大小。初始化的时候默认client和server总窗口和可用端口分别都是360。另外,假设Client总共只发送360bytes数据,所以总窗口大小不会往前移动。
client 发送140bytes到server端,Seq=1,Length=140;可用窗口大小往前移动,变成260bytes,总窗口大小不变,依然是360。这中间的120是已发送,等待确认;
server端收到140bytes,放入buffer中,但是应用程序很繁忙,只取出100个字节。这时候可用窗口大小: 260(360-100)。接着server端要给client发确认,Ack=141,Window=260。黑框左边缘向前移动,表示140字节已经确认收到,但是应用程序太慢,处理很忙导致我现在只能处理260bytes(回顾下上面server端收到数据要做的两件事);
client收到来自server端的ack回应,首先总窗口左边缘向前移动,表示第一步的140bytes server已经收到,剩下260数据。接着被告知server的可接收窗口是260,client就调整自己的发送窗口是260,表示一次发数据不能超过260;
client发送180bytes,可用窗口变成80(260-180),等待确认发送的180bytes;
server收到180bytes,放入buffer中,应用程序依然很繁忙,这次一个字节都不处理。此时可用窗口大小:80(260-180),然后发180 ack给client,并告知窗口大小80;
client收到ack,确认之前发送的180已经到达,剩余数据还有80字节。被告知server端接收窗口大小是80,调整自己的发送窗口大小为80
client发送80bytes,可用窗口变成0(80-80),等待确认发送的80bytes;
server收到180bytes,放入buffer中,应用程序依然很繁忙,一个字节都不处理。此时可用窗口大小:80(80-80),然后发80 ack给client,并告知窗口大小0;
client收到ack,确认之前发送的80已经到达。被告知server端接收窗口大小是0,调整自己的发送窗口大小为0,此时无论client是否还有数据要发送,都不能再发了。
总结
窗口就是量化接收端和服务端当前能处理数据的能力。个人理解,如发现接收端的窗口越来越小,或者越来越大,都可能会有问题。
1. 接收端的窗口越来越小,那么就是接收端处理不过来(1.发的程序太快,太多;2.收的程序太慢,太少;3.发端带宽比收端带宽更大),会导致接收端发送给发送端的窗口变小,从而发送端调整发送窗口大小,降低发送速度,网络流量就会下降。
2. 接收端的窗口越来越大,那么就是发送端处理不过来(1.收的程序太快,太多;2.发的程序太慢,太少;3.发端带宽比收端带宽更小),会导致接收端发送给发送端的窗口变大,从而发送端调整发送窗口大小,增加发送速度,网络流量就会升高。