tcp 保证可靠性机制
https://blog.csdn.net/xuzhangze/article/details/80490362
1、检验和
TCP检验和的计算与UDP一样,在计算时要加上12byte的伪首部,检验范围包括TCP首部及数据部分,但是UDP的检验和字段为可选的,而TCP中是必须有的。计算方法为:在发送方将整个报文段分为多个16位的段,然后将所有段进行反码相加,将结果存放在检验和字段中,接收方用相同的方法进行计算,如最终结果为检验字段所有位是全1则正确(UDP中为0是正确),否则存在错误。
2、序列号
TCP将每个字节的数据都进行了编号,这就是序列号。
序列号的作用:
a、保证可靠性(当接收到的数据总少了某个序号的数据时,能马上知道)
b、保证数据的按序到达
c、提高效率,可实现多次发送,一次确认
d、去除重复数据
数据传输过程中的确认应答处理、重发控制以及重复控制等功能都可以通过序列号来实现
3、确认应答机制(ACK)
TCP通过确认应答机制实现可靠的数据传输。在TCP的首部中有一个标志位——ACK,此标志位表示确认号是否有效。接收方对于按序到达的数据会进行确认,当标志位ACK=1时确认首部的确认字段有效。进行确认时,确认字段值表示这个值之前的数据都已经按序到达了。而发送方如果收到了已发送的数据的确认报文,则继续传输下一部分数据;而如果等待了一定时间还没有收到确认报文就会启动重传机制。
正常情况下的应答机制:
4、超时重传机制
当报文发出后在一定的时间内未收到接收方的确认,发送方就会进行重传(通常是在发出报文段后设定一个闹钟,到点了还没有收到应答则进行重传),其基本过程如下:
当然,未收到确认不一定就是发送的数据包丢了,还可能是确认的ACK丢了:
当接收方接收到重复的数据时就将其丢掉,重新发送ACK。而要识别出重复的数据,就要用到前面提到的序列号了,利用序列号很容易就可以做到去重的效果。
重传时间的确定:报文段发出到收到应答中间有一个报文段的往返时间RTT,显然超时重传时间RTO会略大于这个RTT,TCP会根据网络情况动态的计算RTT,即RTO是不断变化的。在Linux中,超时以500ms为单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。其规律为:如果重发一次仍得不到应答,就等待2*500ms后再进行重传,如果仍然得不到应答就等待4*500ms后重传,依次类推,以指数形式递增,重传次数累计到一定次数后,TCP认为网络或对端主机出现异常,就会强行关闭连接。
5、连接管理机制
连接管理机制即TCP建立连接时的三次握手和断开连接时的四次挥手。
首先三次握手:
建立过程为:
(1)B首先建立传输控制块TCB,进入LISTEN(收听)状态,等待用户的连接请求。如有,则建立连接。(这个过程在套接字编程中为服务器端调用socket函数、bind函数和listen函数的过程)
(2)A建立传输控制块TCB,然后向B发送连接请求报文段,报文段中首部的同步位SYN=1,同时选择一个序列号seq=x,TCP规定SYN报文段不携带数据,但要消耗一个序列号。然后A进入SYN-SENT(同步已发送)状态。(这个过程在套接字编程中为客户端调用socket函数和connect函数的过程)
(3)B收到请求后,如同意建立连接,就向A发送确认报文段。此时SYN=1、ACK=1,确认号ack=x+1,同时选择一个序列号seq=y,这个报文也不携带数据,但要消耗一个序列号。然后B进入SYN-RCVD状态(同步收到)。
(4)A收到B的确认后,还要向B发送确认。确认报文段的ACK=1,确认号ack=y+1,seq=x+1。TCP规定,ACK报文段可以携带数据,而如果不携带数据则不消耗序列号,此时下一个报文段的序列号仍为seq=x+1。这时,连接就建立成功了,A进入ESTABLISHED状态(已建立连接状态)。
(5)当B收到A的确认后,也进入ESTABLISHED状态,此时就可以进行数据传输了。
当然,在进行三次握手时不是仅进行连接,可能还会进行一些后续操作所需要的信息交流。
四次挥手:
在连接释放时,连接的两方都要同意才能够释放成功(就像情侣分手一样,分手时两个人的事儿)。连接的双方都可以提出释放连接,这里假设A先提出释放连接,首先双方都处于ESTABLISHED状态。
(1)当A的数据传送完后,就可以向其TCP发起连接释放了,此后停止再发送数据,主动关闭TCP连接。首先A向B发送一个FIN报文段,报文段首部FIN=1,序列号seq=u(u为最后传送的数据的序列号加1),然后A进入FIN-WAIT-1(终止等待1)状态。FIN报文段不能携带数据,但要消耗一个序列号。
(2)B收到释放连接的报文段后即发出确认报文段,报文首部ACK=1,ack=u+1,seq=v(v等于B前面传送过的数据的序列号加1),然后B进入CLOSE-WAIT(关闭等待)状态。这时从A到B这个方向的连接就释放了,TCP连接就处于半关闭状态。(注意:此后A不能主动向B发送数据,但是A可以给B发送确认报文段,也就是说A仍要接收来自B的报文,因为从B到A这个方向的连接还没有关闭)
(3)当A收到B的确认报文后,就进入FIN-WAIT-2(终止等待2)状态,等待B发出的连接释放报文段。
(4)当B的数据发送完毕后,其应用进程就通知TCP释放连接。B向A发送FIN报文,报文段首部FIN=1,ack=u+1(重复发送上一次已经发送过的确认号),seq=w(w为B最后发送报文段的序列号加1)。然后B进入LAST-ACK(最后确认)状态,等待A的确认。
(5)A在接收到B的连接释放报文后,必须进行确认。A向B发送的确认报文段中报文首部ACK=1,ack=w+1,seq=u+1。然后A进入TIME-WAIT(时间等待)状态(如果无差错,此状态时间为2MSL),注意,此时TCP连接还没有释放掉,必须经过TIME-WAIT设置的时间2MSL后,A撤销相应的传输控制块TCB,才进入CLOSED状态,结束了此次TCP连接。MSL叫做最长报文段寿命,RFC793建议设为2分钟,但在现在实际网络情况中,常用值有三种:30秒,1分钟,2分钟。必须要在A进入CLOSED状态后才能开始建立下一个新的连接。
(6)B收到A的确认报文后,也进入CLOSED状态,撤销相应的传输控制块TCB,此时,TCP连接全部断开。
这样TCP四次挥手完成。
6、流量控制
接收端处理数据的速度是有限的,如果发送方发送数据的速度过快,导致接收端的缓冲区满,而发送方继续发送,就会造成丢包,继而引起丢包重传等一系列连锁反应。
因此TCP支持根据接收端的处理能力,来决定发送端的发送速度,这个机制叫做流量控制。
在TCP报文段首部中有一个16位窗口长度,当接收端接收到发送方的数据后,在应答报文ACK中就将自身缓冲区的剩余大小,放入16窗口大小中。这个大小随数据传输情况而变,窗口越大,网络吞吐量越高,而一旦接收方发现自身的缓冲区快满了,就将窗口设置为更小的值通知发送方。如果缓冲区满,就将窗口置为0,发送方收到后就不再发送数据,但是需要定期发送一个窗口探测数据段,使接收端把窗口大小告诉发送端。 其过程如下:
注意:窗口大小不受16位窗口大小限制,在TCP首部40字节选项中还包含一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。
7、拥塞控制
流量控制解决了 两台主机之间因传送速率而可能引起的丢包问题,在一方面保证了TCP数据传送的可靠性。然而如果网络非常拥堵,此时再发送数据就会加重网络负担,那么发送的数据段很可能超过了最大生存时间也没有到达接收方,就会产生丢包问题。
为此TCP引入慢启动机制,先发出少量数据,就像探路一样,先摸清当前的网络拥堵状态后,再决定按照多大的速度传送数据。
此处引入一个拥塞窗口:
发送开始时定义拥塞窗口大小为1;每次收到一个ACK应答,拥塞窗口加1;而在每次发送数据时,发送窗口取拥塞窗口与接送段接收窗口最小者。
慢启动:在启动初期以指数增长方式增长;设置一个慢启动的阈值,当以指数增长达到阈值时就停止指数增长,按照线性增长方式增加;线性增长达到网络拥塞时立即“乘法减小”,拥塞窗口置回1,进行新一轮的“慢启动”,同时新一轮的阈值变为原来的一半。
“慢启动”机制可用图表示:
文中可能有不少错误,望各位高手指正。