理解TCP/IP三次握手及当时客户端/服务器端的状态变化

1、TCP/IP三次握手原理图:


Windows客户机访问Linux的Apache服务,tcpdump抓包截图 浏览器

Windows客户端(浏览器):192.168.11.145 缓存

Linux服务器(Apache):192.168.11.243 安全


1.一、下面咱们详细解释TCP/IP创建链接时的三次握手(三次发送报文的过程)及客户端与服务器端在每次握手时显示的链接状态,配有tcpdump抓包来解释?

服务器:192.168.11.244 服务器

客户端(浏览器):192.168.11.243
网络

提示:客户端与服务器端的链接状态可使用netstat -an|more命令来查看 并发

(1)客户端(192.168.11.243)发起访问请求第一次握手,发送【SYN】Seq=x socket

当客户端调用connect函数发起链接时,首先发SYN给服务端,而后本身进入SYN_SENT状态,并等待服务端发送ACK+SYN。 tcp

此时进入 SYN_SENT状态:表示客户端已经发送了SYN报文,等待服务器端回应,这段时间一直处于这种状态。 函数

10122    0.280825    192.168.11.243    192.168.11.244    TCP    74    22649→80 [SYN] Seq=0 Win=5840 Len=0 MSS=1460 SACK_PERM=1 TSval=3301310567 TSecr=0 WS=128 工具

(2)服务器端(192.168.11.244)响应请求,第二次握手,发送【SYN,ACK】Seq=y Ack=x+1

服务端收到这个报文后,进入SYN_RECV状态,而后发送ACK+SYN给客户端,ACK用于对客户端SYN的回应,SYN用于询问客户端是否准备好进行数据传输。

此时进入SYN_RECV状态:表示服务端收到客户端发送ACK+SYN报文。

10123    0.280828    192.168.11.244    192.168.11.243    TCP    74    80→22649 [SYN, ACK] Seq=0 Ack=1 Win=5792 Len=0 MSS=1460 SACK_PERM=1 TSval=2610666359 TSecr=3301310567 WS=128

(3)客户端(192.168.11.243)响应,第三次握手,发送【ACK】Seq=x+1 Ack=y+1

客户端收到ACK后进入ESTABLISHED状态,而后发送一个带ACK标志的TCP报文做为服务器端SYN的回应告诉服务器端准备好数据传输,发送SYN通知服务器端开始传输。

服务器端收到最后一个SYN+ACK后,进入到ESTABLISHED状态。

ESTABLISHED:表示链接已经创建成功了。

10124    0.280886    192.168.11.243    192.168.11.244    TCP    66    22649→80 [ACK] Seq=1 Ack=1 Win=5888 Len=0 TSval=3301310567 TSecr=2610666359


1.二、咱们用文字再次描述一遍客户端访问Web服务器,链接创建(ESTABLISHED)的过程


1)Client

Client发送一个带SYN标志的TCP报文(报文1)到服务器端,表示但愿创建一个TCP链接。

此时程序上,Client端调用socket函数调用时,至关于Client端产生了一个处于Closed状态的套接字。

Client端又调用connect函数调用,系统为Client随机分配一个端口,连同传入connect中的参数(Server的IP和端口),这就造成了一个链接四元组,connect调用让Client端的socket处于SYN_SENT状态。

当Server返回确认,并发送SYN,Client返回确认及SYN后,套接字处于ESTABLISHED阶段,此时双方的链接已经能够进行读写操做。

2)Server

Server发送一个带ACK标志和SYN标志的TCP报文(报文2)给客户端,ACK用于对报文1的回应,SYN用于询问客户端是否准备好进行数据传输。

此时在程序上,Server端调用socket函数调用时,至关于Server端产生了一个处于Closed状态的监听套接字。

Server端调用bind操做,将监听套接字与指定的地址和端口关联,而后又调用listen函数,系统会为其分配未完成队列和完成队列,此时的监听套接字能够接受Client的链接,监听套接字状态处于LISTEN状态。

客户端发送一个带ACK标志的TCP报文(报文3),做为报文2的回应。

当Server端调用accept操做时,会从完成队列中取出一个已经完成的client链接,同时在server这端会产生一个会话套接字,用于和client端套接字的通讯,这个会话套接字的状态是ESTABLISH。


