TCP三次握手,四次断开详解

TCP三次握手,四次断开详解一、三次握手 TCP建立连接的过程就是三次握手(Three-way Handshake),在建立连接的过程实际上就是客户端和服务端之间总共发送三个数据包。进行三次握手主要

一、三次握手

TCP连接建立的过程是三次握手,客户端和服务器之间实际上发送了三个数据包。三次握手主要是为后续数据传输的可靠性做准备,让客户端和服务器都指定自己的初始化序列号,以保证双方能够发送和接收数据包。本质上,当客户端连接到服务器时,它指定一个端口,建立TCP连接,同步双方的序列号(seq)和确认号(ack),并交换TCP窗口大小信息。

1.1三次握手流程图

1.2 三次握手过程详解

当第一次建立连接时,客户端最初处于关闭状态,当连接发起时,客户端主动打开端口。然后执行三向握手。

发送第一个SYN 的一端执行主动打开,另一端收到此SYN 并发回下一个SYN 执行被动打开。在套接字编程中,当客户端执行connect()时,会触发三向握手。

初次握手:客户端向服务器发送带有SYN标志的数据包(TCP有6个标志,SYN标志在倒数第15位,二进制位为1。第一个SYN发送数据包后SYN=1)、请求建立连接,并指定客户端的初始化序列号seq(为了网络安全,初始序列号是随机生成的)。此时,客户端处于SYN_SENT状态。头同步位SYN=1,初始序列号seq=x,SYN=1的报文段不能携带数据,但会消耗序列号。

第二次握手:服务器收到客户端的SYN标志包后,向客户端发送SYN标准位包作为响应,表示同意(SYN向客户端表明客户端到服务器的通道是OK的)。建立连接。还要指定自己的初始化序列号seq。并且它使用客户端的seq+1(x+1)作为ACK的值接收到客户端的SYN(ACK用于确保从服务器到客户端的通道没有问题)指示。服务器当前处于SYN_RCVD状态。

确认标志位表示数据包的ACK为1,确认号ack=x+1(必须回复的数据包的ACK在发送的序列号上加1),初始序列号Masu。服务器序列为seq=y。

第三次握手:客户端收到SYN标志包后,发送ACK标志包,同时表示将使用服务器的初始序列号seq+1(y+1)作为自己的确认号ACK指示的值。从服务器收到SYN 标志数据包。此时,客户端处于ESTABLISHED状态(连接完成)。

确认标志位中,数据包的ACK为1,确认号ack=y+1,客户端的序列号seq=x+1(第一个是seq=x,所以第二个报文段有+1)。是必须的)。 ACK可以携带数据,但如果不携带数据,则不消耗序列号。

最后,服务器收到确认标志数据包,服务器状态由syn_received变为ESTABLISHED。这样,两者之间就建立了联系。

当发送最后一个数据包时,ACK不占用序列号,因为服务器不需要响应该数据包。

ACK一般包含序列号,但通常不占用序列号。下一个数据包也可以从ACK序列号开始。

1.3三次握手中需要注意的

三路握手标志位:SYN、SYN、ACK。

在初始握手期间,客户端使用SYN 标志数据包进行响应。在第二次握手期间,服务器使用SYN 和ACK 标志进行响应。第三次,客户端使用ACK 标志进行响应。

检查状态。当服务器响应时,客户端更改为SYN_SENT 状态。当客户端发送数据包时,服务器变为SYN_RCVD状态。

ACK 消息用于响应,SYN 消息用于同步。

LISTEN: 侦听来自远程TCP 端口的连接请求。

SYN_SENT: 发送连接请求后等待匹配的连接请求。

SYN_RECEIVED:发送和接收连接请求后,等待连接请求的确认。

ESTABLISHED: 表示打开的连接,可以将数据传输给用户。

FIN-WAIT-1:等待来自远程TCP 的连接中止请求或先前连接中止请求的确认。

FIN-WAIT-2:等待来自远程TCP 的连接中止请求的确认。

CLOSING: 正在等待连接中止的远程TCP 确认。

LAST-ACK: 等待最初发送到远程TCP 的连接中止请求的确认。

TIME-WAIT:等待足够的时间以确保远程TCP 收到连接中止请求的确认。

CLOSED: 无连接状态。

思考:如果在第三次握手时,由于网络问题而没有收到客户端发送给服务器的ack包,会发生什么情况?

此时引入超时重传定时器,如果服务器没有收到ACK包,则重传数据包(SYN+ACK数据包)。

超时重传定时器:当数据包超时重传时,该定时器清零。

如果没有收到ack包,服务器端检测超时,则服务器重发数据包:SYN+ACK。

SACK:(选择性确认): 是一个TCP 选项,它执行TCP 来单独确认非连续片段。用于发出真正丢失的数据包的信号并仅重传丢失的片段。要使用SACK,两个设备在建立连接时必须同时支持SACK。如果允许,TCP 报文段可以在后续发送过程中包含SACK 选项。如果它们包含一系列非连续的未确认数据序列,则双方都必须支持这些SYN 数据包的SACK 允许选项。

思考:那是不是意味着服务器发送数据包的次数没有限制呢?

当然不是。您可以更改TCP 参数设置来更改通过TCP 发送的syn+ack 数据包的数量。

在centos系统上,syn+ack数据包默认发送5次。该文件的保存路径为/proc/sys/net/ipv4/。

tcp_synack_retries。

内核参数配置文件:/proc/sys/net/ipv4

proc 代表进程,vm 代表虚拟内核,net 代表网络。

[root@nginx-kafka01 html]# cd /proc/sys

[root@nginx-kafka01 系统]# ls

abi crypto 调试dev fs 内核net sunrpc 用户vm

[root@nginx-kafka01 ipv4]# pwd

/proc/sys/net/ipv4

临时更改的文件为/proc/sys/net/ipv4/tcp_synack_retries

[root@nginx-kafka01 ipv4]# cat tcp_synack_retries

内核参数更改(调优)和永久更改:vim /etc/sysctl.conf

修改后在/proc/sys/net/ipv4/tcp_synack_retries生效。

[root@nginx-kafka01 ipv4]# vim /etc/sysctl.conf

1 # Sysctl 设置通过以下文件定义:

2 # /usr/lib/sysctl.d/、/run/sysctl.d/和/etc/sysctl.d/。

3#

4 # 供应商配置位于/usr/lib/sysctl.d/中。

5 # 要覆盖整个文件,请创建一个包含相同文件的新文件。

6 # 将新设置添加到/etc/sysctl.d/以覆盖。

7 # 仅特定设置,稍后按词法添加文件

8 # 命名/etc/sysctl.d/并将新配置放在那里。

9#

10 # 有关详细信息,请参阅sysctl.conf(5) 和sysctl.d(5)。

11#

12 net.ipv4.ip_forward=10

13 虚拟机交换=10

刷新启用或重新启动

更新生效:systemctl -p

重启:初始化6

显示当前内核参数。

系统ctl-a

1.4 三次握手的知识细节

当服务启动时,会初始化两个队列:半连接队列和全连接队列。

1.半连接队列(syn队列)

发送给客户端的数据存储在该空间中,服务器将连接信息放入半连接队列中,连接状态为syn_recvd。

在半连接队列中,如果服务器没有收到客户端的ack消息,即连接已满。

可能的问题有:

(1)半连接队列配置参数太小。

如果一半连接已满,请通过更改配置参数size /proc/sys/net/ipv4/tcp_max_syn_backlog来扩展连接队列。

[root@nginx-kafka01 ipv4]# cat tcp_max_syn_backlog

128

(2)Synflood攻击(洪水攻击)

如果一半连接已满并且更改连接数据不起作用,则为Synflood 攻击。 SYN攻击检测:如果我们看到服务器上有大量的半连接,特别是源IP地址是随机的,我们基本可以断定这是一次SYN攻击。 Symflood攻击症状可以在dmess日志中确认。在Linux/Unix上,您可以使用系统自带的netstats命令来检测SYN攻击。

什么是synflood攻击?synflood攻击是指利用TCP协议的缺陷,发送大量伪造的TCP连接请求。他们经常使用假IP或IP号码段发送大量首次握手数据包。服务器发送第二次握手包(SYN+ACK 包)。由于对方是假IP,所以收不到数据包,也不响应第三次握手数据包。这导致被攻击的服务器维护大量的SYN_RECV。状态为“半连接”,默认会重试5次来响应第二次握手数据包,大量随机恶意SYN_RECV会填满不完整的连接。这会阻止正常的业务请求连接。这是典型的DoS/DDoS 攻击。

