一、滑动窗口实现
所谓的流量控制, 就是告诫对方发送速率不要太快, 要让接收来来得及接收数据。
形容如下;
甲向乙发送数据。经过TCP三握手连接以后, 当乙告诉甲:“我的接受窗口rwnd = 400”(这里rwnd表示receiver window的意思)。所以,发送方的发送窗口不能超过接收方给出的接受窗口的数值。而TCP窗口的单位是字节, 而不是报文段。
例子解释如下:
假设每个报文的长度为100字节, 报文段的序号初始值设置为1。
1、主机乙对该传输过程进行了三次流量控制。(按道理说,seq 1 和 sql 101发送后主机乙放回的rwnd = 200, 但是在该应答中返回的是300, 那可能是因为主机B调用recv函数读取了100字节, 后续若遇到此类情况, 皆是如此处理)。
2、seq因为没有发送成功,第一次流量控制ack= 201,rwnd = 300; 在经过三次发送过, seq = 201, 再次发送属于超时重传。
3、最后返回的ACK应答中, rwnd = 0, 表示甲主机不能再向乙主机传输数据了。 等待乙主机的进程读取数据。
4、如果乙主机的缓存空间又空闲了。 那么乙主机将会向甲主机发送ACK = 1, rwnd = 400的应答, 如果该应答在传输的过程中丢失了。甲主机将会一直等待乙主机的非零窗口通知, 而乙主机也一直在等待甲主机发送的数据,在没有其他措施的情况下, 这种相互等待将会一直死锁下去
由4小点我们引入了一个新的措施, 叫做持续及时器:
*持续计时器:
TCP连接的任何一段, 如果收到了0窗口通知, 那么将会启动持续计时器。当持续计时器的时间到了后, 该端将会发送一个零窗口的探测报文段(携带一个字节的数据),如果还是收到了零窗口通知, 将继续重新启动计时器, 重复该步骤, 直到打破死锁位置。
二、滑动窗口机制
由上图所示, 甲主机的发送窗口为20, 乙主机的接受窗口是20.
1)、甲主机开始接收到了来自乙主机ack = 29, win = 20的窗口通知。 那么甲主机就发送序号为[29,49)里面的数据, 理想乙主机希望接受[29,49)序号的数据。
2)、可能因为序号为31的字节数据丢失、又或者是滞留在网络当中, 导致数据不能完全接受, 那么乙主机发送ack = 31, win = 20的通知窗口给对端
3)、甲主机有接受ack = 31, win = 20,的通知窗口。 接受并且发送序号为[31,51)里面的数据, 又因为可能序号为35的字节丢失重复以上操作
三、试验分析
通过客户端不断向服务写入数据, 但是服务器端并不读取任何数据进入睡眠状态。
服务器程序代码片段:
for(;;){
pause();
n = read(confd, buf, MAXLINE);
printf("n = %d\n", n);
if(n < 0){
if(errno == EINTR) continue;
else{
fprintf(stderr, "read error \n");
break;
}
}
else if(n == 0) break;
else{
buf[n] = 0;
write(confd, buf, n);
}
}
客户端程序代码片段
for(;;){
write(sockfd, sendbuf, MAXLENGTH);
usleep(5);
}
抓包结果:
四、总结
1、了解滑动窗口的含义。
2、怎么通过发动窗口来控制流量的(即工作流程)
3、拓展:在三中的试验结果win = 295 275, 远小于接受缓冲区的空闲缓存区大小值(提示:wscale, 窗口大小偏移因子, 在TCP三路握手中, 可以看到这个值)