2、链接关闭(CLOSE)的过程,四次握手

与链接创建分为server/client不一样,链接关闭并无绝对的server/client之分,链接关闭分为主动关闭和被动关闭。 Server和client均可以担任这两个角色中的任意一个。如client能够关闭它与server的链接,一样的server同样也能够关闭一些长时间无读写事件发生的链接。既然这么说了,下面就会分红两个部分:server主动关闭,client主动关闭。


2.1 本例是客户端浏览器访问Apache服务器的80端口,Server返回页面给Client,而后Server主动关闭,Client被动关闭,开始两者都处于ESTABLISHED状态,下面配有tcpdump抓包来分析:


服务器(Apache):172.21.50.1

客户端(浏览器):192.168.144.129


(1)服务器端(172.21.50.1)主动发起关闭请求,第一次握手开始,调用close函数发送【FIN,ACK】,Seq=x(492)Ack=y(360)到客户端

7    0.014071    172.21.50.1    192.168.144.129    TCP    54    80→54658 [FIN, ACK] Seq=492 Ack=360 Win=15744 Len=0

此时服务器端处于FIN_WAIT1状态。

(2)客户端(192.168.144.129)收到这个【FIN,ACK】,发送回应的【ACK】开始第二次握手,Seq=y(360)Ack=x+1(493)给服务器。

8    0.017852    192.168.144.129    172.21.50.1    TCP    60    54658→80 [ACK] Seq=360 Ack=493 Win=65208 Len=0

服务器端收到这个【ACK】后进入FIN_WAIT2状态。

(3)客户端(192.168.144.129)也没有数据要传输了,开始关闭与客户端的链接,第三次握手开始,也须要调用close函数发送一个【FIN,ACK】,Seq=y(360)Ack=x+1(493)给服务器。

9    0.018789    192.168.144.129    172.21.50.1    TCP    60    54658→80 [FIN, ACK] Seq=360 Ack=493 Win=65208 Len=0

此时客户端的状态为LAST_ACK

(4)服务器端(172.21.50.1)收到来自客户端的【FIN,ACK】后,服务器端的套接字处于TIME_WAIT状态,第四次握手,它会向客户端再发送一个【ACK】确认,Seq=x+1(493)Ack=y+1(361)。

10    0.018801    172.21.50.1    192.168.144.129    TCP    54    80→54658 [ACK] Seq=493 Ack=361 Win=15744 Len=0

当客户端收到ACK后就处于CLOSED状态。

2.2 本例是客户端浏览器访问Apache服务器的443端口,Server返回页面给Client,而后Server主动关闭,Client被动关闭,开始两者都处于ESTABLISHED状态,下面配有tcpdump抓包来分析:

(这个有点不一样,有5次握手)

服务器(Apache):172.21.50.1

客户端(浏览器):192.168.144.129


(1)服务器端(172.21.50.1)主动发起关闭请求第一次握手开始,调用close函数发送【FIN,ACK】,Seq=x(3766)Ack=y(823)到客户端

32    0.419117    172.21.50.1    192.168.144.129    TCP    54    443→52666 [FIN, ACK] Seq=3766 Ack=823 Win=17920 Len=0

此时服务器端处于FIN_WAIT1状态。

(2)客户端(192.168.144.129)收到这个【FIN,ACK】,发送回应的【ACK】开始第二次握手,Seq=y(823)Ack=x(3766)给服务器。

33    0.422318    192.168.144.129    172.21.50.1    TCP    60    52666→443 [ACK] Seq=823 Ack=3766 Win=64852 Len=0

服务器端收到这个【ACK】后进入FIN_WAIT2状态。

(3)客户端(192.168.144.129)再发送一个回应的【ACK】,开始第三次握手,Seq=y(823)Ack=x+1(3767)给服务器。

34    0.422405    192.168.144.129    172.21.50.1    TCP    60    52666→443 [ACK] Seq=823 Ack=3767 Win=64852 Len=0

此时客户端处于CLOSE_WAIT状态。

(4)客户端(192.168.144.129)也没有数据要传输了,开始关闭与客户端的链接,第四次握手开始,也须要调用close函数发送一个【FIN,ACK】,Seq=y(823)Ack=x+1(3767)给服务器。