[服务器端资源分配是在第二次握手时分配的,但客户端资源是在第三次握手完成后分配的,使得服务器容易受到SYN洪水攻击]

Syn-Flood攻击成功的关键是服务器资源有限,服务器收到请求时分配资源。通常,服务器使用这些资源来提供有关请求的关键信息,包括请求的源和目标(5 元组),以及TCP 选项,例如最大分段长度MSS、时间戳和您保存的选择性响应。启用Sack、窗口缩放因子Wscale等当后续的ACK消息到达时,三向握手完成,并创建一个新的连接,可以将此信息复制到连接结构中,以指导后续消息的发送和接收。

MSS:TCP 最大消息长度传输层一次可以发送的最大数据量(不包括标头字段)

一般来说,MSS的最大值为: 1500 – ip 标头(20 字节) – tcp 标头(最小20 字节)=1460 最大消息长度

采用IP层的MSS tcp包,一般不需要对网络层进行分片,MSS有选项。

MTU:最大传输单元网络层最大1500字节

如何解决synflood:

a. 缩短超时(SYN 超时)时间。

b. 增加最大连接数

c. 过滤网关保护

SYNcookie技术

Syn cookies用于解决syn洪水攻击。 TCP建立连接时,双方的起始消息序列号是任意的。 SYN cookie 使用它来构建初始序列号。当客户端收到这个SYN+ACK消息时,根据TCP标准,它会回复一个ACK消息,并在消息中返回ack=n + 1。然后服务器收到后,返回ack-1。第一个发送的SYN+ACK报文的编号已经消失。服务器巧妙地通过这种方式间接存储了一些SYN报文信息。服务器必须看到序列号ack – 1。

以下是启用syncookie 的方法: /proc/sys/net/ipv4/tcp_syncookies 值为1 表示启用syncookie。

[root@nginx-kafka01 ipv4]# cat tcp_syncookies

1

2.全连接队列(acceptqueue)

服务器连接现在处于已建立状态,表明连接队列已满。

3. 如何查看连接状态

netstat -a

netstat-tanpl

netstat-anplut

需要安装ss -lnt

[root@nginx-kafka01 ipv4]# netstat -a

有效的互联网连接(服务器和已建立)

Proto Recv-Q Send-Q 本地地址外部地址状态

tcp 0 0 0.0.0.0:mysql 0.0.0.0:* 监听

tcp 0 0 0.0.0.0:sunrpc 0.0.0.0:* 监听

tcp 0 0 0.0.0.0:http 0.0.0.0:* 听

tcp 0 0 0.0.0.0:ssh 0.0.0.0:* 听

tcp 0 0 localhost:smtp 0.0.0.0:* 监听

tcp 0 0 nginx-kafka01:ssh 192.168.2.135:52102 已建立

tcp6 0 0 [:]:sunrpc [:]:* 请听

tcp6 0 0 [:]:hbci [:]:* 请听

tcp6 0 0 localhost:smtp [:]:* 听

UDP 0 0 0.0.0.0:sunrpc 0.0.0.0:*

UDP 0 0 0.0.0.0:757 0.0.0.0:*

udp 0 0 本地主机:323 0.0.0.0:*

udp6 0 0 [:]:sunrpc [:]:*

udp6 0 0 [:]:757 [:]:*

udp6 0 0 localhost:323 [:]:*

活动UNIX 域套接字(服务器和已建立的)

[root@nginx-kafka01 ipv4]# netstat -tanpl

有效的互联网连接(服务器和已建立)

Proto Recv-Q Send-Q 本地地址外部地址状态PID/程序名称

tcp 0 0 0.0.0.0:3306 0.0.0.0:* 监听7117/mysqld

TCP 0 0 0.0.0.0:111 0.0.0.0:* 监听6102/rpcbind

tcp 0 0 0.0.0.0:80 0.0.0.0:* 监听6669/nginx: 主控

TCP 0 0 0.0.0.0:22 0.0.0.0:* 监听6640/sshd

TCP 0 0 127.0.0.1:25 0.0.0.0:* 监听7284/master

