Linux下套接字详解(补充)---- TCP协议中的三次握手和四次挥手(图解)

来源:互联网 发布:js视频广告广告代码 编辑:程序博客网 时间:2024/06/05 16:28

转载自 TCP协议中的三次握手和四次挥手(图解)

其他写的比较好的

简析TCP的三次握手与四次分手

TCP的三次握手(建立连接)和四次挥手(关闭连接)

TCP协议三次握手过程分析

TCP三次握手详解及释放连接过程

TCP/IP 相关知识点与面试题集

TCP/IP详解学习笔记(13)– TCP连接的建立与终止

这里写图片描述

  1. 序号:Seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

  2. 确认序号:Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。

  3. 标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

  4. URG:紧急指针(urgent pointer)有效。

  5. ACK:确认序号有效。

  6. PSH:接收方应该尽快将这个报文交给应用层。

  7. RST:重置连接。

  8. SYN:发起一个新连接。

  9. FIN:释放一个连接。

1 TCP过程详解


建立TCP需要三次握手才能建立,而断开连接则需要四次握手。整个过程如下图所示:
TCP过程详解

这里写图片描述

  • 为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

    这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。

1.1 三次握手


先来看看如何建立连接的。

首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资源。Client端接收到ACK报文后也向Server段发生ACK报文,并分配资源,这样TCP连接就建立了。

这里写图片描述

  1. 第一次握手:Client将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。

  2. 第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

  3. 第三次握手:Client收到确认后,检查ack是否为x+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=y+1,并将该数据包发送给Server,Server检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。

发送方 接收方 报文 数据包 客户端状态 服务器状态 意义 客户端 服务器 SYN同步报文 SYN = 1, ACK=0, seq = x SYN_SEND 客户端请求发起连接, 等待服务器确认请求 服务器 客户端 SYN同步报文 SYN = 1, ACK = 1,seq = y, ack = x + 1 SYN_RECV 服务器确认客户端的请求 客户端 服务器 确认报文 ACK=1,seq = x+1, ack = y + 1 ESTABLISHED ESTABLISHED 客户端确认, 连接完成

|

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

这里写图片描述

1.2 四次挥手


那如何断开连接呢?简单的过程如下:
这里写图片描述

【注意】中断连接端可以是Client端,也可以是Server端。

假设Client端发起中断连接请求,也就是发送FIN报文。

Server端接到FIN报文后,意思是说”我Client端没有数据要发给你了”,但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,”告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。

当Server端确定数据已发送完成,则向Client端发送FIN报文,”告诉Client端,好了,我这边数据发完了,准备好关闭连接了”。Client端收到FIN报文后,”就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。

Server端收到ACK后,”就知道可以断开连接了”。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!

这里写图片描述

由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。

  1. 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。

  2. 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。

  3. 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。

  4. 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
    上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,具体流程如下图:

2 使用tcpdump分析三次握手的过程


2.1 tcpdump抓包和telnet模拟连接


使用tcpdump来抓取数据包

sudo tcpdump -i eth1 -S -nt '(src 192.168.10.71 and dst 192.168.10.134) or (src 192.168.10.134 and dst 192.168.10.71)'

然后用telnet模拟tcp连接

telnet 192.168.10.134 80

用telnet模拟tcp连接

抓取到的数据包如下所示

抓取到的数据包如下所示

tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth1, link-type EN10MB (Ethernet), capture size 262144 bytes[1]  IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [S], seq 3289266508, win 29200, options [mss 1460,sackOK,TS val 8157203 ecr 0,nop,wscale 7], length 0[2]  IP 192.168.10.134.80 > 192.168.10.71.32846: Flags [S.], seq 1229022989, ack 3289266509, win 14480, options [mss 1460,sackOK,TS val 159118902 ecr 8157203,nop,wscale 7], length 0[3]  IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [.], ack 1229022990, win 229, options [nop,nop,TS val 8157203 ecr 159118902], length 0[4]  IP 192.168.10.134.80 > 192.168.10.71.32846: Flags [S.], seq 1229022989, ack 3289266509, win 14480, options [mss 1460,sackOK,TS val 159120102 ecr 8157203,nop,wscale 7], length 0[5]  IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [.], ack 1229022990, win 229, options [nop,nop,TS val 8157323 ecr 159118902], length 0[6]  IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [F.], seq 3289266509, ack 1229022990, win 229, options [nop,nop,TS val 8157522 ecr 159118902], length 0[7]  IP 192.168.10.134.80 > 192.168.10.71.32846: Flags [F.], seq 1229022990, ack 3289266510, win 114, options [nop,nop,TS val 159122093 ecr 8157522], length 0[8]  IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [.], ack 1229022991, win 229, options [nop,nop,TS val 8157522 ecr 159122093], length 0^C8 packets captured8 packets received by filter0 packets dropped by kernel

2.2 三次握手过程分析


首先是第1个报文包含SYN标志, 这个一个同步报文, 即客户端192.168.10.71向服务器192.168.10.134发起连接请求, 该报文包含ISN值为3289266508的seq序号

IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [S], seq 3289266508, win 29200, options [mss 1460,sackOK,TS val 8157203 ecr 0,nop,wscale 7], length 0

接着的2个报文,同样是一个同步报文,包含SYN标志([S]), 他表示服务器同意客户端的连接请求, 同时它将客户端发送给自己的seq码+1作为回复的确认码ack = 3289266509, 同样他会顺带着发送一个seq码(序号值是用来标识TCP数据流中的每一个字节的,但是同步报文比较特殊,即使它没有携带任何数据,他也要占用一个序号值)