35    0.423136    192.168.144.129    172.21.50.1    TCP    60    52666→443 [FIN, ACK] Seq=823 Ack=3767 Win=64852 Len=0

此时客户端的状态为LAST_ACK

(5)服务器端(172.21.50.1)收到来自客户端的【FIN,ACK】后,服务器端的套接字处于TIME_WAIT状态,第五次握手,它会向客户端再发送一个【ACK】确认,Seq=x+1(3767)Ack=y+1(824)。

36    0.423147    172.21.50.1    192.168.144.129    TCP    54    443→52666 [ACK] Seq=3767 Ack=824 Win=17920 Len=0

当客户端收到ACK后就处于CLOSED状态。

因为在链接关闭后,还不能肯定全部链接关闭前的包都被客户端接受到了(包的接受是没有前后顺序的),所以有了TIME_WAIT状态。在这个状态中,服务器仍然在等待客户机发送的【ACK】包。这个状态将保持2*MSL的时间,这里的MSL指的是一个TCP包在网络中存在的最长时间,通常状况下 2*MSL=240秒。

3.2.2 Client主动关闭,Server被动关闭:

原理与上面相同,略。


3.3 介绍TIME_WAIT这个状态,分别按照client/server两个部分讲述


首先说一下TCP/IP详解中描述的关于TIME_WAIT的描述及其存在的必要性:

主动关闭的socket当收到对端的FIN操做后,该socket就会处于TIME_WAIT状态,处于TIME_WAIT状态的socket会存活2MSL(Max Segment Lifetime),

之因此存活这么长时间是有理由的:

一方面是可靠的实现TCP全双工链接的终止,也就是当最后的ACK丢失后,被动关闭端会重发FIN,所以主动关闭端须要维持状态信息,以容许它从新发送最终的ACK。

另外一方面TCP在2MSL等待期间,定义这个链接(4元组)不能再使用,任何迟到的报文都会丢弃。设想若是没有2MSL的限制,刚好新到的链接正好知足原先的4元组,这时候链接就可能接收到网络上的延迟报文就可能干扰最新创建的链接。重复的分节在网络中消逝。

3.4 状态为TIME_WAIT是否是全部执行主动关闭的socket都会进入TIME_WAIT状态呢?


有没有什么状况使主动关闭的socket直接进入CLOSED状态呢?

主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间,这个是TCP/IP必不可少的,也就是“解决”不了的。也就是TCP/IP设计者原本是这么设计的。

主要有两个缘由:

一、防止上一次链接中的包,迷路后从新出现,影响新链接(通过2MSL,上一次链接中全部的重复包都会消失)

二、可靠的关闭TCP链接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会从新发 fin, 若是这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。因此 主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

特别提示的是:为何TIME_WAIT状态还须要等待2MSL才能回到CLOSED状态?或者为何TCP要引入TIME_WAIT状态?

《TCP/IP详解》中如此解释:当TCP执行一个主动关闭,并发回最后一个ACK后,该链接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可让TCP再次发送最后的ACK以防止这个ACK丢失(另外一端超时重发最后的FIN)。

附注:MSL(Maximum Segment Lifetime)即最大生存时间,RFC 793中指出MSL为2分钟,可是实现中的经常使用值为30秒、1分钟或者2分钟。

3.5 终止TCP/IP链接(四次握手)

    因为TCP链接是全双工的,所以每一个方向都必须单独进行关闭。原则是主动关闭的一方(如已传输完全部数据等缘由)发送一个FIN报文来表示终止这个方向的链接,收到一个FIN意味着这个方向再也不有数据流动,但另外一个方向仍能继续发送数据,直到另外一个方向也发送FIN报文。

四次挥手的具体过程以下:

客户端发送一个FIN报文(报文4)给服务器,表示我将关闭客户端到服务器端这个方向的链接。

服务器收到报文4后,发送一个ACK报文(报文5)给客户端,序号为报文4的序号加1。

服务器发送一个FIN报文(报文6)给客户端,表示本身也将关闭服务器端到客户端这个方向的链接。

客户端收到报文6后,发回一个ACK报文(报文7)给服务器,序号为报文6的序号加1。

至此,一个TCP链接就关闭了。(4次挥手不是关闭TCP链接的惟一办法,见五、TCP/IP答疑解惑)

