计算机网络基础:TCP协议之三次握手与四次挥手

上一篇里,我们了解了传输层的TCP协议以及TCP协议实现的机制,接下来我们就来详解一下TCP协议的连接管理机制:

连接管理

         TCP提供面向有连接的数据传输,面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。

         也就是说TCP协议需要在客户端与服务器成功连接之后才可使用,那么客户端与服务器的连接过程是什么呢?

  • TCP建立连接:三次挥手 (客户端连接服务器需要完成三次握手
  • TCP断开连接:四次挥手 (通信完毕过后断开连接需要四次挥手

         三次握手与网络通信之间的关系:

           

面试题:

        TCP三次握手的过程,accept发生在三次握手哪个阶段?

  • 发生在三次握手后。

       

三次握手

  • 客户端与服务器在握手之前都做了一些前期的准备。服务器在开始先分配一个描述符,然后填充一下sockaddr_in结构体,绑定创建的文件描述符及服务器端口,接着listen监听,使得刚才的文件描述符成为一个监听描述符,最后阻塞至accept等待客户端的连接。而客户端相较来说简单一些,就是分配文件描述符,填充sockaddr_in结构体,最后进行connect请求服务器的连接,直至服务器响应。
  • 在客户端经过connect请求服务器响应的时候,向服务器发送同步报文段也就是SYN请求,发送完毕后等待服务器响应。服务器如果收到了SYN同步报文段,那么就会给客户端发送ACK响应,意为收到了客户端发送的同步报文段,在此同时,服务器也会发送SYN同步报文段,请求客户端的响应。客户端在接收到SYN同步报文段后也会发送ACK响应来回复服务器。这个过程就是三次握手的过程
  • 这样看来,客户端与服务器的连接是双方的,两者都得发送请求同样两者也都得响应。图上来看,SYN_SENT就是请求连接状态,SYN_RCVD就是等待连接状态。在三次握手成功后,服务器与客户端都会进入ESTABLISHED状态,也就是TCP连接成功态,这个时候就可以进行数据的传送了。
  • 这个过程中如果客户端的SYN请求如果丢包,服务器不会响应,而客户端会有一个等待时间,等待时间到达,未收到ACK响应,这个时候客户端会发起再次请求。如果多次请求都未成功,此时客户端可能会判断网络异常,不会再次请求。同样再服务器接收到客户端的SYN请求之后也会发送ACK响应同时发送SYN请求。如果客户端迟迟不给服务器ACK响应,服务器也会进行重发,直至判断网络异常。所以说,三次握手任意一个缺失都不会连接成功,也就无法通信。所以三次握手也是确保TCP可靠的一种方式。

 

四次挥手

         在客户端与服务器通信完毕后,客户端调用close,开始进行四次挥手:

  • 在客户端与服务器数据传输完毕之后,客户端没有请求了,所以此时调用close关闭文件描述符,开始进入FIN_WAIT_1状态,同时向服务器发送FIN结束报文段并等待服务器的响应。当服务器这里收到了FIN结束报文段时,这个时候服务器进入CLOSE_WAIT状态。并给客户端进行应答发送ACK。当客户端收到服务器的ACK响应时,进入FIN_WAIT_2状态。当服务器调用close时,会向客户端发送FIN结束报文段。此时进入LAST_ACK状态。此时的客户端收到服务器发送的FIN时,会向服务器进行响应ACK,并且客户端进入TIME_WAIT状态,TIME_WAIT结束之后,进入CLOSED,断开连接成功。当服务器收到客户端最后的ACK时,进入CLOSED状态,断开连接成功。
     

一、CLOSE_WAIT与LAST_ACK状态:

1、在三次握手的时候服务器可以将SYN与ACK同时发送,但是为什么这里服务器发送的FIN与ACK是分开发送的呢?

  • 首先FIN信号是由于调用close所以才发送的。而客户端调用close时,发送FIN结束报文段并进入FIN_WAIT_1状态。而这个报文段在服务器中用户态其实是无法感知的,内核会自己处理这个报文段,也就是说由内核进行ACK响应。这个过程中不是由用户代码决定的,服务器的FIN是由用户代码调用close发送的,所以内核与服务器不一定是同时处理这个信息的。所以FIN与ACK不一定是同时发送出去的。(注意:这里是不一定!!!但是三次握手的时候发送SYN是由内核直接完成的,所以这就可以达到一个同步发送的情况。)

2、若服务器的代码没有调用close,意味着并没有发送FIN结束报文段。那么此连接的服务器将会长期保持在CLOSE_WAIT状态,这会有什么影响? 

  • 服务器长期保持在CLOSE_WAIT状态,也就是说分配的文件描述符并没有关闭并归还。那么大量的CLOSE_WAIT存在的话,就会导致一种资源的泄漏可能到最后就没有可分配的文件描述符了,那么就会使一些客户端无法连接,从而造成不可估量的影响。

二、TIME_WAIT状态

1、TIME_WAIT的目的:

  • 确保连接正常释放
  • 使之前发送的消息被接收完毕

2、在客户端最后一次发送ACK响应后,进入TIME_WAIT状态,而这个状态的时候客户端在做什么呢? 

  • 此时客户端在等待!在客户端最后发送ACK响应后,进入TIME_WAIT状态,这是为了防止最后发送的ACK响应丢包。在这里,TIME_WAIT状态会等待2MSL的时间。
  • MSL(Max Segment Life):报文的最大生存时间,这里的生存时间指的是一个报文从发生到被接收到的整个过程,这个过程的时间就是MSL。(Linux下可以利用cat /proc/sys/net/ipv4/tcp_fin_timeout来查看MSL的值) 

3、客户端最后一次发送ACK响应后,为什么要等待2MSL呢?

  • 为了确保最后一条ACK消息的到达。因为客户端在发送最后一条ACK响应后进入TIME_WAIT状态,如果这条ACK报文丢失,那么服务器在等待一个MSL的时间过后发现没有收到ACK响应,那么它会重新发送一条FIN报文。这样一条ACK响应的时间加上重发的FIN的时间正好就是2MSL。如果客户端等待2MSL后没有收到FIN报文,那么意味着服务器收到了客户端发送的ACK报文,这样就断开连接。 

4、在TIME_WAIT的时候,客户端与服务器之间的TCP连接还是存在的。

5、如何避免TIME_WAIT状态?

  • 首先服务器可以设置SO_REUSEADDR套接字选项来通知内核,如果端口忙,但TCP连接位于TIME_WAIT状态时可以重用端口。在一个非常有用的场景就是,如果你的服务器程序停止后想立即重启,而新的套接字依旧希望使用同一端口,此时SO_REUSEADDR选项就可以避免TIME_WAIT状态。

 

常见面试题:

  • 三次握手未完成时

答:当这三个过程没有全部完成 被称为半连接状态

  • 三次握手为什么必须是三次

答:防止延迟(已失效)的连接请求被服务器误认为是新连接,因此造成资源浪费或错误。

        (client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接)

 

        总的来说:"3次握手”的作用就是双方都能明确自己和对方的收、发能力是正常的。

       第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

       第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。 从客户端的视角来看,我接到了服务端发送过来的响应数据包,说明服务端接收到了我在第一次握手时发送的网络包,并且成功发送了响应数据包,这就说明,服务端的接收、发送能力正常。而另一方面,我收到了服务端的响应数据包,说明我第一次发送的网络包成功到达服务端,这样,我自己的发送和接收能力也是正常的。

       第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力,服务端的发送、接收能力是正常的。 第一、二次握手后,服务端并不知道客户端的接收能力以及自己的发送能力是否正常。而在第三次握手时,服务端收到了客户端对第二次握手作的回应。从服务端的角度,我在第二次握手时的响应数据发送出去了,客户端接收到了。所以,我的发送能力是正常的。而客户端的接收能力也是正常的。

        经历了上面的三次握手过程,客户端和服务端都确认了自己的接收、发送能力是正常的。之后就可以正常通信了

  • 为什么连接的时候是三次握手,关闭的时候却是四次挥手

答:因为当Server端收到Client端的SYN连接请求报文后, 可以直接发送SYN+ACK报文,其中ACK报文是用来应答的,SYN报文是用来同步的。但是在关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,因为服务器是否现在关闭发送数据通道还需要上层应用来决定,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。总的来说,四次挥手的原因是:一方发送FIN只表示自己发完了所有要发的数据,但还允许对方继续把没发完的数据发过来。

  • 三次握手的目的是什么

答:一是客户端为了确认服务器端此时在正常运行,二是为了获取序列号(ISN),以便让对方知道接下来接收数据的时候如何按序列号组装数据。

  • 若不进行四次挥手会出现什么后果

答:浪费套接字资源,导致可用的套接字越来越少,进而使得服务器无法正常运行。

 

  • SYN泛洪攻击   (针对三次握手,是TCP/IP协议的一个缺陷)

答:利用合理的服务请求来占用过多的服务资源,从而使合法用户无法得到服务的响应。syn flood属于Dos 攻击的一种。Dos攻击发送无效的请求,使得正确的请求无法被响应。
       攻击者会模拟大量的虚拟IP向服务器发送连接请求,但是服务器确认请求的时候也就是第二步的时候   得不到客户端的回应 因为IP都是攻击者模拟的不存在的IP, 这些请求长时间占用未连接队列导致正确的请求被丢弃或者运行缓慢,最终会引起主机运行缓慢甚至是耗尽资源而瘫痪,以此达到攻击目的。
       恶意的向某个服务器端口发送大量的SYN包,则可以使服务器打开大量的半开连接,分配TCB,从而消耗大量的服务器资源,同时也使得正常的连接请求无法被相应。系统会为此耗尽资源。

  • 避免SYN泛洪攻击

答:延缓TCB分配方法
        消耗服务器资源主要是因为当SYN数据报文一到达,系统立即分配TCB,从而占用了资源。而SYN Flood 由于很难建立起正常连接,因此,当正常连接建立起来后再分配TCB则可以有效地减轻服务器资源的消耗,常见的方法是使用Syn Cache和Syn Cookie技术。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章