众所周知,网络层的ip协议是非可靠的(即不保证对方能够收到正确的数据),信息传递的可靠性由传输层中的TCP来保证。
要保证信息传递的可靠性,需要解决的问题有:① 数据在传输过程中失真,② 丢包,③ 乱序,④ 重复包。
即解决了上述四个问题也就保证了可靠性。
1> 添加校验和字段
发送方在发送时对数据报文进行算法计算校验和,并且一起发送给接收端,接收端接收到数据时,也会重新计算一遍数据的校验和,如果不一致表示报文在传输过程中出错了,需要重传。
2> 添加序号
tcp连接中,为传输的数据的每一个字节按顺序编号。在传输中,以字节为基础,TCP把报文分段,给每个报文段会指派一个序号,每个报文段的序号就是在这个报文段中第一个字节数据的序号。
添加序号解决了:
① 乱序问题,接收端接收到数据后,首先是保存在缓冲区中的,接收端会自行排序;
② 丢包问题,如果接收端发现有中间数据未接收到,会请求服务端重新发送该数据报文;
③ 重复包问题,如果接收端发现有重复包会丢弃;
3> 确认应答(添加ACK --- acknowledge)
为保证数据有被接收端接收,TCP为每个响应报文添加了ACK标记以及ack数值来通知发送端我已经接收到的数据有哪些,ack数值表示希望接收到的下一个报文开始序号。
在连接丢包的情况下,又衍生了一堆问题:重发机制,流量控制,拥塞控制等。
4> 重发控制
当一个报文数据段(例如序号为 2001 ~ 3000)丢失后,接下来发送端每发送一次其他数据报文段,都会收到ack = 2001(表示接收端希望接收2001序号为开始的报文段)的确认应答,在连续接收到三次2001的确认应答时,发送端会进行此报文段的重发(这只是一种重发机制 --- 高速重发控制,其他机制可自行百度)。
5> 流量控制
在传输过程中,报文段并非立即转到内存中,而是先保存到缓冲区中,在接收端中,假如处理速度太慢会导致缓冲区数据堆积,这时发送端继续发送会导致丢包问题。为解决缓冲区满的问题,避免丢包,在tcp首部中,会有专门的字段通知发送端我的接收窗口大小(即我的缓冲区现在还有多少剩余位置),当此字段数值为0时,表示缓冲区满,发送端此时不应该继续发送数据。
当窗口大小等于0时,发送端理应不继续发送数据,等待接收端发给自己的窗口大小更新的消息,获得此消息后才允许继续发送数据,但是所有的报文段都有可能丢失,假如通知窗口大小更新的消息丢失了,那么发送端将一直以为接收端缓冲区满,导致不再发送数据报文段,为了避免此情况,发送端在接收到窗口大小为0的情况下,会定时发送一个消息(窗口探测消息)来获取最新的窗口大小。
6> 拥塞控制
在网络出现拥堵时,如果突然发送一个较大量的数据时,极大可能会导致整个网络的瘫痪,TCP为了防止此现象的发生,在通信一开始时会通过一个叫慢启动的算法对发送数据量进行控制。
在慢启动开始时,会将拥塞窗口的大小设置为1MSS,在接下来的通信中,每个包的往返会对窗口大小进行加1的增加,这样拥塞窗口会以 1,2,4,8 指数函数的趋势增长,在拥塞状态下激增亦会导致网络拥塞的发生,为了防止这种情况,引入了慢启动阈值的概念,当达到这个阈值后,每收到一个应答确认时,只允许下面公式进行拥塞窗口的比例放大:(1个数据段的字节数)/拥塞窗口字节 * 1个数据段字节数。因此达到阈值后拥塞窗口的大小会直线的趋势进行扩大。
在慢启动开始时,并没有设置慢启动阈值,而是在触发超时重发机制时,慢启动阈值的大小会被设置成当前拥塞窗口一半的大小。就会出现下图的趋势: