zoukankan      html  css  js  c++  java
  • TCP的十一种状态与三次握手分析(有图)

    我们知道TCP是面向连接的,我们只知道有连接断开,其实内部还有一些比较复杂的状态。去了解各个状态之间的切换有助于我们更加深入的了解TCP。下面我们就来分析各个状态。

    1、如下图示(图源百度)图中显示出了10种状态。

    我们假定断开时是client主动断开的。
    对于server来说状态有:closed -> listen -> syn_recv -> enstablished -> close_wait -> last_ack -> closed
    对于client来说状态有:closed -> syn_send -> enstablished -> fin_wait1 -> fin_wait2 -> time_wait

    2.结合三次握手进行分析状态
    我们知道三次握手的这样: 
    client 发送 syn x到server
    server 回复syn y和ack x+1
    client再回复 ack y+1
    下面分析上面三步与状态的关系:
    1)首先,刚创建的socket都是closed状态,server调用listen之后进入listen状态
    2)接着、客户端调用connnect。(TCP协议会完成三次握手,client发送第一个syn之后就进入syn_send状态。与此同时,server收到syn并回复syn和ack,server进入syn_recv状态)
    3)之后、client和server都进入了enstablisted状态。之后就可以互相发送数据了。

    关于TCP协议头中的确认号ack的理解可以参考:http://www.cnblogs.com/xcywt/p/8075623.html

    关于Wireshark的理解可以参考:http://www.cnblogs.com/xcywt/p/8025113.html

    结合Wireshark抓包分析三次握手:

     这里设置的过滤器,先清空当前捕获的包,在浏览器打开博客园。

    假设TCPa -> TCPb

    第一次:如下图,发送一个请求 syn,序号为0

    第二次:B回复,发送syn,ack为0+1

    第三次,A回复,ack = y + 1。其中y为之前B发送过来的序号,这里过来的序号的0,所以ack = 1;


    3.结合断开时四次握手进行分析状态

    如图(图源百度,侵删)

    我们知道四次握手是这样的(假定是client先close 的):
    client调用close。TCP协议会发送FIN x给server
    server收到FIN x之后,会回复ack x+1
    接着、server调用close,给客户端发送FIN y
    最后,客户端回复ack y+1
    分析与状态的关系:
    1)client调用close,发送了FINx。client进入fin_wait1状态。server收到并回复ack,server进入close_wait状态。然后client会收到ack,进入fin_wait2状态
    2)server接着调用close,给client发送了fin,server则进入了了last_ack状态。
    3)client收到FIN 之后,回复ack。client进入time_wait状态。server收到ack之后,进入closed状态。
    client在保持了2个MSL之后就进入closed状态)

    4.注意事项
    1)client进入time_wait状态之后,会保持在这个状态2MSL。目的是为了确保发送过去的ack可以被收到(因为后面已经没有数据可以发送了)。
    2)连接过程是状态的改变,促使状态的改变是用户的调用。所以切换状态不一定是用户的调用。(比如,server进入close_wait状态,纯粹是TCP协议做好的,用户并没有调用什么接口)
    3)关于退出时的分析,存在一个主动一个被动关系。上面分析的client主动,则client会出现fin_wait1、fin_wait2、time_wait状态。server会出现close_wait、last_ack状态。
    如果是server主动断开的,则关系刚刚反过来了。server先进入fin_wait1状态,然后是fin_wait2状态。后面整个就反过来了。

    5.关于closing状态的出现(这里就是第十一种状态)
    通过上面的分析我们知道,client主动退出时,先给server发送了一个FIN。接着会收到一个ack确认这个FIN。
    如果没收到ack,而是收到server发来的FIN y。那么这时候则进入closing状态。
    这种情况是怎么出现的呢:那就是双方几乎同时closer一个socket。这是双方都正在关闭socket连接。这种情况出现的几率很小

    6.为什么连接需要三次握手,断开需要四次握手。
    首先我们知道,TCP协议是去全双工的。可以在发送的同时进行接收数据。
    假定是主机A和主机B进行通信,断开时是A主动断开的。
    1)三次握手:第一次握手表明A可以发数据给B。但是无法保证B发给A的数据可以被收到。所以B也需要发送SYN给A,A对它进行回应,才保证了B也可以发数据给A。
    个人理解可以把三步拆分为四步理解:
    a)主机A给B发送SYN
    b)主机B回复ack --- 这时表明A可以发数据给B
    c)主机B发送SYN给A
    d)主机A回复ack --- 这时表明B也可以发送数据给A
    只不过协议中,把中间两步放在一步进行了。

    2)四次握手,就像下面这样理解:
    a)主机A给B发送FIN,表示对B说“我要断开了”
    b)主机B回复ack进行确认,表示对A说“嗯,我知道了,你可以断开了”
    c)然后B发送FIN给A,表示对A说“A,我也要断开了”
    d)A回复ack进行确认,表示对B说:“嗯,知道了,你断开吧”
    前两步对A进行断开,后两步对B进行断开。
    那么为什么不能把中间两步进行合并呢,因为无法保证被断开的一方的数据已经传送完毕了。
    就拿上面的例子来说,假如A断开了通知B,但是B还有数据没有发送完毕,如果立即断开(调用close发送FIN),就无法保证数据的可靠性。
    如果等数据发送完毕再将fin和ack一起发过去,n那么A就会长时间处于fin_wait1状态。这样就比较不好了。

    实际情况中我们可能会在server忘记关闭客户端的socket。那么client就会一直处于fin_wait2状态,越来越多的fin_wait2状态会导致系统崩溃。所以我们需要在编程中要注意在什么情况下要关闭双方的socket。

    7.其他知识
    什么时候会收到SIGPIPE这个信号呢?
    当server关闭一个连接之后,client接着发送数据,第一次发送会收到一个RST的响应。如果接着发送则系统会发出一个SIGPIPE信号给client。
    系统默认的处理是将应用程序退出。实际编程中,我们可以捕获这个信号,让应用程序不退出

     如下(伪代码):

    #include"signal.h"
    void sig_recvpipe(int sig)
    {
    }
    
    int main()
    {
        ......
        signal(SIGPIPE, sig_recvpipe);
        ......
    }
  • 相关阅读:
    0x02 枚举、模拟、递推
    0x01 位运算
    bzoj3529: [Sdoi2014]数表
    bzoj5216: [Lydsy2017省队十连测]公路建设
    POJ1789Truck History
    最小生成树模板
    POJ1258Agri-Net
    POJ1860Currency Exchange(SPFA)
    POJ3083Children of the Candy Corn
    POJ2503Babelfish
  • 原文地址:https://www.cnblogs.com/xcywt/p/8082428.html
Copyright © 2011-2022 走看看