zoukankan      html  css  js  c++  java
  • 关于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


    各个状态的意义如下:
    LISTEN - 侦听来自远方TCP端口的连接请求;
    SYN-SENT -在发送连接请求后等待匹配的连接请求;
    SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
    ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
    FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
    FIN-WAIT-2 - 从远程TCP等待连接中断请求;
    CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
    CLOSING -等待远程TCP对连接中断的确认;
    LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
    TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
    CLOSED - 没有任何连接状态;

     

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

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

    但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,
    所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了",(报文1,ACK,应答)
    只有等到我Server端所有的报文都发送完了,我才能发送FIN报文(报文2,FIN,服务器已经结束),
    因此不能一起发送。
    故需要四步握手。

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

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

    先说第一点(要满足可靠连接的需要),
    如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。
    那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。
    这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求
    所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。

    再说第二点(防止前后两次连接的数据包混淆),
    如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。
    也就是说有可能新连接和老连接的端口号是相同的。
    一般来说不会发生什么问题,但是还是有特殊情况出现:
    假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,
    于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。
    所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。

    各种协议都是前人千锤百炼后得到的标准,规范。
    从细节中都能感受到精巧和严谨。
    每次深入体会都有同一个感觉:精妙。

    在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG.
    其中,对于我们日常的分析有用的就是前面的五个字段。

    它们的含义是:
    SYN表示建立连接,
    FIN表示关闭连接,
    ACK表示响应,
    PSH表示有DATA数据传输,
    RST表示连接重置。

    其中,
    ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,
    如果只是单个的一个SYN,它表示的只是建立连接。

    TCP的几次握手就是通过这样的ACK表现出来的。
    但SYN与FIN是不会同时为1的,因为前者表示的是建立连接,而后者表示的是断开连接。

    RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。
    一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。
    PSH为1的情况,一般只出现在 DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。

    TCP的连接建立和连接关闭,都是通过请求-响应的模式完成的。

    概念补充-TCP三次握手:
    TCP(Transmission Control Protocol)传输控制协议
    TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
    位码即tcp标志位,有6种标示:
    SYN(synchronous建立联机)
    ACK(acknowledgement 确认)
    PSH(push传送)
    FIN(finish结束)
    RST(reset重置)
    URG(urgent紧急)
    Sequence number(顺序码)
    Acknowledge number(确认码)


    在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
    第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

    完成三次握手,客户端与服务器开始传送数据.


    示例:
    第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
    第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
    第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,
    若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。

    完成三次握手,主机A与主机B开始传送数据。

    这个协议非常重要,这里把它的链接和释放整理一下

    首先是三次握手:

    1、  客户端发起,像服务器发送的报文SYN=1,ACK=0,然后选择了一个初始序号:seq=x。

    SYN是干什么用的?

    在链接的时候创建一个同步序号,当SYN=1同时ACK=0的时候,表明这是一个连接请求的报文段。如果对方有意链接,返回的报文里面SYN=1,ACK=1,。从这个意义上来说,SYN=1的时候,就表明这是一个‘请求’或者‘接受请求’的报文。

    SYN=1的报文段不能携带数据。但是要消耗掉一个序号,

    ACK是干什么用的?

    仅当ACK=1的时候,确认字号(期望收到对方下一个报文段的第一个数据字节的编号)才有效。因此,TCP规定,当链接建立之后,所有往来的报文里面的ACK都应该是1(事实上,也只有客户端发起的链接请求报文的ACK没有置1)。

    现在的状态:客户端进入SYN-SEND状态;

    2、  服务器接收到了SYN=1,ACK=0的请求报文之后,返回一个SYN=1,ACK=1的确认报文。

    同时,确认号ack=x+1,同时也为自己选择一个初始序号seq=y

    现在的状态:服务器进入SYN-REVD状态;

    3、  客户端接收到了服务器的返回信息之后,还要给服务器返回最后一条确认,ACK=1,确认号ack=y+1;

    现在的状态:客户端进入ESTABLISHED状态。

     

    下面说一下为什么两次握手不行,非得三次:

     

    首先说明一种正常的情况,就是客户端发送了一条请求链接的报文,但是由于网络原因丢失了,所以,不可能接收到服务器端的确认。这个时候,客户端就就只有再一次发送原来的请求报文,这次服务器收到之后返回确认,客户端再确认一次,链接确立。

    然后考虑一种不正常的情况,客户端发了两次请求链接的报文,第二条被服务器捕捉到,返回数据,完成了两次握手。数据传送完成之后,链接关闭。但是这时候,第一条拥塞的请求报文现在到达了服务器端,服务器还以为客户端要又一次建立连接,于是发送确认,然后把自己敞开,等着客户端发送过来数据。于是,很多的网络资源就是这样浪费掉了。

    要是实行三次握手,服务器收到了一条过期的请求报文,返回确认信息,客户端接收到了服务器的信息之后感到莫名其妙,心想:我他妈又没要链接,你返回这个是不是疯了。于是不置一词。服务器过一段时间还没有收到第三次握手的数据,知道客户端并没有要求建立链接的请求,含泪离开。

    然后是四次挥手(four-way handshake):

    现在双方的状态都是ESTABLISHED状态。

    1、  客户端发起请求,请求断开链接。FIN=1,seq=u。u是之前传送过来的最后一个字节的序号+1。

    FIN:用来释放一个链接,当FIN=1的时候,表明此报文的发送方已经完成了数据的发送,没有新的数据要传送,并要求释放链接。

    客户端进入FIN-WAIT-1状态,等着服务器返回确认;

    2、  服务器收到客户端的请求断开链接的报文之后,返回确认信息。ACK=1,seq=v,ack=u+1。

    服务器进入CLOSE-WAIT状态。

    这个时候,客户端不能给服务器发送信息报文,只能接收。但是服务器要是还有信息要传给服务器,仍然能传送。

    3、  当服务器也没有了可以传的信息之后,给客户端发送请求结束的报文。FIN=1,ACK=1,

    ack=u+1,seq=w。

    这个时候的状态:服务器进入LAST-ACK状态。

    4、  客户端接收到FIN=1的报文之后,返回确认报文,ACK=1,seq=u+1,ack=w+1。

    发送完毕之后,客户端进入等待状态,等待两个时间周期。关闭。

    为什么最后还要等待两个时间周期呢?

    1、  客户端的最后一个ACK报文在传输的时候丢失,服务器并没有接收到这个报文。
    这个时候服务器就会超时重传这个FIN消息,然后客户端就会重新返回最后一个ACK报文,等待两个时间周期,完成关闭。
    如果不等待这两个时间周期,服务器重传的那条消息就不会收到。服务器就因为接收不到客户端的信息而无法正常关闭。

    2、  预防上一次在三次握手中提到的失效的报文干扰。两个时间周期过去之后,所有的报文都会在网络中消失,保证下一次重新连接的时候有乱七八糟的报文影响。

    下面看下大家一般比较关心的三种TCP状态

    SYN_RECV
    服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:
    1、net.ipv4.tcp_synack_retries :INTEGER
    默认值是5
    对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。通常我们不对这个值进行修改,因为我们希望TCP连接不要因为偶尔的丢包而无法建立。

    2、net.ipv4.tcp_syncookies
    一般服务器都会设置net.ipv4.tcp_syncookies=1来防止SYN Flood攻击。假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。

    这些处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接。大量SYNC_RECV的TCP连接会导致半连接队列溢出,这样后续的连接建立请求会被内核直接丢弃,这就是SYN Flood攻击。

    能够有效防范SYN Flood攻击的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明。SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。

    观测服务上SYN_RECV连接个数为:7314,对于一个高并发连接的通讯服务器,这个数字比较正常。


    CLOSE_WAIT
    发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。
    出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码

    TIME_WAIT
    根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

    建立连接:

    理解:窗口和滑动窗口
    TCP的流量控制,TCP使用窗口机制进行流量控制
    什么是窗口?
    连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端,接收方发送的确认信息中包含了自己剩余的缓冲区尺寸

    剩余缓冲区空间的数量叫做窗口

    2. TCP的流控过程(滑动窗口)

    参考文章:

    http://www.cnblogs.com/lamian/p/3983497.html

    http://kb.cnblogs.com/page/209100/

    http://blog.csdn.net/whuslei/article/details/6667471/

    http://www.cnblogs.com/Jessy/p/3535612.html

    http://www.cnblogs.com/softidea/p/6062147.html(TIME_WAIT)

  • 相关阅读:
    C#可空类型 T?
    Unity踩过的坑
    Unity可视化数据:创建图表
    unity3d屏幕截图功能
    unity3d插入android有米广告
    Unity调用PC摄像头
    使用Unity的50个建议
    Unity3d流光效果
    unity3d中的Quaternion.LookRotation
    Unity3D中可重载虚函数的总结
  • 原文地址:https://www.cnblogs.com/softidea/p/3984067.html
Copyright © 2011-2022 走看看