网络是怎样连接的-收发数据(下)

2.3.4 根据网络包平均往返时间调整 ACK 号等待时间

网络堵塞后TCP会重传网络包

前面说的只是一些基本原理,实际上网络的错误检测和补偿机制非常复杂。

首先是返回 ACK 号的等待时间,这个等待时间叫超时时间。

当网络传输繁忙时就会发生拥塞,ACK 号的返回会变慢。

这时我们就必须将等待时间设置得长一点,否则可能会重传了包之后,前面的 ACK 号才来。

这样的重传是多余的,看上去只是多发一个包,但它造成的后果却没那么简单。

 ACK 号的返回变慢大多是由于网络拥塞引起的,如果此时再出现很多多余的重传,网络会更加拥塞。

如果等待时间过长,那么包的重传就会出现很大的延迟,也会导致网络速度变慢。

如果某一个包被重复发送多次,接收方可以根据序号判断出这个包是重复的,因此并不会造成网络异常。

TCP采用了动态调整等待ACK返回时间的方法

根据服务器物理距离的远近,ACK 号的返回时间也会产生很大的波动,所以将等待时间设置为一个固定值并不是一个好办法。

TCP 采用了动态调整等待时间的方法,这个等待时间是根据 ACK 号返回所需的时间来判断的。

TCP 会在发送数据的过程中持续测量 ACK 号的返回时间,如果 ACK 号返回变慢,则相应延长等待时间。

相对地,如果 ACK 号马上就能返回,则相应缩短等待时间。

由于计算机的时间测量精度较低,ACK 返回时间过短时无法被正确测量,因此等待时间有一个最小值,这个值在每个操作系统上不一样,基本上是在 0.5 秒到 1 秒之间。

 

2.3.5 使用窗口有效管理 ACK 号

滑动窗口机制利用等待ACK号的时间直接发送后续的一系列包

每发送一个包就等待一个 ACK 号的方式是最简单也最容易理解的。

等待 ACK 号的这段时间中,什么都不做实在太浪费了。

为了减少这样的浪费,TCP 采用图 2.10(b)这样的滑动窗口方式来管理数据发送和 ACK 号的操作。

滑动窗口,就是在发送一个包之后不等待 ACK 号返回,直接发送后续的一系列包。

这样一来,等待 ACK 号的这段时间就被有效利用起来了。

接收缓冲区接收处理不过来的包

滑动窗口机制导致的问题

虽然这样做能够减少等待 ACK 号时的时间浪费,但有一些问题需要注意。

在一来一回方式中,接收方完成接收操作后返回 ACK 号,然后发送方收到 ACK 号之后才继续发送下一个包,因此不会出现发送的包太多接收方处理不过来的情况。

但如果不等返回 ACK 号就连续发送包,就有可能会出现发送包的频率超过接收方处理能力的情况。

使用接收缓冲区接收数据

当接收方的 TCP 收到包后,会先将数据存放到接收缓冲区中。

然后,接收方需要计算 ACK 号,将数据块组装起来还原成原本的数据并传递给应用程序。

如果这些操作还没完成下一个包就到了也不用担心,因为下一个包也会被暂存在接收缓冲区中。

超出接收缓冲区接受能力会导致溢出

如果数据到达的速率比处理这些数据并传递给应用程序的速率还要快,那么接收缓冲区中的数据就会越堆越多,最后就会溢出。

缓冲区溢出之后,后面的数据就进不来了,因此接收方就收不到后面的包了,这就和中途出错的结果是一样的,也就意味着超出了接收方处理能力。

我们可以通过下面的方法来避免这种情况的发生。

通过窗口大小参数解决溢出

首先,接收方需要告诉发送方自己最多能接收多少数据。

然后发送方根据这个值对数据发送操作进行控制,这就是滑动窗口方式的基本思路。

窗口字段的原理

在这张图中,接收方将数据暂存到接收缓冲区中并执行接收操作,当接收操作完成后,接收缓冲区中的空间会被释放出来,也就可以接收更多的数据了。

这时接收方会通过 TCP 头部中的窗口字段将自己能接收的数据量告知发送方,这样一来,发送方就不会发送过多的数据,导致超出接收方的处理能力了。

