• TCP三次握手&四次挥手


    三次握手

    含义 : 建立一个TCP连接时,需要客户端和服务器总共发送3个包

    目的 : 让客户端连接到服务器的指定端口,建立起TCP连接,同步连接双方的序列号和确认号,并交换 TCP 窗口大小信息,当客户端执行connect()时将触发三次握手。

    第一次握手 :

    客户端发送 : SYN=1 代表是一个连接请求(连接接收报文),此数据报不可携带数据

           seq=x 代表客户端自己的初识序号(seq=0即代表是第0号帧)

           此时客户端进入 syn_sent 状态,代表客户端在等待服务器的回复

    第二次握手 :

    服务器监听接收 : 监听到客户端发送的连接请求报文后,若同意连接则向客户端发送确认

              SYN=1  ACK=1  seq=y 代表服务器自己的初始序号(seq=0即代表服务器发出的是第0号帧)

              ack=x+1(表示期望收到对方下一个报文段第一个数据字节序号也是 x+1 ,并同时声明 x 为止的所有数据都已正确收到)

             此时服务器进入 syn_rcvd 状态,代表服务器已收到客户端的连接请求,等待客户端的确认

    第三次握手 :

    客户端收到服务器的确认报文后,需再次发送确认报文给服务器,并同时携带要发送给服务器的数据

              ACK=1 表示确认号 ack=y+1 有效(代表着期望收到服务器的第1个帧)

              seq=x+1 表示客户端自身的第1个帧(如果客户端第一次握手的初始序号为0时)

    一旦服务器收到客户端的第三次握手的确认之后,当前TCP进入Established(已连接)状态,即可发起http请求,客户端与服务器开始传送数据。

    注意 : 1.三次握手期间并未传递数据,直到建立连接后才进入数据传输状态

        2.SYN是TCP/IP建立连接时使用的握手信号,客户端与服务器之间建立正常的网络连接时,客户端首先发出一个SYN消息,服务器使用SYN+ACK应答代表已接受此消息,最后客户端再以ACK消息响应.

    简述三次握手过程 :

     (1) 第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。

     (2) 第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。

     (3) 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。

    四次挥手

    概念 : TCP连接后的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。

    主动方 : 发送 Fin+ACK 报文,并将发送序号置为x  Fin=1  ACK=1  seq=x

    被动方 : 1.发送 ACK 报文,置发送序号为z,确认序号为 x+1 ACK=x+1  seq=z

         2.发送 Fin+ACK 报文,置发送序号为y,取人序号为x    Fin=1  ACK=x   seq=y

    主动方 : 发送 ACK 报文,置发送序号为x,确认序号为y  ACK=y  seq=x

    简述四次挥手过程 :

    由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,

    一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

    (1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。

    (2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

    (3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。

    (4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

    1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

    因为服务端的LISTEN状态下的SOCKET当收到SYN报文的连接请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可能不会马上关闭SOCKET,可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

    2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

    可靠的实现TCP全双工链接的终止。虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

     

     

    3. 为什么不能用两次握手进行连接?

    3次握手完成的两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
        现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

     

    补充:

    a. 默认情况下(不改变socket选项),当你调用close( or closesocket,以下说close不再重复)时,如果发送缓冲中还有数据,TCP会继续把数据发送完。

    b. 发送了FIN只是表示这端不能继续发送数据(应用层不能再调用send发送),但是还可以接收数据。

    c. 应用层如何知道对端关闭?通常,在最简单的阻塞模型中,当你调用recv时,如果返回0,则表示对端关闭。在这个时候通常的做法就是也调用close,那么TCP层就发送FIN,继续完成四次握手。如果你不调用close,那么对端就会处于FIN_WAIT_2状态,而本端则会处于CLOSE_WAIT状态。这个可以写代码试试。

    d. 在很多时候,TCP连接的断开都会由TCP层自动进行,例如你CTRL+C终止你的程序,TCP连接依然会正常关闭,你可以写代码试试。

     

    TIME_WAIT状态

       TIME_WAIT状态 :主动关闭的一方在发送完对对方FIN报文的确认(ACK)报文后,会进入TIME_WAIT状态。TIME_WAIT状态也称为2MSL状态。

     

     1. 什么是2MSL

        MSL Maximum Segment Lifetime,也就是报文最大生存时间,引用《TCP/IP详解》中的话:“它(MSL)是任何报文段被丢弃前在网络内的最长时间。”那么,2MSL也就是这个时间的2倍。

     当TCP连接完成四 个报文段的交换时,主动关闭的一方将继续等待一定时间(2-4分钟),即使两端的应用程序结束。

      

     2. 为什么需要2MSL?

       其一,保证发送的ACK会成功发送到对方。

       其二,报文可能会被混淆,意思是说,其他时候的连接可能会被当作本次的连接。直接引用《The TCP/IP Guide》的说法:The second is to provide a “buffering period” between the end of this connection and any subsequent ones. If not for this period, it is possible that packets from different connections could be mixed, creating confusion.

     

     3. TIME_WAIT状态所带来的影响:

       当某个连接的一端处于TIME_WAIT状态时,该连接将不能再被使用。事实上,对于我们比较有现实意义的是,这个端口将不能再被使用。某个端口处于TIME_WAIT状态(其实应该是这个连接)时,这意味着这个TCP连接并没有断开(完全断开),那么,如果你bind这个端口,就会失败。对于服务器而言,如果服务器突然crash掉了,那么它将无法再2MSL内重新启动,因为bind会失败。解决这个问题的一个方法就是设置socketSO_REUSEADDR选项。这个选项意味着你可以重用一个地址。

     

       4. 对于TIME_WAIT的插曲:

       当建立一个TCP连接时,服务器端会继续用原有端口监听,同时用这个端口与客户端通信。而客户端默认情况下会使用一个随机端口与服务器端的监听端口通信。有时候,为了服务器端的安全性,我们需要对客户端进行验证,即限定某个IP某个特定端口的客户端。客户端可以使用bind来使用特定的端口。对于服务器端,当设置了SO_REUSEADDR选项时,它可以在2MSL内启动并listen成功。但是对于客户端,当使

    bind并设置SO_REUSEADDR时,如果在2MSL内启动,虽然bind会成功,但是在windows平台上connect会失败。而在linux上则不存在这个问题

        要解决windows平台的这个问题,可以设置SO_LINGER选项。SO_LINGER选项决定调用close时TCP的行为。SO_LINGER涉及到linger结构体,如果设置结构体中l_onoff为非0,l_linger为0,那么调用close时TCP连接会立刻断开,TCP不会将发送缓冲中未发送的数据发送,而是立即发送一个RST报文给对方,这个时候TCP连接就不会进入TIME_WAIT状态。如你所见,这样做虽然解决了问题,但是并不安全。通过以上方式设置SO_LINGER状态,等同于设置SO_DONTLINGER状态。

     

        5. 断开连接时的意外:

        当TCP连接发生一些物理上的意外情况时,例如网线断开,linux上的TCP实现会依然认为该连接有效,而windows则会在一定时间后返回错误信息。

    常见面试题

      • TCP协议和UDP协议的区别是什么
        • TCP协议是有连接的,有连接的意思是开始传输实际数据之前TCP的客户端和服务器端必须通过三次握手建立连接,会话结束之后也要结束连接。而UDP是无连接的
        • TCP协议保证数据按序发送,按序到达,提供超时重传来保证可靠性,但是UDP不保证按序到达,甚至不保证到达,只是努力交付,即便是按序发送的序列,也不保证按序送到。
        • TCP协议所需资源多,TCP首部需20个字节(不算可选项),UDP首部字段只需8个字节。
        • TCP有流量控制和拥塞控制,UDP没有,网络拥堵不会影响发送端的发送速率
        • TCP是一对一的连接,而UDP则可以支持一对一,多对多,一对多的通信。
        • TCP面向的是字节流的服务,UDP面向的是报文的服务。
      • 请详细介绍一下TCP协议建立连接和终止连接的过程?
        •   建立连接:三次握手
          • image
          • 关闭连接:四次挥手
          • image
      • 三次握手建立连接时,发送方再次发送确认的必要性?
        • 主 要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结 点长时间滞留了,一直延迟到连接释放以后的某个时间才到达B,本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次 新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了,这样一直等待A发来数据,B的许多 资源就这样白白浪费了。
      • 四次挥手释放连接时,等待2MSL的意义?
        • 第 一,为了保证A发送的最有一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN和ACK 报文段的确认。B会超时重传这个FIN和ACK报文段,而A就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着A重传一次确认。
        • 第二,就是防止上面提到的已失效的连接请求报文段出现在本连接中,A在发送完最有一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。

     

      • 常见的应用中有哪些是应用TCP协议的,哪些又是应用UDP协议的,为什么它们被如此设计?
        • 以下应用一般或必须用udp实现?
          • 多播的信息一定要用udp实现,因为tcp只支持一对一通信。
          • 如果一个应用场景中大多是简短的信息,适合用udp实现,因为udp是基于报文段的,它直接对上层应用的数据封装成报文段,然后丢在网络中,如果信息量太大,会在链路层中被分片,影响传输效率。
          • 如果一个应用场景重性能甚于重完整性和安全性,那么适合于udp,比如多媒体应用,缺一两帧不影响用户体验,但是需要流媒体到达的速度快,因此比较适合用udp
          • 如果要求快速响应,那么udp听起来比较合适
          • 如果又要利用udp的快速响应优点,又想可靠传输,那么只能考上层应用自己制定规则了。
          • 常见的使用udp的例子:ICQ,QQ的聊天模块。
    • 以qq为例的一个说明

    登陆采用TCP协议和HTTP协议,你和好友之间发送消息,主要采用UDP协议,内网传文件采用了P2P技术。总来的说: 
    1.登陆过程,客户端client 采用TCP协议向服务器server发送信息,HTTP协议下载信息。登陆之后,会有一个TCP连接来保持在线状态。 
    2.和好友发消息,客户端client采用UDP协议,但是需要通过服务器转发。腾讯为了确保传输消息的可靠,采用上层协议来保证可靠传输。如果消息发送失败,客户端会提示消息发送失败,并可重新发送。 
    3.如果是在内网里面的两个客户端传文件,QQ采用的是P2P技术,不需要服务器中转。

  • 相关阅读:
    2020年“3D视觉工坊”视频号最受欢迎视频 Top 10!
    缓存一致性解决方案杂谈
    Mybatis的@Param注解在多参数时候不写,可以正确执行。
    Redis设计与实现之简单动态字符串
    YApi mac上部署
    拖拽方式生成Vue用户界面
    终于可以愉快的撸Java异步代码了!
    Windows 取证之ShellBags
    初识Fastjson漏洞(环境搭建及漏洞复现)
    mongo-express 远程代码执行漏洞分析
  • 原文地址:https://www.cnblogs.com/hsmwlyl/p/10568410.html
Copyright © 2020-2023  润新知