tcp 0 0 192.168.2.152:22 192.168.2.135:52102 建立7879/sshd: root@pts

tcp6 0 0 :111 :* 监听6102/rpcbind

tcp6 0 0 :3000 :* 监听7840/grafana-server

tcp6 0 0 :1:25 :* 监听7284/master

[root@nginx-kafka01 ipv4]# netstat -anlut

有效的互联网连接(服务器和已建立)

Proto Recv-Q Send-Q 本地地址外部地址状态

tcp 0 0 0.0.0.0:3306 0.0.0.0:* 请听

tcp 0 0 0.0.0.0:111 0.0.0.0:* 请听

tcp 0 0 0.0.0.0:80 0.0.0.0:* 请听

tcp 0 0 0.0.0.0:22 0.0.0.0:* 请听

tcp 0 0 127.0.0.1:25 0.0.0.0:* 请听

TCP 0 0 192.168.2.152:22 192.168.2.135:52102 已建立

tcp6 0 0 :111 :* 请听

tcp6 0 0 :3000 :* 请听

tcp6 0 0 :1:25 :* 请听

UDP 0 0 0.0.0.0:111 0.0.0.0:*

UDP 0 0 0.0.0.0:757 0.0.0.0:*

UDP 0 0 127.0.0.1:323 0.0.0.0:*

udp6 0 0 :111 :*

udp6 0 0 :757 :*

udp6 0 0 :1:323 :*

[root@nginx-kafka01 ipv4]# ss -lnt

状态Recv-Q Send-Q 本地地址: 端口对等地址: 端口

请听0 50 *:3306 *:*

请听0 128 *:111 *:*

请听0 128 *:80 *:*

请听0 128 *:22

                           *:*                  
LISTEN     0      100                     127.0.0.1:25                                          *:*                  
LISTEN     0      128                            :::111                                        :::*                  
LISTEN     0      128                            :::3000                                       :::*                  
LISTEN     0      100                           ::1:25                                         :::*         

 

recv0Q– send-Q

netstat和ss 看到的值是不一样的

netstat:其中,接收队列(Recv-Q)和发送队列(send-Q)需要你特别关注。

Recv-Q:Established: The count of bytes not copied by the user program connected to this socket. Listen‐:接收方已经发送了ack,但是用户程序还没有去拿:连接到此套接字的用户程序未复制的字节数。

Send-Q:发送的数据包。

syn backlog:是 TCP 协议栈中的半连接队列长度。

它们通常应该是0.当你发现它们不是0时,说明网络包的堆积发生。

1.5 三次握手中的常见问题

1、三次握手为什么是三次而不是两次?

为了保证数据能达到目标,TCP采用三次握手策略。三次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已经准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。最主要的目的就是双方都需要确认自己与对方的发送与接收都是正常的。

第一次握手:客户端:发送数据包,服务端收到了。服务端就能得知:客户端的发送能力、服务端的接收能力都是正常的。

第二次握手:服务端发送数据包,客户端收到了。客户端就能得出:服务端的接收、发送能力,客户端的接收、发送能力都是正常的。

第三次握手:客户端发送数据包,服务端接收到了。服务端就能得出:客户端的接收、发送能力正常,服务器自己的发送、接收能力也是正常的。

因此,需要三次握手才能确认双方的接收和发送能力是否正常。

现在假设将三次握手改为仅需要两次握手的话,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发送了确认应答分组。按照两次握手的协定,S认为连接已经是成功建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S是否已经准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成,将会忽略S发来的任何数据分组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。

2、如果已经建立了连接,但是客户端突然故障了怎么办?

TCP还设有一个保活计时器,显然,客户端如果出现了故障,服务器不能一直等下去,白白浪费资源。服务器每次收到一次客户端的请求之后都会重新复位这个计时器,时间通常是设置为2个小时,如果2小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。如果一连发送10个探测报文仍然没有反应,服务器就认为客户端出现了故障,接着就会关闭连接。

3、为什么三次握手,返回时,ack值是seq加1(ack=x+1)?

假设对方接收到数据,比如sequence number = 1000,TCP Payload = 1000,数据第一个字节编号为1000,最后一个为1999,回应一个确认报文,确认号为2000,意味着2000前的字节接收完成,准备接收编号为2000及更多的数据。

