1)校验和
数据段在发送之前会预先计算一个校验和,存在头部,对端接收之后会再一次计算段的校验和,如果和头部的不相等则丢弃。
2)确认应答机制
从三次握手那里也可以感受到,发送端在传输数据时会发送一个seq,接收端收到之后会回传一个ack=seq 1 给到发送端,如果发送端接收不到ack包说明包丢失了,这时候再依据一定规律重传即可。这一定程度上保证了TCP传输的可靠性,而不是我随便想发就发,不管你收到了没有。
3)重传机制
上面讲到了确认应答,那么如果没有收到应答应该在何时开始重传呢?这就涉及到重传机制,一种是超时重传,一种是快速重传。
首先是超时重传,先明确一个报文包往返的时间称为RTT,而重传的时间称为RTO。发送端在发送一个包之后就会开启一个计时器,计算直到收到确认包的时间,如果在一个RTT内收不到对端发送的确认包,那么就可以判定发送的包丢失了/因为网络原因延迟到达了,这时候发送端就会开启超时重传,重新发送一个报文包,并将计时器清零。由此可见,设置的RTO与实际的RTT不应该相差太大,否则可能造成无谓等待或者无谓重传,,因此RTO的取值应该略大于RTT。
考虑到超时重传需要RTO时间,这会在一定程度上影响到传输的效率,那么有没有更快速的方法去判断包丢失呢?这就是快速重传可以实现的,我们都知道传输的报文包的seq是顺序出去的,那么假设发送端发生了包a、包b、包c、包d,如果包b在中途丢了,在这个传输过程中对于接收端来说,他会先回复包a的确认,之后如果没有收到包b,他会重复发送包a的确认包,在发送端收到三个冗余的包a确认包之后,就清楚包a后面的包丢失了,进而执行重传。这个过程是以数据为驱动的,而不是以超时时间为驱动的。
4)流量控制--滑动窗口
TCP两端互传肯定不能是随心所欲,想发就发,需要有一个东西来控制他们的发送接收速率,这个东西就是滑动串口。在TCP中是维护两个绝对指针和一个相对指针来控制窗口,当前发送完但是还没确认的数据划分在发送窗口中,可用窗口中是维护即将发送的符合大小要求的数据。对于已发送的包收到确认之后就会从发送窗口中“移除”,其实就是指针右移了,可用窗口也相应增大。依据窗口来发送数据就不会造成频繁超发的情况。
本端的窗口大小是通过在与对端交换报文时,其中的win数值决定的,也就是接收端其实是可以间接控制发送端的速率的,也就是说流量控制实际上是基于滑动窗口的。
①窗口关闭:这里还需要注意的是当可用窗口大小为0时说明不能继续发送了,需要停下来等待对端回复确认。这时候接收端处理完毕之后会发送一个win为非零值的包给对端,而如果这个包丢失了,那么就会造成死锁的情况:发送端在等接收端确认,接收端在等发送端发送新的报文。为了解决这个问题,TCP的每个连接都会维护一个持续计时器,当收到对端的win=0的包之后就会开启计时,如果发送超时就会发送窗口探测报文,对方在确认这个报文的时候会带上win,以此打破死锁的局面。
②小窗口:现在考虑如果窗口腾出多少字节空间对端就发送多少字节,那么就可能造成很多几个字节的包被发送出去(需要注意报文头都40字节了),这样无疑会耗费掉传输资源,因此接收方的策略一般为 当窗口小于Min(MSS,缓存空间/2) 时就会向对端回win=0的包,以此来停止发送行为。发送方同样是类似的策略,达到要求才发送数据。
5)ARQ协议
自动重传请求(Automatic Repeat-reQuest)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。确认和超时其实就是上面2)、3)两点的内容,这不过这里聚合起来讲。
ARQ有两种分类协议,一种是停止等待ARQ,一种是连续ARQ。
其中停止等待就是在发送一组报文包之后并不马上发送下一组,而是等待对端确认之后再发送下一组,如果超时就重发,直到收到确认包。这种方式显而易见的可以保证可靠性,但是问题也很显然,如果对端确认得慢,那么传输速率也会随之被影响。
另一种连续ARQ指的是发送方维护一个窗口【滑动窗口】,发送端并不等待对方确认之后才发下一个报文组,在窗口中的报文可以发送出去不等待确认,对端采用的是累积确认,对按序到达的最后一个报文包发送ack,表明该报文seq号之前的包都被收到了。这种做法的好处是信道利用率较高,但是如果发送端发送5个包,对端收到了1、2、4、5,根据该定义,对端只会确认2号包,因此发送端会重发3、4、5号包【回退N】,但其实只丢了一个,但发送端并不清楚。
6)拥塞控制
参考资料:[小林coding图解网络]