zoukankan      html  css  js  c++  java
  • 传输层(一)TCP的三次握手和四次挥手及关闭套接字的原理

    TCP连接需三次握手才能建立,断开连接则需要四次握手。

      客户端TCP状态迁移:
      CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
      服务器TCP状态迁移:
      CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
      整个过程如下图所示:
      一、建立TCP连接
      三次握手:所谓的“三次握手”即对每次发送的数据量是怎样跟踪进行协商使数据段的发送和接收同步,根据所接收到的数据量而确定的数据确认数及数据发送、接收完毕后何时撤消联系,并建立虚连接。
      为了提供可靠的传送,TCP在发送新的数据之前,以特定的顺序将数据包的序号,并需要这些包传送给目标机之后的确认消息。TCP总是用来发送大批量的数据。当应用程序在收到数据后要做出确认时也要用到TCP。
      位码即TCP标志位,有6种标示:SYN(synchronous建立联机)、ACK(acknowledgement确认)、PSH(push传送) 、FIN(finish结束)、RST(reset重置)、URG(urgent紧急)
      确认号:其数值等于发送方的发送序号 +1(即接收方期望接收的下一个序列号)。
      详细过程如下:
      第一次:
      第一次握手:建立连接时,客户端发送SYN包(SYN=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
      第二次:
      第二次握手:服务器收到SYN包,必须确认客户的SYN(ACK=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。
      第三次:
      第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
      三次握手的流程图如下:
       在三次握手过程中,还有一些重要概念:
      未连接队列:

       在三次握手协议中,服务器维护一个未连接队列,该队列为每个客户端的SYN包(SYN=j)开设一个条目,该条目表明服务器已收到SYN包,并向客户发 出确认,正在等待客户的确认包。这些条目所标识的连接在服务器处于 SYN_RECV状态,当服务器收到客户的确认包时,删除该条目,服务器进入ESTABLISHED状态。

      Backlog参数:

      表示内核为相应套接字排队的最大连接个数。仅对于backlog来说,我们需要取一个比较大的值以应对大量的服务请求。

      服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。注意,每次重传等待的时间不一定相同。

      半连接存活时间

      是指半连接队列的条目存活的最长时间,也即服务器从收到SYN包到确认这个报文无效的最长时间,该时间值是所有重传请求包的最长等待时间总和。有时我们也称半连接存活时间为Timeout时间、SYN_RECV存活时间。

      二、关闭TCP连接:

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

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

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

      步骤如下:

      第一步:当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

      第二步:主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先

    发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

      第三步:主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。

      第四步:主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

      在网络编程时,常常会创建套接字,套接字使用完成后常常关闭套接字,那么关闭Socket时客户端和服务端究竟做了什么?

      关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。

      主动关闭是指有本地主机主动发起的关闭;而被动关闭则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接。

      被动关闭的情况下:

      客户端发起中断连接请求,也就是发送FIN报文。

      服务器接到FIN报文后,报文意思是说“我客户端没有数据要发给你了,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据”。

      所以服务器先发送ACK,告诉客户端:“你的请求我收到了,但是我还没准备好,请继续你等我的消息"。

      这个时候客户端就进入FIN_WAIT状态,继续等待服务器的FIN报文。

      当服务器确定数据已发送完成,则向客户端发送FIN报文,告诉客户端:“好了,我这边数据发完了,准备好关闭连接了"。

      Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕服务器不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果服务器没有收

    到ACK则可以重传“。

      Server端收到ACK后,"就知道可以断开连接了"。

      Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。就这样,TCP连接就这样关闭了!

      MSL意思是最大段生命周期(Maximum Segment Lifetime)表明一个包存在于网络上到被丢弃之间的时间。每个IP包有一个TTL(time_to_live),当它减到0时则包被丢弃。

    每个路由器使TTL减一并且传送该包。当一个程序进入TIME_WAIT状态时,他有2个MSL的时间,这个充许TCP重发最后的ACK,万一最后的ACK丢失了,使得FIN被重新传输。

    在2MSL等待状态完成后,socket进入CLOSED状态。

      整个过程客户端所经历的状态如下:

      而服务器所经历的过程如下:

      注意: 在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。

      问题1:为什么连接的时候是三次握手,关闭的时候却是四次握手?
      因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同 步的。但是关闭连接时,当Server端

    收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你 发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我

    才能发送FIN报文,因此不能一起发送。故需要四步握手。

      问题2:为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

      虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发

    可能丢失的ACK报文。

      三、winsocks2关闭套接字的函数有:closesocket,shutdown,WSASendDisconnect.。

      int closesocket( SOCKET s)的作用是关闭指定的socket,并且回收其所有的资源。

      int shutdown( SOCKET s,  int how)则是用于任何类型的套接口禁止接收、禁止发送或禁止收发,但并不对资源进行回收。

      how参数为0时,则该套接口上的后续接收操作将被禁止。这对于低层协议无影响。

      how为1时,则禁止后续发送操作。对于TCP,将发送FIN。

      how为2时,则同时禁止收和发。

  • 相关阅读:
    每种特定的迭代器如何使用
    常量迭代器
    容器迭代器
    三十分钟掌握STL
    高快省的排序算法
    FloatTest32 Example
    /浮点数的比较
    java第一天
    ACwing 898
    POJ 3268
  • 原文地址:https://www.cnblogs.com/suvllian/p/5469541.html
Copyright © 2011-2022 走看看