- CLOSED(关闭):初始状态,表示连接未建立或已关闭。
- LISTEN(监听):表示服务器正在等待客户端请求连接。
- SYN-SENT(同步已发送):表示客户端已经发送了连接请求报文段,并等待服务器确认。
- SYN-RCVD(SYN-RECEIVED,同步已接收):表示服务器已经接收到了连接请求报文段,并向客户端发送了确认报文段。
- ESTABLISHED(已建立):表示连接已经建立,双方可以进行数据传输。
- FIN-WAIT-1(终止等待1):表示客户端已经发送了关闭连接请求报文段,等待服务器确认或拒绝。
- FIN-WAIT-2(终止等待2):表示客户端已经收到了服务器的确认报文段,等待服务器发送关闭连接请求报文段。
- CLOSE-WAIT(关闭等待):表示服务器已经发送了关闭连接请求报文段,等待客户端确认或拒绝。
- CLOSING(关闭中):表示客户端和服务器同时发送了关闭连接请求报文段,等待对方确认或拒绝。
- LAST-ACK(最后确认):表示服务器已经收到了客户端的关闭连接请求报文段,并发送了确认报文段,等待客户端确认或拒绝。
- TIME-WAIT(时间等待):表示连接已经关闭,但是为了保证最后的数据传输已经完成,客户端和服务器都需要等待一段时间。
一般比较以下这三种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连接个数为上千个到1万,对于一个高并发连接的服务器,这个范围比较正常。
即 可用来进行SYN洪泛攻击
CLOSE_WAIT (四次挥手阶段出现)
发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。
即 被动关闭一方有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(主要是耗端口),停止服务。 (socket、端口、进程的关系)
即 主动关闭一方有TIME_WAIT,会持续2MSL的时长。如果过多,会耗尽socket,需要特别注意!
为什么需要TIME_WAIT?TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。
TCP协议的实现中,需要确保在网络中所有的数据包都被正确地接收和处理完毕,才能最终关闭连接,以避免可能出现的数据包丢失和重复等问题
和TIME_WAIT状态有关的系统参数有一般由3个,一般修改设置如下:
1 | net.ipv4.tcp_tw_recycle = 1 # 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 |
番外:
让我们探讨一下Linux上的tcp_tw_reuse设置项。这个设置涉及到TIME_WAIT状态的连接,即处于等待状态的连接,其目的是管理这些状态下连接的重用策略。
左耳朵耗子,即陈皓,在极客时间上开设的专栏中提到,很多中文文档推荐开启这两个参数,但细查资料会发现,开启这些参数并非总是推荐的做法。我个人在Ubuntu 20.04上测试tcp_tw_reuse时,发现其默认值为disabled,意即在默认情况下是关闭的。Linux文档说明,如果从协议的角度看是安全的,那么新连接可以重用TIME_WAIT状态的套接字,但通常不建议未经专家建议或要求修改此设置。
在实际操作中,我查询到的默认值却是2,而非文档中提及的0或1,这引发了我的好奇。进一步查阅Linux内核的提交记录,我发现tcp_tw_reuse的取值已从布尔类型扩展为整型,现在有三个可能的值:0、1和2。其中,0和1的含义保持不变,而2则允许在特定条件下重用TIME_WAIT状态的套接字,特别是针对本机通信地址如127.0.0.1或localhost。
技术专家邵雅芳在极客时间的专栏中提出,不开启此选项可能导致在快速启动应用程序时遇到端口占用问题,因此建议将tcp_tw_reuse设置为1。
综上所述,虽然官方文档默认推荐的设置是0(关闭),而实际默认值可能为2,专家建议在某些情况下开启此功能(设置为1)。那么,面对这些不同的建议,我们应该如何选择呢?
我的方法是进行实际测试。我在一个装有Rocky Linux 9的虚拟机上,运行了Tomcat服务器,并对tcp_tw_reuse的三种设置值进行了压力测试。测试结果显示,无论tcp_tw_reuse设置为0、1还是2,性能差异几乎可以忽略不计。这表明,在我的测试环境下,tcp_tw_reuse的设置对性能的影响微乎其微。
然而,需要指出的是,我的测试环境相对简单,仅限于两台电脑和最高9000 QPS的测试范围。在不同的环境和更高的流量下,tcp_tw_reuse的影响可能会有所不同。此外,虽然我测试的是tcp_tw_reuse对性能的影响,但专家强调的是其在快速启动应用程序时的潜在好处,因此,测试的侧重点存在差异。
总结来说,虽然tcp_tw_reuse在某些情况下可能有其用处,但基于我的测试结果,其对性能的影响不大。在考虑是否修改此设置时,建议根据具体的应用场景和专家建议进行决策。
原文链接: https://dashen.tech/2020/04/26/TCP的11种状态/
版权声明: 转载请注明出处.