TCP三次握手和四次挥手不论是在开发仍是面试中都是一个很是重要的知识点,它是咱们优化web程序性能的基础。可是大部分教材都对这部分解释的比较抽象,本文咱们就利用wireshark来抓包以真正体会整个流程的细节。html
根据下面这幅图咱们来看一下TCP三次握手。p.s: 每一个箭头表明一次握手。web
client发送一个SYN(J)包给server,而后等待server的ACK回复,进入SYN-SENT
状态。p.s: SYN为synchronize的缩写,ACK为acknowledgment的缩写。面试
server接收到SYN(seq=J)包后就返回一个ACK(J+1)包以及一个本身的**SYN(K)**包,而后等待client的ACK回复,server进入SYN-RECIVED
状态。算法
client接收到server发回的ACK(J+1)包后,进入ESTABLISHED
状态。而后根据server发来的SYN(K)包,返回给等待中的server一个ACK(K+1)包。等待中的server收到ACK回复,也把本身的状态设置为ESTABLISHED
。到此TCP三次握手完成,client与server能够正常进行通讯了。shell
咱们来看一下为何须要进行三次握手,两次握手难道不行么?这里咱们用一个生活中的具体例子来解释就很好理解了。咱们能够将三次握手中的客户端和服务器之间的握手过程比喻成A和B通讯的过程:缓存
上面分析还不够形象,很容易忘记,下面咱们利用wireshark来证实一下上面的分析过程。从下面的的输出就能够很容易看出来,必需要通过前面的三次tcp请求才会有起一次http请求。服务器
第一次请求客户端发送一个SYN包,序列号是0。tcp
第二次请求服务器会发送一个SYN和一个ACK包,序列号是0,ack号是1。ide
第三次本地客户端请求会发送一个ACK包,序列号是1,ack号是1来回复服务器。性能
如下面这张图为例,咱们来分析一下TCP四次挥手的过程。
client发送一个FIN(M)包,此时client进入FIN-WAIT-1
状态,这代表client已经没有数据要发送了。
server收到了client发来的FIN(M)包后,向client发回一个ACK(M+1)包,此时server进入CLOSE-WAIT
状态,client进入FIN-WAIT-2
状态。
server向client发送FIN(N)包,请求关闭链接,同时server进入LAST-ACK
状态。
client收到server发送的FIN(N)包,进入TIME-WAIT
状态。向server发送**ACK(N+1)**包,server收到client的ACK(N+1)包之后,进入CLOSE
状态;client等待一段时间尚未获得回复后判断server已正式关闭,进入CLOSE
状态。
通常在提到TCP三次握手的时候,一样会涉及到TCP滑窗,下面咱们补充一下什么是TCP滑窗。若是采用PAR的形式来传递的话,每一次发送方发送完包后必须获得接收方的确认回复,改进一下这个流程,发送端一次能够发送多个包,没必要每次等到接收方的ack回复,同时接收端也要告诉发送端本身能接收多少。固然还须要保证顺序性,对于乱序的情况,咱们能够容许等待必定时间的乱序,好比先缓存提早到达的数据,而后等待须要的数据,若是必定时间没有达到就drop掉。
TCP滑动窗口能够解决咱们上面提到的概念,滑动窗口中的数据主要分为下面几类:
对于接收端也是有一个接收窗口,相似发送端,接收端的数据有三个分类(注意接收端并不要等待ACK):
下面这部份内容参考自coolshell,我认为写的比较好,因此转过来分享一下,感兴趣的朋友能够阅读一下 :)
TCP要保证全部的数据包均可以到达,因此,必须要有重传机制。
注意,接收端给发送端的Ack确认只会确认最后一个连续的包,好比,发送端发了1,2,3,4,5一共五份数据,接收端收到了1,2,因而回ack 3,而后收到了4(注意此时3没收到),此时的TCP会怎么办?咱们要知道,由于正如前面所说的,SeqNum和Ack是以字节数为单位,因此ack的时候,不能跳着确认,只能确认最大的连续收到的包,否则,发送端就觉得以前的都收到了。
一种是不回ack,死等3,当发送方发现收不到3的ack超时后,会重传3。一旦接收方收到3后,会ack 回 4——意味着3和4都收到了。
可是,这种方式会有比较严重的问题,那就是由于要死等3,因此会致使4和5即使已经收到了,而发送方也彻底不知道发生了什么事,由于没有收到Ack,因此,发送方可能会悲观地认为也丢了,因此有可能也会致使4和5的重传。
对此有两种选择:
这两种方式有好也有很差。第一种会节省带宽,可是慢,第二种会快一点,可是会浪费带宽,也可能会有无用功。但整体来讲都很差。由于都在等timeout,timeout可能会很长。
因而,TCP引入了一种叫Fast Retransmit 的算法,不以时间驱动,而以数据驱动重传。也就是说,若是,包没有连续到达,就ack最后那个可能被丢了的包,若是发送方连续收到3次相同的ack,就重传。Fast Retransmit的好处是不用等timeout了再重传。
好比:若是发送方发出了1,2,3,4,5份数据,第一份先到送了,因而就ack回2,结果2由于某些缘由没收到,3到达了,因而仍是ack回2,后面的4和5都到了,可是仍是ack回2,由于2仍是没有收到,因而发送端收到了三个ack=2的确认,知道了2尚未到,因而就立刻重转2。而后,接收端收到了2,此时由于3,4,5都收到了,因而ack回6。
Fast Retransmit只解决了一个问题,就是timeout的问题,它依然面临一个艰难的选择,就是,是重传以前的一个仍是重传全部的问题。对于上面的示例来讲,是重传#2呢仍是重传#2,#3,#4,#5呢?由于发送端并不清楚这连续的3个ack(2)是谁传回来的?也许发送端发了20份数据,是#6,#10,#20传来的呢。这样,发送端颇有可能要重传从2到20的这堆数据(这就是某些TCP的实际的实现)。可见,这是一把双刃剑。