一、面试中协议栈常问的点
1.tcp三次握手的过程?

客户端------>服务端:
将Syn位置1,发送自己的初始序列号seqnum,此位随机值,表示请求连接,并告知服务器自己的初始序列号。
服务端------>客户端:
将Syn和ACK位置1,将收到的客户端初始序列号加1作为确认号Acknum,和初始序列号seqnum发送到客户端,表示同意连接,并发送自己的初始序列号。
客户端------>服务器:
将ACK置1,将服务端发送过来的初始序列号加1,作为确认号发送到服务端,表示确认服务器序列号,连接正式建立。
其中syn只在第一次和第二次握手中被置1,syn标志的作用是再握手阶段用于初始序列号,交换双方的seq值,第三次握手客户端只需发送确认号,让客户端知道自己能够接收,同理ack只在第二次和第三次握手中置1。
三次握手过程中服务端的状态:
1.初始状态:LISTEN
-
服务器调用
listen()
后进入LISTEN
状态,等待客户端的SYN
报文。 -
关键行为:监听指定端口,准备接受连接请求。
2.收到SYN后:SYN_RCVD
-
当服务器收到客户端的
SYN
报文(第一次握手),会回复SYN+ACK
(第二次握手),并进入SYN_RCVD
状态。 -
含义 :已收到客户端的连接请求,正在等待客户端的最终
ACK
确认。 -
风险:此状态下服务器会为连接分配资源(如半连接队列),可能遭受SYN洪泛攻击(SYN Flood)
-
SYN泛洪攻击:
攻击原理:
-
攻击者发送大量伪造源 IP 的
SYN
报文,但不回复第三次握手的ACK
。 -
服务器在
SYN_RCVD
状态下维护半连接队列,资源被耗尽,无法响应正常请求。 -
防御措施:
-
SYN Cookies (Linux 默认启用):
不存储半连接状态,而是通过加密算法生成
SYN+ACK
的序列号,验证合法客户端的ACK
。bash
-
限制半连接队列大小:
sysctl -w net.ipv4.tcp_max_syn_backlog=2048
-
防火墙/IP 黑名单 :过滤高频
SYN
请求。
3.收到ACK后:ESTABLISHED
-
服务器收到客户端的
ACK
报文(第三次握手)后,确认连接建立,状态变为ESTABLISHED
。 -
含义:双方已同步序列号,可以开始正常数据传输。
关键注意事项
-
半连接队列(SYN Queue)
-
在
SYN_RCVD
状态下,连接会被放入半连接队列(未完全建立的连接队列)。 -
若队列满,新
SYN
可能被丢弃(触发客户端重传)。
-
-
全连接队列(Accept Queue)
-
进入
ESTABLISHED
后,连接被移至全连接队列,等待服务器调用accept()
处理。 -
若队列满且未及时
accept()
,可能导致客户端认为连接已建立,但服务器无法响应。
-
-
超时处理
- 若服务器未收到客户端的
ACK
(第三次握手),会重传SYN+ACK
(默认重试次数由系统配置,如Linux的tcp_synack_retries
)。
- 若服务器未收到客户端的
2.TCP四次挥手过程?