TCP三次握手,四次挥手的时序图:

4、TCP/IP答疑解惑

4.1 为何在TCP协议里,创建链接是三次握手,而关闭链接倒是四次握手呢?

    由于当处于LISTEN 状态的服务器端SOCKET当收到SYN报文(客户端但愿新建一个TCP链接)后,它能够把ACK(应答做用)和SYN(同步做用)放在同一个报文里来发 送给客户端。但在关闭TCP链接时,当收到对方的FIN报文时,对方仅仅表示对方没有数据发送给你了,但未必你的全部数据都已经所有发送给了对方,因此你 大可没必要立刻关闭SOCKET(发送一个FIN报文),等你发送完剩余的数据给对方以后,再发送FIN报文给对方来表示你赞成如今关闭链接了,因此一般情 况下,这里的ACK报文和FIN报文都是分开发送的。

4.2 为何TIME_WAIT 状态还须要等2*MSL秒以后才能返回到CLOSED 状态呢?

    由于虽然双方都赞成关闭链接了,并且握手的4个报文也都发送完毕,按理能够直接回到CLOSED 状态(就比如从SYN_SENT 状态到ESTABLISH 状态那样),可是咱们必须假想网络是不可靠的,你没法保证你(客户端)最后发送的ACK报文必定会被对方收到,就是说对方处于LAST_ACK 状态下的SOCKET可能会由于超时未收到ACK报文,而重发FIN报文,因此这个TIME_WAIT 状态的做用就是用来重发可能丢失的ACK报文。

4.3 关闭TCP链接必定须要4次挥手吗?

    不必定,4次挥手关闭TCP链接是最安全的作法。但在有些时候,咱们不喜欢TIME_WAIT 状态(如当MSL数值设置过大致使服务器端有太多TIME_WAIT状态的TCP链接,减小这些条目数能够更快地关闭链接,为新链接释放更多资源),这时 咱们能够经过设置SOCKET变量的SO_LINGER标志来避免SOCKET在close()以后进入TIME_WAIT状态,这时将经过发送RST强 制终止TCP链接(取代正常的TCP四次握手的终止方式)。但这并非一个很好的主意,TIME_WAIT 对于咱们来讲每每是有利的。

5、SYN攻击

SYN攻击原理:

    SYN Flood利用TCP协议缺陷,发送了大量伪造的TCP链接请求,使得被攻击方资源耗尽,没法及时回应或处理正常的服务请求。一个正常的TCP链接须要三次握手,首先客户端发送一个包含SYN标志的数据包,其后服务器返回一个SYN/ACK的应答包,表示客户端的请求被接受,最后客户端再返回一个确认包ACK,这样才完成TCP链接。在服务器端发送应答包后,若是客户端不发出确认,服务器会等待到超时,期间这些半链接状态都保存在一个空间有限的缓存队列中;若是大量的SYN包发到服务器端后没有应答,就会使服务器端的TCP资源迅速耗尽,致使正常的链接不能进入,甚至会致使服务器的系统崩溃。


检测SYN攻击很是的方便,当你在 务器上看到大量的半链接状态时,特别是源IP地址是随机的,基本上能够判定这是一次SYN攻击。咱们使用系统自带的netstat 工具来检测SYN攻击:
# netstat -n -p TCP
tcp 0  0 10.11.11.11:23 124.173.152.8:25882  SYN_RECV -
tcp 0  0 10.11.11.11:23 236.15.133.204:2577  SYN_RECV -
tcp 0  0 10.11.11.11:23 127.160.6.129:51748  SYN_RECV -
tcp 0  0 10.11.11.11:23 222.220.13.25:47393  SYN_RECV -
tcp 0  0 10.11.11.11:23 212.200.204.182:60427 SYN_RECV -
tcp 0  0 10.11.11.11:23 232.115.18.38:278  SYN_RECV -
tcp 0  0 10.11.11.11:23 239.116.95.96:5122 SYN_RECV -

tcp  0 0 10.11.11.11:23 236.219.139.207:49162 SYN_RECV -

上面是在LINUX系统中看到的,不少链接处于SYN_RECV状态(在WINDOWS系统中是SYN_RECEIVED状态),源IP地址都是随机的,代表这是一种带有IP欺骗的SYN攻击。

相关文章
相关标签/搜索