IP 192.168.10.134.80 > 192.168.10.71.32846: Flags [S.], seq 1229022989, ack 3289266509, win 14480, options [mss 1460,sackOK,TS val 159118902 ecr 8157203,nop,wscale 7], length 0

最后的第3个报文客户端向服务器发送一个同步报文以确认同服务器的连接,

IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [.], ack 1229022990, win 229, options [nop,nop,TS val 8157203 ecr 159118902], length 0

2.3 四次挥手过程分析


我们抓到的包编号[6][7][8]的包即是4次挥手的过程, 4次挥手为什么是3个包,这点我们慢慢说, 先来看看这几个报文

首先是一个结束报文段[6], 由FIN标识[F], 即客户端请求关闭连接,结束报文段和同步报文段一样,也要占用一个序号值seq(3289266509)

IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [F.], seq 3289266509, ack 1229022990, win 229, options [nop,nop,TS val 8157522 ecr 159118902], length 0

接收到客户端的断开信号FIN后, 服务器会发送一个报文给予确认即ack=seq + 1, 但是此报文是可以省略, 因为服务器马上会发送自己的结束报文, 而该结束报文除了携带者自己的序号值seq用来客户端确认, 还包含了对于客户端结束报文段的确认ack


我们会发现,仅用于确认的目的的报文是可以也被省略的,因为我们服务器的结束报文中包含了对于客户端结束报文的确认信息。确认报文师傅出现在连接断开的过程中, 这个取决于TCP延迟确认的特性。因此我们现在抓取到的数据包中,就缺少了服务器的确认报文段,

服务器的结束报文段,包含了seq序号值和对客户端结束报文段的确认信息ack=客户端结束报文段seq+1

IP 192.168.10.134.80 > 192.168.10.71.32846: Flags [F.], seq 1229022990, ack 3289266510, win 114, options [nop,nop,TS val 159122093 ecr 8157522], length 0

最后我们的客户端发送ack=服务器结束报文段seq+1来对服务器的结束报文段进行确认。

IP 192.168.10.71.32846 > 192.168.10.134.80: Flags [.], ack 1229022991, win 229, options [nop,nop,TS val 8157522 ecr 159122093], length 0

3 状态转换


3.1 Client状态转换


整个过程Client端所经历的状态如下:

这里写图片描述

TCP链接中主动断开链接netstat观察可能出现的状态流转是

  • ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

或者

  • ESTABLISHED->FIN_WAIT_1->TIME_WAIT->CLOSED

跳过FIN_WAIT_2,证明被动方(服务器)也完成了数据传输任务,直接把ACK和FIN一起发给了主动方,因此主动方从FIN_WAIT_1直接跳过FIN_WAIT_2进入TIME_WAIT

3.2 Server状态转换


而Server端所经历的过程如下:转载请注明:blog.csdn.net/whuslei
这里写图片描述

【注意】 在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。

3.3 状态转换总结


连接的建立和释放所要求的步骤可以用一个有限状态机来表达,该状态机有11种状态。每一种状态中都存在一些合法的事件,当合法事件发生的时候,可能需要采取某个动作。当其他事件发生的时候,则报告一个错误。

状 态 描 述 CLOSED 关闭状态,没有连接活动或正在进行 LISTEN 监听状态,服务器正在等待连接进入 SYN RCVD 收到一个连接请求,尚未确认 SYN SENT 已经发出连接请求,等待确认 ESTABLISHED 连接建立,正常数据传输状态 FIN WAIT 1 (主动关闭)已经发送关闭请求,等待确认 FIN WAIT 2 (主动关闭)收到对方关闭确认,等待对方关闭请求 TIMED WAIT 完成双向关闭,等待所有分组死掉 CLOSING 双方同时尝试关闭,等待对方确认 CLOSE WAIT (被动关闭)收到对方关闭请求,已经确认 LAST ACK (被动关闭)等待最后一个关闭确认,并等待所有分组死掉

状态转换

4 常见问题汇总


4.1 为什么连接的时候是三次握手,关闭的时候却是四次握手?


因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。

其中ACK报文是用来应答的,SYN报文是用来同步的。

但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,”你发的FIN报文我收到了”。

只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。

故需要四步握手。

4.2 三次握手建立连接时,发送方再次发送确认的必要性


主要是为了防止已失效的连接请求报文段突然又传到了B,因而产生错误。假定出现一种异常情况,即A发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,一直延迟到连接释放以后的某个时间才到达B,本来这是一个早已失效的报文段。但B收到此失效的连接请求报文段后,就误认为是A又发出一次新的连接请求,于是就向A发出确认报文段,同意建立连接。假定不采用三次握手,那么只要B发出确认,新的连接就建立了,这样一直等待A发来数据,B的许多资源就这样白白浪费了。

4.3 为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?


虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。

所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

四次挥手释放连接时,等待2MSL的意义
第一,为了保证A发送的最有一个ACK报文段能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN和ACK报文段的确认。B会超时重传这个FIN和ACK报文段,而A就能在2MSL时间内收到这个重传的ACK+FIN报文段。接着A重传一次确认。
第二,就是防止上面提到的已失效的连接请求报文段出现在本连接中,A在发送完最有一个ACK报文段后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。

0 0
原创粉丝点击