首先明确TCP的几个特点
即TCP的建立连接和关闭连接,也就是所谓的三次握手和四次挥手
可以看到所谓的三次握手的过程中,不管是服务端还是客户端都经历了一个消息的发送与接受的过程,即一来一回
自古多情伤离别,断开连接相比建立连接要复杂的多,因为要处理各种状态,各种收尾工作。
其实四次挥手实质上还是客户端和服务端的一来一回
前边建立连接时我们说过了会确定消息序号,有序性正是跟这些序号有关,按照序号一一发送,接收端再对每个包一一应答。但是考虑到网络传输中出现的各种问题就不是这么一句话说的清了。
发送端和接收端都有对应的缓存记录发送和接受的包
先看发送端,
再看接收端
可以看到8,9 已经收到而6,7还没收到,出现了乱序,可能6,7是发生了丢包。此时为了保证一致性,8,9就只能先缓存着不能发送ACK.
进一步的,如果4的ACK到了而5的ACK丢包了,此时的情况变为5的ACK包丢了,6,7的数据包丢了,此时该怎么办?
发送端会采用超时重试的方法。针对每一个包都会有一个定时器,如果定时期限内没有收到ACK就会重试。定时器的时长是TCP采样每次往返时间的,随着网络波动而自适应变化
OK,假设现在已经超时开始重传5,6,7,接收方就发现 5 原来接受过,于是抛弃5.假设接收方收到6,开始发送6的ACK,若是7再次丢包且再次超时,此时的重传策略需要注意
超时间隔加倍,即每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
**快速重传机制,即当接收方收到一个序号大于下一个所期望的报文段时,就检测到了数据流中的一个间格,于是发送三个冗余的ACK,客户端收到后,就在定时器过期之前,重传丢失的报文段。**比如当前接收方收到6,8,9,按理说6之后是7,结果收到8,说明7的丢包了,此时就会给发送三个6的ACK。客户端收到3个,就会发现7的确又丢了,不等超时,马上重发。
快速重传避免了超时间隔加倍带来的周期过长的问题。
还有一个累计确认机制,即如果发送端收到了编号为9的ACK,哪怕它还没收到编号为7的ACK,也认为7已经被接收端接收到了,因为接收端是挨个儿发送ACK,能发送9的ACK必然证明7的ACK已经发送,只是可能ACK发生了丢包而已。
滑动窗口rwnd是怕发送方把接收方缓存塞满,而拥塞窗口cwnd是怕把网络塞满。
流量控制:
接收端处理数据包,发送端窗口不断右移,这是一个正常的过程。若是接收端一直耗着不处理数据,发送端窗口就会不断变小甚至变为0。
拥塞控制: 网络杜塞可能会造成包丢失和超时问题。一旦出现了这些现象就说明,发送速度太快了,要慢一点。
一条TCP连接开始,cwnd设置为一个报文段,一次只能发送一个;当收到这一个确认的时候,cwnd加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认cwnd加一,两个确认cwnd加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认cwnd加一,四个确认cwnd加四,于是一次能够发送八个。可以看出这是指数性的增长。
增长到一个值sshresh=65535字节时候,每收到一个确认后,cwnd增加1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加1/8,八个确认一共cwnd增加1,于是一次能够发送九个,变成了线性增长。
一直增长下去会出现问题,一旦出现阻塞或者丢包,需要重传。这个时候,将sshresh设为cwnd/2,将cwnd设为1,重新开始慢启动。这真是一旦超时重传,马上回到解放前。但是这种方式太激进了,将一个高速的传输速度一下子停了下来,会造成网络卡顿。
对应前边说过的快速重传算法,当接收端发现丢了一个中间包的时候,发送三次前一个包的ACK,于是发送端就会快速的重传,不必等待超时再重传。cwnd减半为cwnd/2,然后sshthresh = cwnd,当三个包返回的时候,cwnd = sshthresh + 3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长。
(想自学习编程的小伙伴请搜索圈T社区,更多行业相关资讯更有行业相关免费视频教程。完全免费哦!)