一.TCP状态
首先需要了解TCP中有哪些状态每个状态都有什仫意义?下面就来详细的介绍。。。
1).CLOSED:表示初始状态.
2).LISTEN:表示服务器端的某个SOCKET处于监听状态,可以接受连接。
3).SYN_RCVD:这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手 过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
4).SYN_SENT:这个状态与SYN_RCVD有关,当客户端发送SYN报文,因此也随即会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
5).ESTABLISHED:表示连接已经建立了。
6).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看到。
7).FIN_WAIT_2:实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求关闭连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
8).TIME_WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标 志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
9).CLOSING:这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一 个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
10).CLOSE_WAIT:这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一个SOCKET后发送FIN报文给自己,系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话, 那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
11).LAST_ACK: 它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
在上图中可以发现存在2*MSL超时重传机制,为什仫在拆除连接后要等待该2*MSL的时间?
答:我们知道在发送方和接收方拆除连接后会等该一定的时间,这是为了防止刚被释放的端口立即被释放,在RFC中该超时时间一般为120s。如果在两倍最大分组生存期内FIN的应答没有到达的话,FIN的发送方就会直接释放连接;而接收方也会注意到,对方已经不再监听了,于是不再发送,连接拆除。。。。
MSL是报文段最大大生存时间(Maximum Segment Lifetime),RFC 793指出MSL为2分钟,现实中常用30秒或1分钟。
下面从另一个维度进行状态说明:
全部11种状态
1. 客户端独有的:(1)SYN_SENT (2)FIN_WAIT1 (3)FIN_WAIT2 (4)CLOSING (5)TIME_WAIT 。
2. 服务器独有的:(1)LISTEN (2)SYN_RCVD (3)CLOSE_WAIT (4)LAST_ACK 。
3. 共有的:(1)CLOSED (2)ESTABLISHED 。
各个状态的意义如下:
- LISTEN - 侦听来自远方TCP端口的连接请求;
- SYN-SENT -在发送连接请求后等待匹配的连接请求;
- SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
- ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
- FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
- FIN-WAIT-2 - 从远程TCP等待连接中断请求;
- CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
- CLOSING -等待远程TCP对连接中断的确认;
- LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
- TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
- CLOSED - 没有任何连接状态;
LISTENING :侦听来自远方的TCP端口的连接请求 .
- 有提供某种服务才会处于LISTENING状态, TCP状态变化就是某个端口的状态变化,提供一个服务就打开一个端口,例如:提供www服务默认开的是80端口,提供ftp服务默认的端口为21,当提供的服务没有被连接时就处于LISTENING状态。FTP服务启动后首先处于侦听(LISTENING)状态。处于侦听LISTENING状态时,该端口是开放的,等待连接,但还没有被连接。就像你房子的门已经敞开的,但还没有人进来。
- 看LISTENING状态最主要的是看本机开了哪些端口,这些端口都是哪个程序开的,关闭不必要的端口是保证安全的一个非常重要的方面,服务端口都对应一个服务(应用程序),停止该服务就关闭了该端口,例如要关闭21端口只要停止IIS服务中的FTP服务即可。关于这方面的知识请参阅其它文章。
- 如果你不幸中了服务端口的木马,木马也开个端口处于LISTENING状态。
SYN-SENT: 客户端SYN_SENT状态:
- 客户端通过应用程序调用connect进行active open.于是客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT. /*The socket is actively attempting to establish a connection. 在发送连接请求后等待匹配的连接请求 */
- 当请求连接时客户端首先要发送同步信号给要访问的机器,此时状态为SYN_SENT,如果连接成功了就变为ESTABLISHED,正常情况下SYN_SENT状态非常短暂。例如要访问网站http://www.baidu.com,如果是正常连接的话,用TCPView观察 IEXPLORE .EXE(IE)建立的连接会发现很快从SYN_SENT变为ESTABLISHED,表示连接成功。SYN_SENT状态快的也许看不到。
- 如果发现有很多SYN_SENT出现,那一般有这么几种情况,一是你要访问的网站不存在或线路不好,二是用扫描软件扫描一个网段的机器,也会出出现很多SYN_SENT,另外就是可能中了病毒了,例如中了"冲击波",病毒发作时会扫描其它机器,这样会有很多SYN_SENT出现。
SYN-RECEIVED: 服务器端状态SYN_RCVD
- 再收到和发送一个连接请求后等待对方对连接请求的确认。当服务器收到客户端发送的同步信号时,将标志位ACK和SYN置1发送给客户端,此时服务器端处于SYN_RCVD状态,如果连接成功了就变为ESTABLISHED,正常情况下SYN_RCVD状态非常短暂。如果发现有很多SYN_RCVD状态,那你的机器有可能被SYN Flood的DoS(拒绝服务攻击)攻击了。
再收到和发送一个连接请求后等待对方对连接请求的确认。当服务器收到客户端发送的同步信号时,将标志位ACK和SYN置1发送给客户端,此时服务器端处于SYN_RCVD状态,如果连接成功了就变为ESTABLISHED,正常情况下SYN_RCVD状态非常短暂。如果发现有很多SYN_RCVD状态,那你的机器有可能被SYN Flood的DoS(拒绝服务攻击)攻击了。
SYN Flood的攻击原理是:
- 这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟);一个用户出现异常导致服务器的一个线程等待1分钟并不是什么很大的问题,但如果有一个恶意的攻击者大量模拟这种情况,服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源----数以万计的半连接,即使是简单的保存并遍历也会消耗非常多的 CPU 时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。此时从正常客户的角度看来,服务器失去响应,这种情况我们称做: 服务器端受到了SYN Flood攻击(SYN洪水攻击 )
在进行三次握手时,攻击软件向被攻击的服务器发送SYN连接请求(握手的第一步),但是这个地址是伪造的,如攻击软件随机伪造了51.133.163.104、65.158.99.152等等地址。 服务器 在收到连接请求时将标志位 ACK和 SYN 置1发送给客户端(握手的第二步),但是这些客户端的IP地址都是伪造的,服务器根本找不到客户机,也就是说握手的第三步不可能完成。
这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟);一个用户出现异常导致服务器的一个线程等待1分钟并不是什么很大的问题,但如果有一个恶意的攻击者大量模拟这种情况,服务器端将为了维护一个非常大的半连接列表而消耗非常多的资源----数以万计的半连接,即使是简单的保存并遍历也会消耗非常多的 CPU 时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。此时从正常客户的角度看来,服务器失去响应,这种情况我们称做: 服务器端受到了SYN Flood攻击(SYN洪水攻击 )
ESTABLISHED:代表一个打开的连接。
- ESTABLISHED状态是表示两台机器正在传输数据,观察这个状态最主要的就是看哪个程序正在处于ESTABLISHED状态。
- 服务器出现很多 ESTABLISHED状态: netstat -nat |grep 9502或者使用lsof -i:9502可以检测到。
- 当客户端未主动close的时候就断开连接:即客户端发送的FIN丢失或未发送。
- 这时候若客户端断开的时候发送了FIN包,则服务端将会处于CLOSE_WAIT状态;
- 这时候若客户端断开的时候未发送FIN包,则服务端处还是显示ESTABLISHED状态;
- 结果客户端重新连接服务器。
- 而新连接上来的客户端(也就是刚才断掉的重新连上来了)在服务端肯定是ESTABLISHED; 如果客户端重复的上演这种情况,那么服务端将会出现大量的假的ESTABLISHED连接和CLOSE_WAIT连接。
- 最终结果就是新的其他客户端无法连接上来,但是利用netstat还是能看到一条连接已经建立,并显示ESTABLISHED,但始终无法进入程序代码。
ESTABLISHED状态是表示两台机器正在传输数据,观察这个状态最主要的就是看哪个程序正在处于ESTABLISHED状态。
服务器出现很多 ESTABLISHED状态: netstat -nat |grep 9502或者使用lsof -i:9502可以检测到。
当客户端未主动close的时候就断开连接:即客户端发送的FIN丢失或未发送。
这时候若客户端断开的时候发送了FIN包,则服务端将会处于CLOSE_WAIT状态;
这时候若客户端断开的时候未发送FIN包,则服务端处还是显示ESTABLISHED状态;
结果客户端重新连接服务器。
而新连接上来的客户端(也就是刚才断掉的重新连上来了)在服务端肯定是ESTABLISHED; 如果客户端重复的上演这种情况,那么服务端将会出现大量的假的ESTABLISHED连接和CLOSE_WAIT连接。
最终结果就是新的其他客户端无法连接上来,但是利用netstat还是能看到一条连接已经建立,并显示ESTABLISHED,但始终无法进入程序代码。
FIN-WAIT-1: 等待远程TCP连接中断请求,或先前的连接中断请求的确认
- 主动关闭(active close)端应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态./* The socket is closed, and the connection is shutting down. 等待远程TCP的连接中断请求,或先前的连接中断请求的确认 */
主动关闭(active close)端应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态./* The socket is closed, and the connection is shutting down. 等待远程TCP的连接中断请求,或先前的连接中断请求的确认 */
FIN-WAIT-2:从远程TCP等待连接中断请求
主动关闭端接到ACK后,就进入了FIN-WAIT-2 ./* Connection is closed, and the socket is waiting for a shutdown from the remote end. 从远程TCP等待连接中断请求 */
这就是著名的半关闭的状态了,这是在关闭连接时,客户端和服务器两次握手之后的状态。在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于FIN_WAIT_2状态,而服务器则一直处于WAIT_CLOSE状态,而直到应用层来决定关闭这个状态。
CLOSE-WAIT:等待从本地用户发来的连接中断请求
被动关闭(passive close)端TCP接到FIN后,就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT. /* The remote end has shut down, waiting for the socket to close. 等待从本地用户发来的连接中断请求 */
CLOSING:等待远程TCP对连接中断的确认
比较少见./* Both sockets are shut down but we still don't have all our data sent. 等待远程TCP对连接中断的确认 */
LAST-ACK:等待原来的发向远程TCP的连接中断请求的确认
被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个 FIN,等待对方的ACK.就进入了LAST-ACK . /* The remote end has shut down, and the socket is closed. Waiting for acknowledgement. 等待原来发向远程TCP的连接中断请求的确认 */
TIME-WAIT:等待足够的时间以确保远程TCP接收到连接中断请求的确认
在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态。/* The socket is waiting after close to handle packets still in the network.等待足够的时间以确保远程TCP接收到连接中断请求的确认 */
TIME_WAIT等待状态,这个状态又叫做2MSL状态,说的是在TIME_WAIT2发送了最后一个ACK数据报以后,要进入TIME_WAIT状态,这个状态是防止最后一次握手的数据报没有传送到对方那里而准备的(注意这不是四次握手,这是第四次握手的保险状态)。这个状态在很大程度上保证了双方都可以正常结束,但是,问题也来了。
由于插口的2MSL状态(插口是IP和端口对的意思,socket),使得应用程序在2MSL时间内是无法再次使用同一个插口的,对于客户程序还好一些,但是对于服务程序,例如httpd,它总是要使用同一个端口来进行服务,而在2MSL时间内,启动httpd就会出现错误(插口被使用)。为了避免这个错误,服务器给出了一个平静时间的概念,这是说在2MSL时间内,虽然可以重新启动服务器,但是这个服务器还是要平静的等待2MSL时间的过去才能进行下一次连接。
CLOSED:没有任何连接状态
被动关闭端在接受到ACK包后,就进入了closed的状态。连接结束./* The socket is not being used. 没有任何连接状态 */
TCP状态迁移
大家对netstat -a命令很熟悉,但是,你注意到STATE一栏没,基本上显示着established,time_wait,close_wait等,这些到底是 什么意思呢?
大家很明白TCP初始化连接三次握手吧:发SYN包,然后返回SYN/ACK包,再发ACK包,连接正式建立。但是这里有点出入,当请求者收到SYS /ACK包后,就开始建立连接了,而被请求者第三次握手结束后才建立连接。但是大家明白关闭连接的工作原理吗?关闭连接要四次握手:发FIN包,ACK 包,FIN包,ACK包,四次握手!!为什么呢,因为TCP连接是全双工,我关了你的连接,并不等于你关了我的连接。
客户端正常情况下TCP状态迁移:
|
服务器正常情况下TCP状态迁移:
|
当客户端开始连接时,服务器还处于LISTENING,
客户端发一个SYN包后,他就处于SYN_SENT状态,服务器就处于SYS收到状态,
然后互相确认进入连接状态ESTABLISHED.
当客户端请求关闭连接时,客户端发送一个FIN包后,客户端就进入FIN_WAIT_1状态,等待对方的确认包,
服务器发送一个ACK包给客户,客户端收到ACK包后结束FIN_WAIT_1状态,进入FIN_WAIT_2状态,等待服务器发过来的关闭请求,
服务器发一个FIN包后,进入CLOSE_WAIT状态,
当客户端收到服务器的FIN包,FIN_WAIT_2状态就结束,然后给服务器端的FIN包给以一个确认包,客户端这时进入TIME_WAIT,
当服务器收到确认包后,CLOSE_WAIT状态结束了,
这时候服务器端真正的关闭了连接.但是客户端还在TIME_WAIT状态下,
什么时候结束呢.我在这里再讲到一个新名词:2MSL等待状态,其实TIME_WAIT就是2MSL等待状态,
为什么要设置这个状态,原因是有足够的时间让ACK包到达服务器端,如果服务器端没收到ACK包,超时了,然后重新发一个FIN包,直到服务器收到ACK 包.
TIME_WAIT状态等待时间是在TCP重新启动后不连接任何请求的两倍.
大家有没有发现一个问题:如果对方在第三次握手的时候出问题,如发FIN包的时候,不知道什么原因丢了这个包,然而这边一直处在FIN_WAIT_2状 态,而且TCP/IP并没有设置这个状态的过期时间,那他一直会保留这个状态下去,越来越多的FIN_WAIT_2状态会导致系统崩溃.
上面我碰到的这个问题主要因为TCP的结束流程未走完,造成连接未释放。现设客户端主动断开连接,流程如下:
|
由于Server的Socket在客户端已经关闭时而没有调用关闭,造成服务器端的连接处在“挂起”状态,而客户端则处在等待应答的状态上。
此问题的典型特征是:一端处于FIN_WAIT2 ,而另一端处于CLOSE_WAIT。不过,根本问题还是程序写的不好,有待提高
-------------------------------------------------------------------------
CLOSE_WAIT,TCP的癌症,TCP的朋友。
CLOSE_WAIT状态的生成原因
首先我们知道,如果我们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!
因为如果是CLIENT端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:
Client ---> FIN ---> Server
Client <--- ACK <--- Server
这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。
Client <--- FIN <--- Server
这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。
Client ---> ACK ---> Server
Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。
Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Client,那么可能是在关闭连接之前还有许多数据要发送或者其 他事要做,导致没有发这个FIN packet。
通常来说,一个CLOSE_WAIT会维持至少2个小时的时间。如果有个流氓特地写了个程序,给你造成一堆的 CLOSE_WAIT,消耗你的资源,那么通常是等不到释放那一刻,系统就已经解决崩溃了。
只能通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个 问题。
解决这个问题的方法是修改系统的参数,系统默认超时时间的是7200秒,也就是2小时, 这个太大了,可以修改如下几个参数:
|
然后,执行sysctl命令使修改生效。
连接进程是通过一系列状态表示的,这些状态有:
LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE- WAIT,CLOSING,LAST-ACK,TIME-WAIT和CLOSED
二.TCP协议中的常见计时器
TCP协议通常包含四种计时器:重传计时器,持续计时器,保活计时器和时间等待计时器。
1.重传计时器(Retransmission Timer),该计时器用于整个连接期间,用于处理RTO(重传超时)。当一个报文从发送队列发出去后,就启动该计时器,若在RTO之内收到了该报文的ACK,则停止该重传计时器;若t>=RTO都还没有收到报文的ACK,则重传该报文,并清空该重传计时器。
注意:若ACK报文捎带其它信息,则不会为该报文设置重传计时器。
2.持续计时器(Persistent Timer),用于处理零窗体值的通过,防止"死锁"现象。
假若接收端的TCP要命令发送端的TCP停止发送报文段时,就向发送方发送TCP发送一个报文段,该报文的窗体大小字段为0,这就是零窗体值。发送端的TCP收到该零值窗体值报文后,就会停止向接收端的TCP发送报文,直到接收端的TCP发送一个窗体大小非0的ACK报文为止。。。
当接收端向发送端发送ACK应答的时候,假如该应答在传输的路途中丢失了,发送端并没有收到该应答,不再向接收端发送消息;而接收端却认为自己已经做出了回应,一直处在等待的状态中。此时这种情况就是传说中的"死锁"
为了解决上述的"死锁现象",TCP中存在一个持续计时器。启动后,在未超时期间,若收到了接收端的非0窗体的通知,则停止该计时器;若该持续计时器超时了,则发送TCP就发送一个特殊的探測报文段,该报文段仅包括1B的新数据,该报文不须要确认。探測报文的作用在于提醒对方(目的能够记录在数据部分),重传上次发送方发送的那个ACK报文(即那个非0值窗体的报文)。
注意:TCP规定,接收窗体的rwnd=0,也必须接收这三种报文段:零窗体探測报文段、确认报文段和携带紧急数据的报文段。
3.保活计时器(Keeplive Timer),防止两端的TCP在连接期间长时间处于空暇状态。一般是server设置的计时器,超时通常设置为2h,当server超过了2h还没有收到客户的任何信息,server就向客户发送过一个探測报文段,若连续发送了10个探測报文段(每个75s一个)还没有响应,就觉得客户出了故障,直接终止这个连接。
4.时间等待计时器(Time-Wait timer),超时时间=2MSL,max segment lifetime,这个计时器有两个作用:
1).保证在2MSL时间内,server端可以收到最后一个ACK;
2).可以保证之前某些在网络中滞留非常久的发给server的报文不会在本次连接连接关闭后再去骚扰server。
值得注意的是,最后两次挥手期间,启动了两种计时器,server向client发送FIN后启动重传计时器;client收到FIN后,向server发送ACK,同一时间也启动Time-Wait计时器(时间长度为2MSL)。
三、常见攻击方式
1.Dos攻击:针对TCP协议的攻击的基本原理是:TCP协议三次握手没有完成的时候,被请求端B一般都会重试(即再给A发送SYN+ACK报文)并等待一段时间(SYN timeout),这常常被用来进行DOS。
2.会话劫持
找到活动的FTP,猜测序列号,一旦确定了序列号,攻击者就能够把合法的用户断开。这个技术包括拒绝服务、源路由或者向用户发送一个重置命令。无论使用哪一种技术,这个目的都是要让用户离开通讯路径并且让服务器相信攻击者就是合法的客户机。
如果这些步骤取得成功,攻击者现在就可以控制这个会话。只要这个会话能够保持下去,攻击者就能够通过身份验证进行访问。这种访问能够用来在本地执行命令以便进一步利用攻击者的地位。
3.全连接攻击
连接后,不发送数据,让服务器阻塞在recv/read而无所事事。
参考:
http://blog.csdn.net/qq_34328833/article/details/60468358
http://blog.csdn.net/bzfys/article/details/73733917