确认收到的序列,并且告诉发送端下一次发送的序列号从哪里开始(便于接收方数据排序,便于选择重传)

4、三次握手过程中可以携带数据吗?

其实第三次握手的时候,是可以携带数据的。但是。第一次、第二次握手不可以携带数据。因为如果第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的SYN报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后就疯狂重复的发送着SYN报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。也就是,第一次握手不可以放数据,其中的一个原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于ESTABLISHED状态,对于客户端来说,他已经建立起连接了,并且也知道服务器的接收、发送能力是正常的了,所以携带数据是可以的。

2、四次断开—断开连接
建立一个连接需要进行三次握手,而终止一个连接需要经过四次断开(断开连接),这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。这个连接可以是客户端主动断开的,也可以是因为超过连接时间了,服务端主动断开的。

2.1 四次断开流程图

1、四次断开

 2.2 四次断开过程详解

刚开始的时候,双方都处于established状态。假如是客户端先发起关闭连接请求。第一次断开:客户端发送一个FIN的标准位数据包(FIN=1,连接释放报文段)给服务器端,会在报文中会指定一个序列号seq=u,并停止发送数据,主动关闭TCP连接。此时的客户端处于FIN_WAIT1的状态,等待服务端的确认。

第二次断开:服务端收到FIN标志位数据包之后,会给客户端发送ACK报文(ACK=1:确认报文段),表明已经收到客户端的报文了,这个报文中是把客户端的序列号值进行加一(u+1)作为确认ACK报文的序列号值(ack=u+1:确认序列号),同时也会生成一个初始序列号seq=v。此时的服务器端处于 CLOSE_WAIT (关闭等待)状态。

此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。

第三次断开:如果服务器也想断开连接了(服务端没有要向客户端发出的数据),就会和客户端的第一次挥手一样,会给客户端发送一个FIN报文(FIN=1:连接释放报文,ACK=1),并且随机指定一个序列号seq=w(确认号为ack=u+1)。此时的服务端处于 LAST_ACK (最后确认)的状态,等待客户端的确认。

第四次断开:客户端收到FIN报文(连接释放报文段)之后,一样的发送一个ACK报文(ACK=1,seq=u+1:确认报文段)作为应答,并且把服务端的序列号值+1(ack=w+1)作为自己ACK报文的序列号值。此时的客户端处于TIME_WAIT (时间等待)状态,(此时的TCP没有被释放掉)需要经过一段时间(等待时间计时器设置的时间2MSL)以确保服务端收到自己的ACK报文之后才会进入CLOSED状态,服务器端收到ACK报文之后,就处于关闭连接了,处于CLOSED状态。

收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。

2.3 四次断开中的常见问题

1、timewait过多的情况

一般是客户端,nginx作为反向代理,它既是服务器又是客户端,可能出现。

(1)timewait多的原因是:访问量过大,客户机在访问新的页面。

(2)解决timewait过多情况:①增加机器。②设置防火墙 nginx limit限制  ③修改内核参数:将等待时间减小 。提高资源利用率,在等待时间不处理其他事情。

2、为什么连接的时候是三次握手,关闭的时候却是四次断开?

因为建立连接的时候,服务器在LISTEN状态下,当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文发送给客户端。其中ACK报文是用来应答的,SYN报文是用来同步的。

但是关闭连接时,当Server端收到对方发送的FIN报文时,告诉Client端,“你发的报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。

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

有些材料显示2msl的最大等待时间为2分钟。

虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可能最后一个ACK丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失;Server如果没有收到ACK,将不断重复发送FIN片段。所以Client不能立即关闭,它必须确认Server接收到该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。

4、服务器端会有一个TIME_WAIT状态吗?如果是服务器端主动断开连接呢?

发起连接的主动方基本都是客户端,但是断开连接的主动方服务器端和客户端都可以充当,也就是说,只要是主动断开连接的,就会有 TIME_WAIT状态。

四次断开是指断开一个TCP连接时,需要客户端和服务器端总共发送4个确认包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。

由于TCP连接时全双工的,因此,每个方向的数据传输通道都必须要单独进行关闭。
#以上关于TCP三次握手,四次断开详解的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/93364.html

(0)
CSDN's avatarCSDN
上一篇 2024年7月6日 上午11:51
下一篇 2024年7月6日 下午1:03

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注