中卫市建设局网站 冯进强一般开车用什么导航最好
TCP连接断开
四次挥手
-
四次挥手过程
-
客户端发送FIN报文,客户端进入
FIN_WAIT_1
状态。 -
服务端接收报文,发送ACK报文,服务端进入
CLOSE_WAIT
状态。 -
客户端收到ACK报文,进入
FIN_WAIT_2
状态。 -
服务端处理完数据后,也发送FIN报文,服务端进入
LAST_ACK
状态。 -
客户端接收报文,发送ACK报文,进入
TIME_WAIT
状态。 -
服务器收到ACK报文,进入
CLOSE
状态。至此,服务器关闭连接。 -
客户端在经过2MSL时间后,自动进入
CLOSE
状态。至此,客户端关闭连接。- 主动关闭连接的一方才有TIME_WAIT状态。
-
-
为什么是四次挥手?不是三次?
在关闭连接时,客户端向服务器发送FIN,仅仅表示客户端不再发送数据,但是可以接收数据。
服务器收到FIN报文时,先回复ACK应答报文,而服务器可能还有数据要处理和发送,等服务器端不再发送数据时,才发送FIN报文给客户端表示同意关闭连接。
所以,因为服务端要等待完成数据的发送和处理,所以服务端的ACK 和FIN是分开发送的,所以需要四次挥手。
-
挥手丢失会发送什么?
-
第一次挥手丢失,客户端收不到ACK,会触发超时重传机制,重传FIN报文。
- 重发次数由
tcp_orphan_retries
参数控制。当客户端重传 FIN 报文的次数超过tcp_orphan_retries
后,就不再发送 FIN 报文,则会在等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到第二次挥手,那么直接进入到close
状态。
- 重发次数由
-
第二次挥手丢失,客户端收不到ACK,会触发超时重传机制,重传FIN报文。(ACK报文不会重传)
-
如果关闭方调用close()函数关闭连接,FIN_WAIT2状态不可以持续太久,而
tcp_fin_timeout
控制了这个状态下连接的持续时长,默认值是 60 秒。 -
如果关闭方调用shutdown()函数关闭连接,指定了之关闭发送方向,而接收方向没有关闭,那么意味着主动关闭方还是可以接收数据的。此时,如果主动关闭方一直没收到第三次挥手,那么主动关闭方的连接将会一直处于
FIN_WAIT2
状态
-
-
第三次挥手丢失,服务端收不到ACK,会触发超时重传机制,服务端重传FIN报文。
-
当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于
CLOSE_WAIT
状态,顾名思义,它表示等待应用进程调用 close 函数关闭连接。此时,内核是没有权利替代进程关闭连接,必须由进程主动调用 close 函数来触发服务端发送 FIN 报文。
服务端处于 CLOSE_WAIT 状态时,调用了 close 函数,内核就会发出 FIN 报文,同时连接进入 LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。
-
-
第四次挥手丢失,服务端收不到ACK,会触发超时重传机制,服务端重传FIN报文。
- 当客户端收到服务端的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入
TIME_WAIT
状态。在 Linux 系统,TIME_WAIT 状态会持续 2MSL 后才会进入关闭状态。
- 当客户端收到服务端的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入
-
Socket编程
-
针对TCP如何Socket编程
- 服务端和客户端初始化
socket
,得到文件描述符; - 服务端调用
bind
,将 socket 绑定在指定的 IP 地址和端口;- 此时的Socket是监听Socket。
- 服务端调用
listen
,进行监听; - 服务端调用
accept
,等待客户端连接; - 客户端调用
connect
,向服务端的地址和端口发起连接请求; - 服务端
accept
返回用于传输的socket
的文件描述符;- 此时的Socket是已完成连接的Socket,用来传输数据。
- 客户端调用
write
写入数据;服务端调用read
读取数据; - 客户端断开连接时,会调用
close
,那么服务端read
读取数据的时候,就会读取到了EOF
,待处理完数据后,服务端调用close
,表示连接关闭。
- 服务端和客户端初始化
-
listen时候参数backlog的意义?
int listen (int socketfd, int backlog) // socketfd 为 socketfd 文件描述符 // backlog ,参数随着历史版本变化
在早期 Linux 内核 backlog 是 SYN 队列大小,也就是未完成的队列大小。
在 Linux 内核 2.2 之后,backlog 变成 accept 队列,也就是已完成连接建立的队列长度,所以现在通常认为 backlog 是 accept 队列。
但是上限值是内核参数 somaxconn 的大小,也就说 accpet 队列长度 = min(backlog, somaxconn)。
-
accept发生在三次握手的哪一步?
由图片可见,connect返回成功(单向连接建立成功)是在第二次握手之后,服务端accept成功返回是在三次握手成功之后。
上图的具体步骤
- 客户端的协议栈向服务端发送了 SYN 包,并告诉服务端当前发送序列号 client_isn,客户端进入 SYN_SENT 状态;
- 服务端的协议栈收到这个包之后,和客户端进行 ACK 应答,应答的值为 client_isn+1,表示对 SYN 包 client_isn 的确认,同时服务端也发送一个 SYN 包,告诉客户端当前我的发送序列号为 server_isn,服务端进入 SYN_RCVD 状态;
- 客户端协议栈收到 ACK 之后,使得应用程序从
connect
调用返回,表示客户端到服务端的单向连接建立成功,客户端的状态为 ESTABLISHED,同时客户端协议栈也会对服务端的 SYN 包进行应答,应答数据为 server_isn+1; - ACK 应答包到达服务端后,服务端的 TCP 连接进入 ESTABLISHED 状态,同时服务端协议栈使得
accept
阻塞调用返回,这个时候服务端到客户端的单向连接也建立成功。至此,客户端与服务端两个方向的连接都建立成功。
-
客户端调用close,连接断开的流程是什么?
调用close(),说明没有数据要传输了,服务端收到FIN后调用会read,返回EOF,当服务器端发送FIN,服务端会调用close(),客户端收到后进入2MSL的计时,经过2MSL后,进入CLOSE状态。
-
没有accept,可以建立TCP连接吗?
accpet 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accpet 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。
所以就算不执行accept()方法,三次握手照常进行,并且会顺利建立连接。
-
没有listen,可以建立TCP连接吗?
客户端是可以自己连自己的形成连接(TCP自连接),也可以两个客户端同时向对方发出请求建立连接(TCP同时打开),这两个情况都有个共同点,就是没有服务端参与,也就是没有 listen,就能 TCP 建立连接。