只是自说自话的学习笔记,各路看官绕路~
TCP协议:
1、tcp协议被定义为可靠的协议,但是它是属于传输层的协议,根据七层协议的定义,传输层的数据会先传网络层(ip层),而ip层是尽最大努力做到交付,这里可以理解成ip层也是不可靠的协议,那么在ip层之上的tcp协议如何做到可靠交付呢?这里要提到几个处理方式:
停止等待处理方式
停止等待处理方式可以用一下的例子来举例:a通过tcp协议将数据传送给b,而在tcp协议定义里边,这些数据是需要被分组发送的,当然了这个分组情况是tcp协议自身决定的,而a每次将数据中的一小组发送给b的时候都需要b发送数据给a确认已经成功接收到数据,这样a才会继续将数据发送给b,那么网络有时候会出现异常导致b无法成功接受到数据或者数据出现错误的情况a和b怎么处理呢?b在这里是如果接受到错误信息就直接丢弃然后不返回任何数据通知a,如果b无法接收到数据就不做处理,a在这里的处理方式是给每次发送数据设置一个计时器,如果超过这个时间了b还没有发送确认消息,a就默认这次发送失败,然后再次发送数据给b,这里也被称之为“超时重传”和"自动重传请求机制",如果收到了b的确认信息则自动将计时器关闭。所以这里这个计时器的时间的设定就显得非常重要了,因为这个时间要比正常成功传输的时间稍微长一些,如果短了导致多次重传,浪费网络资源,而长了就会导致效率边长,而且这个正常成功传输的时间也不好确定,因为和经过哪些网络有关系。那么问题来了,b在接收到a重传之后如何处理的呢?首先b是先判断是否已经有了这个数据,如果有了就则直接丢弃这个数据,如果没有则继续向会话层和应用层交付数据,而无论如何,都要再次发送数据和a进行确认,因为之所以会导致这个情况就是因为a没有收到确认信息导致的。
通过这种方式,tcp协议就能在ip层不可靠的传输之上实现可靠的传输了。
不过这里如果是每次要等到数据1发送成功后才继续发送数据2,数据1和数据2都在同一个分组里边,就显得效率太低了,那么如何做到高效率呢?这里采用了另一种机制,那就是流水线传输,就是说发送方可以并行发送多个数据,而不是说串行的发送,这样以来效率方面也就提高了。利用tcp协议如何实现流量控制?这里所谓的流量控制指的是让发送方的发送速率不要那么快,让接收方及时处理,采用的是滑动窗口的处理方式;以a和b发送例子为例,a和b在建立连接的时候(三次握手的时候),b会告诉a它的缓存队列中还可以存储多少个字节(rwnd),而a再根据b告诉它的rwnd的值来传输数据,技术细节:b每次告诉a的时候首部ACK的值为1,小写ack的值是确认的序号,而这里a传输数据的时候会带上seq,即传输数据的开始部分,如果b告诉它的rwnd是100,则此刻a传输的数据是(1~100)。而等b告诉a的rwnd为0的时候证明b这边不允许发送方再发送数据了。而如果b又可以接收数据了,就会再次发送一个rwnd的值告诉a,那么问题来了,如果传输的时候b的网络不好,导致rwnd的值丢失了怎么办?解决方案是:a在接收到b的零窗口的时候(rwnd=0),a会激发一个计时器,每隔一段制定的时间发送一个零窗口探测器,而b在接收到这个零窗口探测信息的时候就会给出目前的窗口值,从而解除了互相等待的状态。
tip: 这里保持一个问题,刚刚提到的滑动窗口的传输方式其实是针对字节实现的,那么问题来了?tcp传输是数据报的形式实现的,那么为何此处是根据字节实现的呢?
- 更详细的三次握手流程:这里以a发送方和b接收方为例,b作为接收方会先开启监听状态,监听是否有发送方做连接请求,这里称之为服务器,而a作为发送方这里称之为客户端,第一次握手:a的tcp协议首部中设置SYN=1,seq=x之后将数据报发送给b,此处SYN=1的时候规定不准携带数据,但是序号会自增。完成这一步操作后a的tcp状态会变为SYN_SEND状态。第二次握手:b在接收到a的数据的时候会根据SYN=1判断是有客户端连接,b在tcp数据包的首部中会将ACK=1,ack=x+1,并且再生成seq=y,将数据报发送给a做确认,此刻b的状态会变为SYN_RCVD状态。第三次握手:a在接收到ACK=1的数据后通过ack=x+1明白是b的确认,a就会将ACK=1,ack=y+1,sql=x+1,再次发送给b,之后进入ESTABLISHED状态,在这一次的操作中允许携带数据,如果不携带则序号不会增加。
tip: 这里将会产生一个问题,如果去掉第三次连接会怎么样呢?这里给出一个例子来解答这个问题,结合以上的知识点可以知道,如果在a第一次握手的时候出现了网络滞留或者丢失问题,a的协议机制有个计时器,超时了会再次发送请求,这也a就发送了两次请求连接的操作,而由于网络滞留问题,其中有一次操作在该连接结束后b才收到,那么如果是两次握手就结束的话,那么b此刻就会进入established状态,开始等待a的数据传输,可是a并没有打算将数据传输给b,这也b就会造成等待,导致b的资源浪费。
- 结合以上知识点来说说看更详细的四次挥手的流程:这里同样以a和b进行tcp连接为例,a在要断开和b的tcp连接的时候,a会先将头部的FIN信号设置为1,序号seq=u,即最后一次传送的序号+1,然后发送数据包,之后进入FIN-WAIT-1状态,等待b的确认。这也是第一次握手的过程。b在收到a的数据包之后,会根据FIN为1判断是有客户端要进行挥手连接,b会将ACK信号设置为1,ack=u+1,然后发送数据包,之后就会进入CLOSE_WAIT状态,此刻A到B的这个连接就已经断开了,即a没有数据要发送了,这时TCP连接就进入了半关闭状态,因为tcp是双全工连接,a到b的连接断开了,可是b到a的连接还在,而a在接收到b的数据包之后便进入了FIN-WAIT-2的状态,等待b释放连接,此刻便完成了二次挥手,第三次挥手是b会发送一个FIN数据包,会再次发送ack为u+1的数据包,此刻b便进入LAST_ACK状态,第三次挥手结束,而第四次挥手是a在接收到b的数据包之后,会根据FIN和ack=u+1判断是第三次挥手,之后a会发送一个ACK数据包,然后进入TIME_WAIT 状态,这里有个地方要注意的,那就是此时TCP连接尚未关闭,而是要等一个计时器的时间,之后才进入CLOSE状态,而b在接受到数据报纸后也会进入CLOSE状态。
tip: 这里将会产生几个问题:
- 问题一?为什么在第四次挥手a要等待一个计时器的时间才进入CLOSE状态?主要原因有:如果a的网络不好,在第四次挥手的时候发送的数据包被丢弃掉了,这样b就会由于没有收到确认信息而重发数据包,如果a即可就进入CLOSE状态了那么肯定无法在此刻接收到b的数据,而让a等待一个计时器的时间就可以解决这个问题了。
- 问题二?在a释放tcp连接处于第二次挥手的时候a关机了,那么b会处于什么状态?并且b怎么处理这种情况?如果a关机了,那么b会处于LAST-ACK状态,之后会在发送数据给a的时候无法接受到a的确认数据包,为了不让b无限等待下去,b会启动一个保活计时器机制,b会在通常两个小时后还没有接收到数据包的话发送一个探测报文段,以后会间隔75分钟发送一次,如果10次之后还没有响应,则断开连接。
- 问题三?服务器tcp连接存在大量close_wait状态,为什么?这里有个地方上面没有提到,以为close_wait状态迁移到last_ack状态是自发的,其实不是,是要socket.close()才会过度到last_ack,那么大量连接处于close_wait状态证明没有及时close掉socket,可能是io阻塞问题。
2、tcp协议中使用到的比较高效率的算法:
- Nagle算法:Nagle算法的目的主要是用来解决a和b tcp协议传输的数据传送的过程,过程是:应用层的应用将要发送的数据逐个字节传输给传输层的tcp的发送缓存的时候,tcp先把第一个字节发送给接收方,把之后数据继续缓存起来,等到接收方回应之后,传送方再把缓存的数据发送出去,然后等到接收方回应,再继续传送缓存的数据给发送方,这里有点串行的方式。Nagle还规定:当送达的数据已经达到发送窗口大小的一半时就立即发送数据包。
那么问题来了,将滑动窗口机制和这个结合,可以知道发送方发送数据是需要接收方告知发送的rwnd是多少的,而Nagle算法这里,先将第一个字节发送给接收方,而如果此时接收方的缓存队列中接收到这个字节后就满了,而交互式的应用进程这里是一个个从缓存中读取字节的,如果读取完接收方就发送确认并告知rwnd为1,而发送方收到确认后再次发送一个字节过来,接收方再次一个字节的处理,长此以往,效率肯定是很低的,那么如何解决呢?tcp协议是这样处理的,接收方在rwnd少的时候会先积累下来,等到多了再去通知发送方,而发送方在数据包少的时候也会积累下来,等到足够量再发送。
下面记录的算法是在拥塞控制的时候使用到的算法:
- 慢开始算法:发送方会先维持一个拥塞窗口, 拥塞窗口的大小取决于网络的情况,只要网络没有出现问题,那么拥塞窗口就会变大,慢开始算法是成倍变大,那么tcp协议如何识别网络出现问题呢?从上面应该可以看到,发送方在发送出数据包的时候,如果没有收到确认数据包,那么便会当做网络出现问题处理。如果收到了确认数据包,则拥塞窗口就会变大,之所以会成倍变大,这里的意思是 如果拥塞窗口是4,则发送方会发送4个数据包出去,都收到应答之后,拥塞窗口变回成倍变大,拥塞窗口默认值为1,并且会设置一个ssthresh慢开始门限,那么如果遇见了网络阻塞,发出去的数据包没有应答的话怎么处理?如果遇见了ssthresh会减半处理,并且拥塞窗口的值会降为1,而如果一直拥塞窗口的值一直增加到超过ssthresh之后便会采取拥塞算法,在这个阶段和慢开始阶段不同的地方在于,发送方发送的数据包串行化的逐个发送的,也即是说拥塞窗口是逐一增加的。
- 快重传算法:发送方在发送了数据包之后会设置一个重发计时器,而快重传的作用就在此处使用。当接收方接收了数据包m1、m2,之后没有接收到m3,而是接收到了m4,快重传算法要求接收方立即发送对m2的重复确认,没错,这里就是m2的重复确认,可以看出来之前已经确认过了一次了,这样可以让发送方及早知道接收方没有接收到m3的数据包,发送方还会继续发送m5和m6,接收方接收到m5和m6后还要继续发送对m2的重复确认,当三次以后接收方会立即发送m3的数据包,而不用等待重发计时器的时间到来。
- 快恢复算法:与快重传算法搭配使用的一个算法,当发生了快重传现象的时候tcp会认为网络可能没有阻塞,不然不可能连续有三个数据到达接收方,所以会将ssthresh设置为一半,而拥塞窗口设置为1,可是这里并不会进行慢开始算法,而是采用拥塞算法,缓慢增加。