接收方在收到数据之后马上就会开始进行处理,如果接收方的性能高,处理速度比包的到达速率还快,缓冲区马上就会被清空,并通过窗口字段告知发送方。

和序号、ACK 号一样,发送操作也是双向进行的。前面提到的能够接收的最大数据量称为窗口大小,它是 TCP 调优参数中非常有名的一个。

 

2.3.6 ACK 与窗口的合并

ACK号和更新窗口的时机

什么时候需要更新窗口大小

当收到的数据刚刚开始填入缓冲区时,没必要每次都向发送方更新窗口大小。

因为只要发送方在每次发送数据时减掉已发送的数据长度就可以自行计算出当前窗口的剩余长度。

因此更新窗口大小的时机应该是接收方从缓冲区中取出数据传递给应用程序的时候。

这个操作是接收方应用程序发出请求时才会进行的,而发送方不知道什么时候会进行这样的操作。

因此当接收方将数据传递给应用程序,导致接收缓冲区剩余容量增加时,就需要告知发送方,这就是更新窗口大小的时机。

什么时候需要更新ACK号

当接收方收到数据时,如果确认内容没有问题,就应该向发送方返回 ACK 号,因此我们可以认为收到数据之后马上就应该进行这一操作。

发送ACK号时可以先等待窗口更新再一起发送

如果将前面两个因素结合起来看,首先发送方的数据到达接收方,在接收操作完成之后就需要向发送方返回 ACK 号,而再经过一段时间,当数据传递给应用程序之后才需要更新窗口大小。

但如果根据这样的设计来实现,每收到一个包,就需要向发送方分别发送 ACK 号和窗口更新这两个单独的包。

这样一来,接收方发给发送方的包就太多了,导致网络效率下降。

因此,接收方在发送 ACK 号和窗口更新时,并不会马上把包发送出去,而是会等待一段时间。

在这个过程中很有可能会出现其他的通知操作,样就可以把两种通知合并在一个包里面发送了。

比如在等待发送ACK 号的时候正好需要更新窗口,这时就可以把 ACK 号和窗口更新放在一个包里发送,从而减少包的数量。

等待能减少连续发送多个ACK号的次数

当需要连续发送多个 ACK 号时,也可以减少包的数量,这是因为 ACK 号表示的是已收到的数据量。

也就是说,它是告诉发送方目前已接收的数据的最后位置在哪里,因此当需要连续发送 ACK 号时,只要发送最后一个 ACK 号就可以了,中间的可以全部省略。

等待也能减少连续多次窗口更新

当需要连续发送多个窗口更新时也可以减少包的数量,因为连续发生窗口更新说明应用程序连续请求了数据,接收缓冲区的剩余空间连续增加。

这种情况和 ACK 号一样,可以省略中间过程,只要发送最终的结果就可以了。

 

2.3.7 接收 HTTP 响应消息

发送 HTTP 请求消息后,接下来还需要等待 Web 服务器返回响应消息。

对于响应消息,浏览器需要进行接收操作,这一操作也需要协议栈的参与。

①浏览器在委托协议栈发送请求消息之后,会调用 read 程序来获取响应消息。

控制流程会通过 read 转移到协议栈,然后协议栈会执行接下来的操作。

和发送数据一样,接收数据也需要将数据暂存到接收缓冲区中。

②协议栈尝试从接收缓冲区中取出数据并传递给应用程序,但这个时候请求消息刚刚发送出去,响应消息可能还没返回。

响应消息的返回还需要等待一段时间,因此这时接收缓冲区中并没有数据,那么接收数据的操作也就无法继续。

这时,协议栈会将应用程序的委托,也就是从接收缓冲区中取出数据并传递给应用程序的工作暂时挂起,等服务器返回的响应消息到达之后再继续执行接收操作。

协议栈接收数据的具体操作过程已经在发送数据的部分讲解过了,因此这里我们就简单总结一下

③协议栈会检查收到的数据块和 TCP 头部的内容,判断是否有数据丢失,如果没有问题则返回 ACK 号。

④协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序。

具体来说,协议栈会将接收到的数据复制到应用程序指定的内存地址中,然后将控制流程交回应用程序。

将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新。

posted @ 2018-11-15 16:53  田错  阅读(302)  评论(0编辑  收藏  举报