TCP三次握手、四次挥手,以及学习方法

TCP三次握手、四次挥手,以及学习方法

三次握手

面试中有一个常见问题

tcp建立连接为什么需要三次握手,两次握手为什么不行?

如果对tcp的状态转换非常清楚的话,这个问题就很好回答了

image-20210107095113689

这张图记录了tcp的所有状态,而三次握手涉及到的只有图中的上半部分。

如果只有两次握手,情况是这样:

  1. client 发送 syn
  2. server 发送 ack + syn
  3. client 收到 server的ack和syn后,进入established 状态,此时,如果没有数据需要发送,会一直什么也不做,等待数据的到来
  4. 而server这边发送了ack和syn后,没有回应,也就无法确认网络是否通畅,但它什么也做不了,因为即使重发ack和syn,根据两次握手的规定,它根本得不到任何回应。所以,即使此时server这边来了数据,也只能等待。
  5. 如果client这边发送了数据,server这边才能收到ack,从而进入established状态,正常收发数据。

虽然一般来说server很少建立连接后直接发送数据,但tcp协议还是提供了这个功能,怎样解决呢?

一种很直接的方案就是:让server回复ack+syn后直接进入established状态,不过这样就有两个问题:首先这是一种冒险,因为你还不能确认到对方的网络是否通畅,就直接发数据了,有可能会做无用功;其次,就引出这个问题网络上了一个常见的回答:

防止已失效的连接请求又传送到服务器端,因而产生错误

这个回答当然是正确的,但我第一次看到这句话并没有想通,后面对tcp的状态转换更加熟悉后才明白,下面来仔细分析一下:

这句话表述的很明确,说的是一种特殊情况,即,client第一次发送syn1,因为未知原因很慢没被接收,第二次由于超时重传发了syn2,被正常接收,此时,第一次的syn1又正常发送到了server。

在这种情况下,server由于一直处于listen状态,会建立一个新的socket,并回复ack+syn,这个syn是server生成的一个isn,而client会直接忽略这个包,server会一直处在established 状态,造成资源的浪费。

而如果是三次握手,server新建立的错误连接会不断重传ack+syn,但client一直没有回复,然后server多次重传后,就会关闭这个连接,这样就避免了的资源的浪费。

四次挥手

相比于tcp连接的建立,关闭可能会更加复杂一点。

tcp作为一个全双工通信方式,理想的状态是满足一下两个要求再关闭:

  1. 我方的字节流输入完毕,向对方发送了fin,并收到了对方的ack
  2. 我方确信对方已经收到了我方的ack

但是在一个数据有概率丢包的状态下,想要双方都完美达成以上两个要求是不可能的(两个将军问题),不过tcp已经大概率可以做到了。

首先在tcp中有一方是可以满足上面那两个条件的,哪一方呢?

后发送fin的一方,因为后发送的一方中,其fin数据段中也携带了ackno,如果收到了这个fin对应的ack,那么表示对方对这个ackno没有质疑,而这个ackno正是对之前的fin的ack,所以可以确定对方一定收到了我之前对fin的ack。

所以,后发送fin的一方只需要收到ack之后就直接关闭了,而先发送fin的一方,就要多等一会,因为虽然收到了对方的fin,然后回了一个ack,但是还无法保证对方是否收到了这个ack,多等一会,如果对方重传了fin,那就再回一个ack,然后再等一会。这个等一会要等多久呢?

答案是2 * MSL,MSL全称是maximum segment lifetime,最长分节生命期。MSL是任何IP数据报能够在因特网存活的最长时间。为什么是2倍的MSL呢?因为假设我们回复的ack丢了,对方会先等一个RTT的时间,这个时间是小于MSL的,然后重传一个fin,路上在拥堵,也就一个MSL的时间,不然就丢了,所以2 * MSL一定是充足的。

等待2MSL后,tcp连接就算是正式关闭了。

我是怎样学习tcp的

learning by coding!

一开始我也是看书看博客,看别人写三次握手,但是看了感觉会了,过两天就忘了,而且感觉即使记住了,也是死记硬背的,没有理解,然后我就下定决心,好好学习一下tcp的状态图,而学一个东西最好的方式有两步

  1. 会用它
  2. 会造它

用tcp协议当然很简单,socket等一系列系统调用封装的很好,甚至可以说太好了以至于我们完全感知不到TCP协议做了什么。因此,自己写一个tcp协议当然就是最好的学习方式了,不过我们初学者,全靠自己当然写不出一个完整能用的tcp协议了,所以我就上网搜了一下相关的课程实验,然后就找到了大名鼎鼎的 CS144 ,斯坦福出品,实验的说明和测试用例都很良心,每当你解决一个bug,就刷新了一下你对tcp协议的认知。当你靠自己通过了所有测试用例,我想你已经对tcp的细节都已经非常了解了,再回首看一下tcp协议,顿时有了一种掌控感,当然,这个lab并没有覆盖tcp的全部特性,也不要觉得做完了就会对tcp无所不知,但是最复杂最核心的部分已经明白,剩下的也就不是难题了。

posted @ 2021-01-07 18:33  十月sy  阅读(52)  评论(0)    收藏  举报