编程技术分享平台

网站首页 > 技术教程 正文

学习笔记-TCP连接状态解析

xnh888 2024-11-21 23:08:12 技术教程 24 ℃ 0 评论

前面两篇详细地给大家介绍过了TCP的三次握手和四次挥手流程(学习笔记-TCP三次握手学习笔记-TCP四次挥手 ),本文主要是介绍在TCP连接过程中的各种状态变化。

状态介绍

CLOSED:表示初始状态。

LISTEN:表示服务器端的某个SOCKET处于,可以接受连接了。

SYN_RCVD:这个状态表示接受到了SYN,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。

SYN_SENT:这个状态与SYN_RCVD遥相呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。

ESTABLISHED:表示连接已经建立了。

FIN_WAIT_1:这个状态要好好解释一下,其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。

TIME_WAIT:表示收到了对方的FIN,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

CLOSING:这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也就会出现CLOSING状态,表示双方都正在关闭SOCKET连接。(如上四次挥手拓展中展示)

CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。当对方close一个SOCKET后发送FIN给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是查看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

LAST_ACK:这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

连接介绍

三次握手

1、开始建立连接之前服务器和客户端的状态都为CLOSED;

2、服务器创建socket后开始监听,变为LISTEN状态;

3、客户端请求建立连接,向服务器发送SYN报文,客户端的状态变为SYN_SENT;

4、服务器收到客户端的报文后向客户端发送ACK和SYN报文,此时服务器的状态变为SYN_RCVD;

5、然后,客户端收到ACK、SYN,就向服务器发送ACK,客户端状态变为ESTABLISHED;

6、服务器端收到客户端的ACK后变为ESTABLISHED。此时3次握手完成,连接建立!

数据传输

四次挥手

1、客户端先向服务器发送FIN报文,请求断开连接,其状态变为FIN_WAIT1;

2、服务器收到FIN后向客户端发送ACK,服务器的状态围边CLOSE_WAIT;

3、客户端收到ACK后就进入FIN_WAIT2状态,此时连接已经断开了一半了。如果服务器还有数据要发送给客户端,就会继续发送;

4、直到发完数据,就会发送FIN报文,此时服务器进入LAST_ACK状态;

5、客户端收到服务器的FIN后,马上发送ACK给服务器,此时客户端进入TIME_WAIT状态;

6、再过了2MSL长的时间后进入CLOSED状态。服务器收到客户端的ACK就进入CLOSED状态。

CLOSING: 客户端发送了FIN,但是没有收到服务器的ACK,却收到了服务器的FIN。这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也就会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

注意点

服务端避免TIME_WAIT状态

TIME_WAIT状态对大并发服务器有影响,应尽可能在服务器避免出现TIME_WAIT状态,如果服务器端主动断开连接,服务端就会进入TIME_WAIT状态(主动发起关闭连接的一方),协议设计上,应该让客户端主动断开连接,这样就把TIME_WAIT状态分散到大量的客户端。如果客户端不活跃了,一些客户端不断开连接,这样子就会占用服务器端的连接资源。服务器端也得有个机制踢掉不活跃的连接。

服务端预防TIME_WAIT过多方法

通过调整内核参数解决,编辑文件/etc/sysctl.conf,加入以下内容:

/*表示开启SYN Cookies。当出现SYN等待队列溢出时,

启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭*/

net.ipv4.tcp_syncookies = 1;

/*表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,

默认为0,表示关闭*/

net.ipv4.tcp_tw_reuse = 1;

/*表示开启TCP连接中TIME-WAIT sockets的快速回收,

默认为0,表示关闭*/

net.ipv4.tcp_tw_recycle = 1;

/*修改系默认的 TIMEOUT 时间

然后执行 /sbin/sysctl -p 让参数生效*/

net.ipv4.tcp_fin_timeout = 30;

TIME_WAIT状态存在的意义

保证可靠地终止TCP连接:处于TIME_WAIT状态的客户端会向服务端发送ACK,如果此时ACK丢失,服务端会超时重传FIN报文段,客户端收到重传的报文段最少需要2MSL,所以发送端会等待2MSL时间;

2MSL的意义

服务端收到ACK,关闭连接。但是客户端无法知道ACK是否已经到达服务端,于是一直等待?假如ACK没有到达服务端,服务端会对FIN进行超时重传,如果客户端等待时间足够,又收到FIN消息,说明ACK没有到达服务端,于是再发送ACK,直到在足够的时间内没有收到FIN,说明ACK成功到达。这个等待时间至少是:服务端的Timeout + FIN的传输时间,为了保证可靠,采用更加保守的等待时间2MSL。客户端发出ACK,等待ACK到达对方的超时时间 MSL(最大报文生存时间),等待FIN的超时重传,也是MSL,所以如果2MSL时间内没有收到FIN,说明对方安全收到ACK。

TIME_WAIT的隐患

服务器端,短时间内关闭了大量的连接,就会造成服务器上出现大量的TIME_WAIT连接,占据大量的Tuple,严重消耗着服务器的资源;客户端,短时间内大量的短连接,会大量消耗Client机器的端口,毕竟端口只有65535个,端口被耗尽了,后续就无法再发起新的连接了。

建立连接协议是三次握手,关闭连接是四次握手

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一 个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未 必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文 和FIN报文多数情况下都是分开发送的。

RST强制终止TCP连接

关闭TCP连接除了四次挥手,还可以强制关闭。虽然四次挥手关闭TCP连接是最安全的做法。但在有些时候,我们不喜欢TIME_WAIT 状态(如当MSL数值设置过大导致服务器端有太多TIME_WAIT状态的TCP连接,减少这些条目数可以更快地关闭连接,为新连接释放更多资源),这时我们可以通过设置SOCKET变量的SO_LINGER标志来避免SOCKET在close()之后进入TIME_WAIT状态,这时将通过发送RST强制终止TCP连接(取代正常的TCP四次握手的终止方式)。主要目的是减少TIME_WAIT状态,一般不推荐使用。

SYN攻击

在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击是一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

#netstat -nap | grep SYN_RECV

本文的初衷为学习笔记的分享,部分图文来源于网络,如侵,联删。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表