假设客户端主动发起关闭,服务端被动关闭:
客户端------>服务端(FIN):
发送FIN=1(终止标志),序列号为seq = u (客户端最后发送的数据序列号加1);
客户端状态:ESTABLISHED------>FIN_WAIT_1(等待服务端确认)。
含义:客户端主动请求关闭连接,但仍能接收服务端的数据。
服务端------>客户端(ACK):
发送ACK报文,ack置1,确认号为u+1;
服务端状态:ESTABLISHED------>CLOSE_WAIT,等待服务端应用层关闭;
客户端状态:FIN_WAIT_1------>FIN_WAIT_2,等待服务端的FIN;
关键点:此时服务端可能还有未发送完的数据。
服务端------>客户端(FIN):
服务端应用层调用close,将FIN置1,发送seq = v,v为最后的数据序列号加1;
服务端状态:CLOSE_WAIT------>LAST_ACK(等待客户端最后确认);
含义:服务端数据发送完毕,等待确认。
客户端------>服务端(ACK):
ACK置1,发送确认号ack = v+1;
客户端状态:FIN_WAIT_2------>TIME_WAIT(等待2MSL后进入CLOSED);
服务端状态:收到ack后进入CLOSED;
作用:确保服务端收到确认,处理可能残余报文。
关键问题解析
1. 为什么需要四次挥手?
-
TCP 是全双工协议,双方需独立关闭各自的通道:
-
客户端发
FIN
表示不再发送数据,但可接收数据(服务端可能还需发送剩余数据)。 -
服务端发
FIN
表示数据已发送完毕,双方才完全关闭。
-
2. TIME_WAIT 状态的作用
-
等待 2MSL(Maximum Segment Lifetime):
-
确保最后一个
ACK
能到达服务端(若丢失,服务端会重传FIN
)。 -
让网络中残留的旧报文失效,避免影响新连接。
-
-
默认时长:Linux 中通常为 60 秒。
3. CLOSE_WAIT 状态积压的隐患
-
若服务端长时间处于
CLOSE_WAIT
,可能是代码未调用close()
,导致资源泄漏。 -
排查方法 :通过
netstat -antp | grep CLOSE_WAIT
检查。
4. 异常场景
-
服务端不发送 FIN :客户端将永远卡在
FIN_WAIT_2
(可通过系统参数如tcp_fin_timeout
强制超时)。 -
客户端崩溃 :服务端在
LAST_ACK
状态超时后重传FIN
,最终关闭。
3.超时重传和快速重传
1. 超时重传(Timeout Retransmission)
触发条件
-
当发送方发送一个数据包后,如果在 RTO(Retransmission Timeout,重传超时时间) 内没有收到对应的 ACK 确认,就会触发超时重传。
-
RTO 计算 :基于 RTT(Round-Trip Time,往返时间)动态调整,通常使用 指数退避(Exponential Backoff) 策略(超时后 RTO 翻倍)。
工作流程
-
发送方发送数据包
Seq=1
,并启动 重传计时器。 -
如果 ACK 未在 RTO 时间内到达:
-
发送方认为数据包丢失,重传该数据包。
-
RTO 翻倍(避免网络拥塞时频繁重传)。
-
-
如果仍然没有 ACK,继续重传,直到达到最大重传次数(如 Linux 默认
tcp_retries2=15
)。
特点
-
保守策略 :等待超时后才重传,适用于 严重丢包 或 网络延迟突增 的情况。
-
效率较低:因为必须等待超时,可能导致传输延迟增加。
2. 快速重传(Fast Retransmit)
触发条件
-
当发送方 连续收到 3 个重复的 ACK(DupACK) 时,立即重传丢失的数据包,而不必等待超时。
-
为什么是 3 次?
- 1~2 次 DupACK 可能是网络乱序(如数据包顺序变化),但 3 次及以上 基本可以判定数据包丢失。
工作流程
-
发送方发送数据包
Seq=1, 2, 3, 4, 5
。 -
假设
Seq=2
丢失,接收方收到Seq=3, 4, 5
时,每次都会回复ACK=2
(期望收到Seq=2
)。 -
发送方收到 3 个
ACK=2
,立即 重传Seq=2
(快速重传)。 -
接收方收到
Seq=2
后,回复ACK=6
(确认所有数据)。
特点
-
快速恢复:比超时重传更快,减少等待时间。
-
依赖 DupACK:仅适用于部分丢包(如果整个窗口丢失,可能不会触发)。
-
通常结合快速恢复(Fast Recovery):
- 重传后,发送方不会直接进入慢启动,而是调整拥塞窗口(
cwnd
),提高传输效率。
- 重传后,发送方不会直接进入慢启动,而是调整拥塞窗口(
对比总结
机制 | 触发条件 | 优点 | 缺点 | 适用场景 |
---|---|---|---|---|
超时重传 | 数据包 ACK 超时(RTO) | 适用于严重丢包 | 延迟高,效率低 | 网络严重拥塞 |
快速重传 | 收到 3 个重复 ACK(DupACK) | 快速恢复,减少等待时间 | 依赖部分丢包情况 | 少量丢包、乱序 |
实际应用
-
超时重传 是 TCP 最基础的重传机制,但效率较低。
-
快速重传 + 快速恢复 是 TCP 优化的重要手段(如 Reno、Cubic 算法 均采用)。
-
现代 TCP(如 SACK)可以更精确地识别丢失的数据包,减少不必要的重传。
扩展:SACK(Selective Acknowledgment)
-
允许接收方 明确告知发送方哪些数据包丢失(而不是仅靠 DupACK)。
-
进一步优化快速重传,减少重传冗余数据。
总结
-
超时重传:兜底机制,确保数据最终送达,但延迟高。
-
快速重传:优化机制,减少等待时间,提高传输效率。
-
两者结合,使 TCP 在保证可靠性的同时,尽量提高传输速度。
4.TCP首部长度,有哪些字段
TCP 首部长度通常为 20 字节(最小长度) ,但如果包含 选项(Options) ,最大可扩展至 60 字节。其结构如下:
1. TCP 首部字段(固定部分,20 字节)
字段名 | 位数 | 说明 |
---|---|---|
源端口(Source Port) | 16 | 发送方的端口号(范围:0~65535)。 |
目的端口(Destination Port) | 16 | 接收方的端口号(如 HTTP=80, HTTPS=443)。 |
序列号(Sequence Number) | 32 | 当前数据包的第一个字节的编号(用于数据排序和重组)。 |
确认号(Acknowledgment Number) | 32 | 期望收到的下一个字节的编号(ACK=1 时有效)。 |
数据偏移(Data Offset) | 4 | TCP 首部长度(单位:4 字节),最小 5(20 字节),最大 15(60 字节)。 |
保留(Reserved) | 6 | 保留字段,必须设为 0。 |
控制标志(Flags) | 6 | 用于控制连接状态(见下表)。 |
窗口大小(Window Size) | 16 | 接收方的可用缓冲区大小(流量控制,单位:字节)。 |
校验和(Checksum) | 16 | 校验 TCP 首部和数据部分(防止数据损坏)。 |
紧急指针(Urgent Pointer) | 16 | 仅当 URG=1 时有效,指向紧急数据的末尾(很少使用)。 |
2. 控制标志(Flags,6 位)
标志位 | 名称 | 说明 |
---|---|---|
URG | Urgent | 紧急数据标志(需配合 Urgent Pointer 使用)。 |
ACK | Acknowledgment | 确认标志(Acknowledgment Number 有效)。 |
PSH | Push | 接收方应立即将数据交给应用层(避免缓冲延迟)。 |
RST | Reset | 强制断开连接(异常终止)。 |
SYN | Synchronize | 同步序列号(用于三次握手建立连接)。 |
FIN | Finish | 终止连接(用于四次挥手关闭连接)。 |
3. TCP 选项(Options,可选,最多 40 字节)
如果 数据偏移(Data Offset)> 5,则包含选项字段,常见选项有:
选项类型 | 说明 |
---|---|
MSS(Maximum Segment Size) | 协商最大报文段长度(通常 1460 字节,避免 IP 分片)。 |
Window Scale(WSOPT) | 扩展窗口大小(用于高带宽网络,突破 65535 字节限制)。 |
SACK(Selective Acknowledgment) | 选择性确认,优化重传机制(仅重传丢失的数据包)。 |
Timestamp(TSOPT) | 时间戳,用于计算 RTT(Round-Trip Time)和防止序列号回绕(PAWS)。 |
NOP(No Operation) | 填充选项,用于对齐 4 字节边界。 |
4. TCP 首部示例

5. 关键字段详解
(1) 数据偏移(Data Offset)
-
表示 TCP 首部长度 ,单位是 4 字节。
-
最小值 5(20 字节),最大值 15(60 字节)。
-
例如:
-
Data Offset = 5
→ 首部 20 字节(无选项)。 -
Data Offset = 8
→ 首部 32 字节(含 12 字节选项)。
-
(2) 序列号(Sequence Number)和确认号(Acknowledgment Number)
-
序列号 :当前数据包的第一个字节的编号(随机初始化,防止预测攻击)。
-
确认号 :期望收到的下一个字节的编号 (
ACK=1
时有效)。
(3) 窗口大小(Window Size)
-
用于 流量控制,表示接收方当前可接收的数据量(单位:字节)。
-
如果窗口为 0,发送方必须暂停传输(避免接收方缓冲区溢出)。
6. 总结
字段 | 作用 |
---|---|
源/目的端口 | 标识通信的应用程序。 |
序列号/确认号 | 保证数据有序和可靠传输。 |
数据偏移 | 确定 TCP 首部长度。 |
控制标志(SYN/ACK/FIN/RST) | 管理连接状态(握手、挥手、复位)。 |
窗口大小 | 流量控制,防止接收方过载。 |
校验和 | 检测数据是否损坏。 |
选项(MSS/SACK/Timestamp) | 优化 TCP 性能(如提高传输效率、减少重传)。 |
TCP 首部设计兼顾了 可靠性、流量控制、拥塞控制,是现代网络通信的基石。
5.TCP在listen时的参数backlog的意义
backlog
参数在 TCP 协议栈的发展过程中经历了多次调整和优化,其语义和实现方式在不同操作系统和内核版本中有所变化。以下是其演进历程:
1. 早期实现(BSD 4.2,1983年)
-
初始定义 :
backlog
表示 半连接队列(SYN Queue)和全连接队列(Accept Queue)的总和上限。 -
问题:
-
未严格区分两种队列,导致资源分配不合理。
-
易受 SYN Flood 攻击(半连接队列被恶意占满)。
-
2. BSD 4.3(1986年)的改进
-
分离队列 :明确区分 半连接队列 和 全连接队列。
-
半连接队列:存储
SYN_RCVD
状态的连接。 -
全连接队列:存储
ESTABLISHED
状态的连接(等待accept()
)。
-
-
backlog
语义 :仅表示 全连接队列的最大长度。 -
新增限制 :半连接队列大小由系统参数单独控制(如
net.ipv4.tcp_max_syn_backlog
)。
3. Linux 的演进(1990s~2000s)
(1) Linux 2.2 之前
-
行为类似 BSD :
backlog
控制全连接队列。 -
问题:高并发场景下队列易溢出。
(2) Linux 2.2+ 的优化
-
引入
somaxconn
:-
全连接队列的实际大小为
min(backlog, somaxconn)
。 -
默认值:
somaxconn=128
(可通过/proc/sys/net/core/somaxconn
调整)。
-
-
半连接队列独立控制:
-
由
net.ipv4.tcp_max_syn_backlog
定义(默认 256)。 -
启用
SYN Cookies
(net.ipv4.tcp_syncookies=1
)后可动态扩展半连接队列。
-
(3) Linux 4.3+ 的进一步改进
-
backlog
的弹性扩展:- 当全连接队列满时,内核可能临时扩大队列(避免直接丢弃连接)。
-
多队列支持:
- 在多核环境下,采用 per-CPU 队列减少锁竞争。