TCP 连接的建立和断开,是网络面试题的常客,也是排查线上问题时绕不开的基础。这篇文章把过程拆开,配上抓包命令,看完应该就不用死记硬背了。
三次握手:双方确认彼此能收发
握手的目的很简单——客户端和服务器互相确认:我能发、你能收,反过来也一样。两次不够(服务器不知道客户端能不能收到回复),四次多余,所以是三次。
具体过程:
- 客户端发 SYN
发一个 SYN 包,带上初始序列号 seq=x,自己进入 SYN_SENT 状态。意思是"我想连你"。
- 服务器回 SYN+ACK
服务器收到后回一个 SYN+ACK,seq=y, ack=x+1,进入 SYN_RCVD。意思是"收到了,我也想连你"。
- 客户端回 ACK
客户端再发 ACK,seq=x+1, ack=y+1,双方都进入 ESTABLISHED。连接建好了。
想亲眼看这个过程,用 tcpdump 抓一下:
tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn) != 0'
四次挥手:双方各关各的通道
TCP 是全双工的,收和发是两条独立的通道。关连接时,每条通道各关一次,一共四次。
- 客户端发 FIN
客户端说"我这边发完了",发 FIN 包,进入 FIN_WAIT_1。
- 服务器回 ACK
服务器说"知道了",发 ACK,自己进入 CLOSE_WAIT。客户端收到后进入 FIN_WAIT_2。
- 服务器发 FIN
服务器把剩余数据发完,也发 FIN,进入 LAST_ACK。
- 客户端回 ACK
客户端发最后一个 ACK,进入 TIME_WAIT,等 2 个 MSL(最大报文生存时间)后彻底关闭。
线上常见的两个坑
TIME_WAIT 堆积
短连接多的服务(比如 HTTP 1.0 没 keep-alive),关连接后会留大量 TIME_WAIT,占着端口不放。调这两个内核参数能缓解:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
SYN Flood 攻击
攻击者伪造源 IP 发大量 SYN,服务器的半连接队列被塞满,正常用户连不上。开 syncookies 可以扛一扛:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 8192
快速查看当前连接状态
# 统计各状态的连接数
netstat -ant | awk '{print $6}' | sort | uniq -c
# ss 更快,推荐
ss -s
如果看到 CLOSE_WAIT 很多,说明你的程序没正确关闭连接,去查代码里 socket 有没有 close。TIME_WAIT 多则是正常现象,数量太大才需要处理。
评论
暂无评论