美文网首页
TCP 握手协议和状态

TCP 握手协议和状态

作者: GOGOYAO | 来源:发表于2019-07-10 09:26 被阅读0次

[TOC]

参考文献

浅谈CLOSE_WAIT

1. 握手协议

握手协议流程图
  • 为什么需要三次握手
    假设 client 的连接请求延迟,client 就会发出第二次连接请求,server 端回复后则建立了连接,进行通信。等到通信结束,连接关闭,此时有可能第一次的连接请求才到 server 端,那么此时server 回复报文之后,就认为连接已建立,但在 client 端看来, 根本没有发起连接请求(连接建立重试后,已完成通信并关闭连接),所以会忽略 server 端的报文,也不会向这个链接发送数据。此时对于 server 端来说,就会因为维护这个链接而导致资源浪费。

  • 为什么需要四次握手
    因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

  • 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
    虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

2. 线上常见问题

2.1. CLOSE_WAIT 很多导致 fd 打满

close_wait 是被动关闭的一端才会有的状态。该状态在服务器停留时间很短,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 FIN 包,一般有如下几种可能:

  • 程序问题:如果代码层面忘记了 close 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 close 也永远执行不到。
  • 响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。
  • BACKLOG 太大:此处的 backlog 不是 syn backlog,而是 accept 的 backlog,如果 backlog 太大的话,设想突然遭遇大访问量的话,即便响应速度不慢,也可能出现来不及消费的情况,导致多余的请求还在队列里就被对方关闭了。

如果你通过「netstat -ant」或者「ss -ant」命令发现了很多 CLOSE_WAIT 连接,请注意结果中的「Recv-Q」和「Local Address」字段,通常「Recv-Q」会不为空,它表示应用还没来得及接收数据,而「Local Address」表示哪个地址和端口有问题,我们可以通过「lsof -i:<PORT>」来确认端口对应运行的是什么程序以及它的进程号是多少。

此外还有一点需要说明:按照前面图例所示,当被动关闭的一方处于 CLOSE_WAIT 状态时,主动关闭的一方处于 FIN_WAIT2 状态。 那么为什么我们总听说 CLOSE_WAIT 状态过多的故障,但是却相对少听说 FIN_WAIT2 状态过多的故障呢?这是因为 Linux 有一个「tcp_fin_timeout」设置,控制了 FIN_WAIT2 的最大生命周期。坏消息是 CLOSE_WAIT 没有类似的设置,如果不重启进程,那么 CLOSE_WAIT 状态很可能会永远持续下去;好消息是如果 socket 开启了 keepalive 机制,那么可以通过相应的设置来清理无效连接,不过 keepalive 是治标不治本的方法,还是应该找到问题的症结才对。

另外两个案例:

2.2. TIME_WAIT 的问题

如下情况会TIME_WAIT 会导致问题:

  • 影响服务挂掉之后,需要立即重启,此时因为 TIME_WAIT会因为端口占用而导致启动失败。
  • 在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。

这个场景下会出现大量socket处于TIME_WAIT状态
此时需要打开SO_REUSEADDR(地址重用),其实这个选项就是告诉OS如果一个端口处于TIME_WAIT状态, 那么我们就不用等待直接进入使用模式, 不需要继续等待这个时间结束,即可将timewait套接字占用的端口号,分配给新的套接字。

SO_REUSEADDR也可以在机器的内核参数配置:

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间

以上配置就是打开系统的TIMEWAIT重用和快速回收,还不行,可以继续调整参数:

vi /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 1200 
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为20分钟。
net.ipv4.ip_local_port_range = 1024 65000 
#表示用于向外连接的端口范围。缺省情况下很小:32768到61000,改为1024到65000。
net.ipv4.tcp_max_syn_backlog = 8192 
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_tw_buckets = 5000 
#表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT套接字将立刻被清除并打印警告信息。
默认为180000,改为5000。对于Apache、Nginx等服务器,上几行的参数可以很好地减少TIME_WAIT套接字数量,但是对于 Squid,效果却不大。此项参数可以控制TIME_WAIT套接字的最大数量,避免Squid服务器被大量的TIME_WAIT套接字拖死。

相关文章

  • TCP 握手协议和状态

    [TOC] 参考文献 浅谈CLOSE_WAIT 1. 握手协议 为什么需要三次握手假设 client 的连接请求延...

  • tcp三次握手四次挥手

    tcp三次握手四次挥手 TCP状态图 TCP状态时序图 tcp三次握手 流程图: TCP握手状态说明: TCP_S...

  • TCP的三次握手四次挥手

    1. TCP协议的三次握手 参考网站TCP(Transmission Control Protocol,传输控制协...

  • ☆技术问答集锦(四)

    2 TCP 与 UDP 网络层:IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议;传输层:TCP协...

  • TCP状态装换图知识详解(图)

    TCP状态装换图 [TOC] 状态图 状态解释 tcp连接的建立3次握手 tcp断开连接4次挥手 TCP正常连接建...

  • TCP连接的建立和终止

    目录(1)三次握手(2)四次挥手(3)TCP状态转换图(4)TCP连接三次握手+四次挥手+状态转换 一:三次握手 ...

  • 三次握手和四次挥手

    三次握手是TCP建立连接时进行的,四次挥手是TCP断开连接是进行的,要弄明白三次握手和四次挥手,需要了解TCP的协...

  • TCP长连接与心跳保活

    可能很多 Java 程序员对 TCP 的理解只有一个三次握手,四次握手的认识,我觉得这样的原因主要在于 TCP 协...

  • 传输层

    ICMP UDP TCP TCP的三次握手 TCP四次挥手 TCP状态机Tcp_status_map.jpg

  • python网络编程

    TCP协议和UDP协议的区别 TCP/IP协议是一个协议簇,里面包含了很多个协议,UDP只是其中的一个。 TCP协...

网友评论

      本文标题:TCP 握手协议和状态

      本文链接:https://www.haomeiwen.com/subject/qlafkctx.html