• TCP状态机


    TCP状态机

    过儿断的是右手 2018-06-13 21:42:16 1203 收藏 2
    展开
            和TCP打交道的时间还是比较多,抓抓包,看看连接,排查一下服务器性能等。以前在学校学习的时候,从课本中学到TCP,但是没有真实的感受,而以前准备面试的时候,大多数时候就是死记硬背的记一下三次握手、四次挥手等知识。感觉学知识还是要学得透彻一点,所以今天也来剖析剖析TCP的各个状态。

    来一张经典的图来镇楼

     

            要直接记住这幅图不容易,因为涉及的状态很多,要一个一个的去理解,然后再串联起来,融会贯通。上图中,粗红色的线代表客户端的TCP连接过程(一般认为主动发起tcp连接的一方为客户端),蓝色虚线是服务器端的TCP连接过程(一般认为被动连接的一方称之为服务器端),细实线用来表示交错的状态。接下来把这些状态分开来理解。

    共同状态:

    CLOSED:关闭状态,这个最好理解,啥都不干

    ESTABLISHED:连接建立状态,此时客户端和服务器端正在进行通信

    客户端:

    SYN_SENT:当处于关闭状态的客户端准备向服务器发起连接请求时,先发送一个SYN报文到服务器,告诉服务器端,我准备来连接你了,请回答;

    FIN_WAIT1:客户端获取了想要的数据了,这个时候是时候关闭连接了,在ESTABLISHED状态下,发送一个FIN报文给服务器端,此时等待服务器端的应答;

    FIN_WAIT2:在FIN_WAIT1的状态下,服务器回了一个ACK包给客户端,意思是你发的连接断开我收到了,不过我现在还有数据没发送完,你再等下;

    CLOSING:服务器端和客户端都在同时关闭这个连接了,此时客户端只收到一个FIN报文,此时只需要发送一个ACK就行了

    TIME_WAIT:服务器发送完数据了,此时就给客户端发送一个FIN保温,意思是现在我这边的内容也发送完了,我们可以正式关闭连接了。此时客户端会发送一个ACK报文给服务器端,告诉服务器端,我收到关闭连接,现在可以确认关闭了,不过我害怕网络有问题,你收不到我发的ACK包,并且我要确定所有的数据都发送完了,所以我需要等一等,确认你确实收到,否则我就要做重传; 

    从图中看出,主动打开的一方的状态转换更为复杂,主要有以下三个分支,现在来逐一的解释一下

    FIN_WAIT1-->FIN_WAIT2-->TIME_WAIT:这个状态是客户端连接的一般状态。

    FIN_WAIT1-->CLOSING-->TIME_WAIT:客户服务器同时关闭了连接

    FIN_WAIT1-->TIME_WAIT:服务器端也没内容发了,发送FIN报文到客户端。进入关闭前的最后状态

    服务器端:

    SYN_RCVD:收到客户端的连接请求,然后发送SYN,并确认客户端的连接请求,即发送ACK包

    CLOSING_WAIT:客户端请求关闭,收到FIN报文,并告诉客户端收到关闭请求了,即发送ACK包;

    LAST_ACK:数据也发完了,这个时候就开始正式进入关闭状态了。

    通过上面得描述,我们可以来梳理下几个常见的问题场景了。

    1 为啥建立连接是三次握手,断开连接是四次挥手?

            建立连接,是客户端发起的,而此时连接的双方是没有数据交换的,所以服务器端在应答请求的时候,SYN可以ACK包一起发送到客户端,这也是TCP的常见做法啦。而四次挥手,TCP是全双工通信,客户端单方面的发起断开请求,不代表服务器端马上就得关闭连接,服务器端如果还有数据发送,这时就必须等服务器发送完数据才能最后关闭,因此FIN与ACK的一般情况下不会一起发。

    2 TIME_WAIT状态过多咋办?

            系统中TIME_WAIT的连接数很多,会导致什么问题呢?这要分别针对客户端和服务器端来看的。

            首先,如果是客户端发起了连接,传输完数据然后主动关闭了连接,这时这个连接在客户端就会处于TIMEWAIT状态,同时占用了一个本地端口。如果客户端使用短连接请求服务端的资源或者服务,客户端上将有大量的连接处于TIMEWAIT状态,占用大量的本地端口。最坏的情况就是,本地端口都被用光了,这时将无法再建立新的连接。

            针对这种情况,对应的解决办法有2个: 
            1. 使用长连接,如果是http,可以使用keepalive 
            2. 增加本地端口可用的范围,比如Linux中调整内核参数:net.ipv4.ip_local_port_range

            对于服务器而已,由于服务器是被动等待客户端建立连接的,因此即使服务器端有很多TIME_WAIT状态的连接,也不存在本地端口耗尽的问题。大量的TIME_WAIT的连接会导致如下问题: 
        1. 内存占用:因为每一个TCP连接都会有占用一些内存。 

        2. 在某些Linux版本上可能导致性能问题,因为数据包到达服务器的时候,内核需要知道数据包是属于哪个TCP连接的,在某些Linux版本上可能会遍历所有的TCP连接,所以大量TIME_WAIT的连接将导致性能问题。不过,现在的内核都对此进行了优化(待确认)。

    最后,再炒点冷饭,我没自己调过,只是从博客看来的

    简单总结一下我对于TIME_WAIT状态TCP连接的理解和处理方法: 
    1. TIME_WAIT状态的设计初衷是为了保护我们的。服务端不必担心系统中有几w个处于TIME_WAIT状态的TCP连接。可以调大net.ipv4.tcp_max_tw_buckets这个参数。 
    2. 使用短连接的客户端,需要关注TIME_WAIT状态的TCP连接,建议是采用长连接,同时调节参数net.ipv4.ip_local_port_range,增加本地可用端口的范围。 
    3. 可以开启net.ipv4.tcp_tw_reuse这个参数,但是实际用处有限。 
    4. 不要开启net.ipv4.tcp_tw_recycle这个参数,它带来的问题比用处大。 
    5. 某些Linux的发行版可以调节TIME_WAIT到CLOSED的等待时间(比如Ali的Linux内核提供了参数net.ipv4.tcp_tw_timeout 
    ),可以稍微调小一点这个参数。

    小弟水平有限,欢迎大家批评指正。
    ————————————————
    版权声明:本文为CSDN博主「过儿断的是右手」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/achenwenping/java/article/details/80627599

  • 相关阅读:
    [CF1263E] Editor
    [CF1288D] Minimax Problem
    [CF1294E] Obtain a Permutation
    [CF770C] Online Courses In BSU
    [CF832D] Misha, Grisha and Underground
    [CF917B] MADMAX
    [CF938D] Buy a Ticket
    [CF959E] Mahmoud and Ehab and the xor-MST
    [CF999E] Reachability from the Capital
    [CF960F] Pathwalks
  • 原文地址:https://www.cnblogs.com/b02330224/p/12971858.html
Copyright © 2020-2023  润新知