Appearance
TCP、UDP 与 DNS
问题
TCP 和 UDP 有什么区别?TCP 三次握手/四次挥手是怎么进行的?DNS 如何解析域名?OSI 七层模型和 TCP/IP 五层模型分别是什么?
结论
TCP vs UDP 对比
| 维度 | UDP | TCP |
|---|---|---|
| 连接 | 无连接(直接发) | 面向连接(三次握手) |
| 可靠性 | 不可靠(不确认、不重传) | 可靠(确认、重传、排序) |
| 传输对象 | 一对一/一对多/多对多 | 只支持一对一 |
| 传输方式 | 面向报文(保留边界) | 面向字节流(无边界) |
| 头部开销 | 8 字节 | 最小 20 字节 |
| 速度 | 快(无握手和确认开销) | 慢(控制机制多) |
| 适用场景 | 实时音视频、直播、DNS 查询 | 文件传输、网页、数据库 |
为什么 UDP 不可靠:发送后不确认、不重传、无超时、不排序、无拥塞控制,"发出去就完事了"。
TCP 三次握手
建立 TCP 连接需要 3 次包交换,目的是双方确认各自的发送/接收能力正常,并同步初始序号(ISN):
客户端 服务端
| → SYN (seq=x) | 第1次:客户端发起连接请求
| ← SYN+ACK (seq=y, ack=x+1) | 第2次:服务端确认并同步自己的序号
| → ACK (ack=y+1) | 第3次:客户端确认服务端的序号
| 双方 ESTABLISHED |为什么需要三次?两次不够:
- 两次握手时服务端无法确认自己的序号是否被客户端收到
- 若第一次 SYN 在网络中延误,到连接释放后才到达,服务端若直接建立连接会造成资源浪费
TCP 四次挥手
关闭 TCP 连接需要 4 次包交换,因为 TCP 是全双工的,双方各需独立关闭自己的发送方向:
客户端(先关) 服务端
| → FIN (seq=u) | 第1次:客户端要求断开(停止发数据)
| ← ACK (ack=u+1) | 第2次:服务端确认(服务端仍可能发数据)
| ← FIN (seq=w, ack=u+1) | 第3次:服务端数据发完后,也要求断开
| → ACK (ack=w+1) | 第4次:客户端确认(进入 TIME-WAIT)
| 2MSL 后 → CLOSED |TIME-WAIT 状态:客户端第 4 次挥手后等待 2MSL(最大报文生存时间),防止最后一个 ACK 丢失后服务端重发 FIN 找不到应答。
为什么需要四次?:服务端收到 FIN 时可能还有数据要发,必须先 ACK 确认,等数据发完再发 FIN,所以无法合并。
DNS 协议
作用:将域名转换为 IP 地址(应用层协议,53 端口)。
TCP 还是 UDP:
- DNS 查询用 UDP:响应包通常 < 512 字节,无需三次握手,响应更快
- 区域传送(同步数据库)用 TCP:数据量大,需要可靠传输
DNS 完整查询过程(7步):
- 浏览器缓存查找
- 操作系统缓存(hosts 文件等)
- 本地 DNS 服务器缓存查找
- 本地 DNS 向根域名服务器查询,得到顶级域名服务器(TLD)地址
- 向 TLD 服务器查询(如
.com),得到权威域名服务器地址 - 向权威域名服务器查询,得到目标 IP 地址
- 本地 DNS 缓存结果并返回给浏览器
递归查询 vs 迭代查询:
- 用户 → 本地 DNS:递归查询(用户只发一次请求,本地 DNS 代为查询到底)
- 本地 DNS → 各级 DNS:迭代查询(每次只返回下一级 DNS 地址,本地 DNS 自己继续查)
OSI 七层模型 vs TCP/IP 五层模型
| OSI 七层 | TCP/IP 五层 | 典型协议 |
|---|---|---|
| 应用层 | 应用层 | HTTP、HTTPS、FTP、DNS、SMTP |
| 表示层 | ↑(合并入应用层) | SSL/TLS、编码/压缩 |
| 会话层 | ↑(合并入应用层) | — |
| 传输层 | 传输层 | TCP、UDP |
| 网络层 | 网络层 | IP、ICMP |
| 数据链路层 | 数据链路层 | Ethernet、Wi-Fi(IEEE 802.11) |
| 物理层 | 物理层 | 光纤、网线、无线电信号 |
TCP/IP 五层将 OSI 的应用层/表示层/会话层合并为应用层,更符合实际网络实现。
常见面试问法:HTTP 是应用层协议,基于传输层的 TCP;IP 是网络层协议;TCP/UDP 工作在传输层;浏览器 URL 解析 → DNS → TCP → HTTP 是跨三层协议的协作。
TCP 的重传机制
TCP 下层网络可能出现丢失、重复或失序,TCP 提供可靠传输服务。TCP 使用两套机制完成重传:
- 基于时间:TCP 发送数据后开启定时器,若在规定时间内未收到 ACK 确认,则对该报文重传;达到一定次数仍失败则发送复位信号。
- 基于确认信息:收到 3 个重复 ACK(快速重传),立即重传而不等待定时器超时。
TCP 的拥塞控制机制
TCP 拥塞控制主要有四种机制:
① 慢启动(Slow Start)
- 初始拥塞窗口 cwnd = 1,指数增长(每 RTT 翻倍)
- 设置慢启动门限(ssthresh):cwnd < ssthresh 时用慢启动,cwnd >= ssthresh 时改用拥塞避免
② 拥塞避免
- cwnd 线性增长(每 RTT 加 1),使网络不容易出现拥塞
- 一旦检测到拥塞(超时),ssthresh = cwnd / 2,cwnd 重置为 1,重新慢启动
③ 快速重传(Fast Retransmit)
- 接收方收到乱序报文段后立即发送重复确认(Duplicate ACK)
- 发送方连续收到 3 个重复 ACK 后,立即重传丢失的报文段,无需等待定时器
④ 快速恢复(Fast Recovery)
- 收到 3 个重复 ACK 后:ssthresh = cwnd / 2,cwnd = ssthresh(不重置为 1)
- 直接进入拥塞避免阶段,避免慢启动带来的吞吐量骤降
TCP 的流量控制机制
流量控制的目的是让发送方不要发太快,让接收方来得及接收。TCP 采用滑动窗口机制:
- 连接建立时,双方各分配缓冲区并将大小告知对方(接收窗口)
- 数据到达时,接收方在 ACK 中携带剩余缓冲区大小(窗口通告)
- 若接收方缓冲区满,通告零窗口,发送方必须停止发送,直到收到正窗口通告
- 发送窗口大小 = min(接收窗口, 拥塞窗口)
TCP 的可靠传输机制
TCP 可靠传输基于连续 ARQ 协议和滑动窗口协议:
- 发送方维护发送窗口,依次发送窗口内所有报文段,并设置定时器
- 收到 ACK 后滑动窗口;定时器超时则重传所有已发但未确认的报文段(超时间隔翻倍)
- 收到 3 个冗余 ACK 触发快速重传
- 接收方使用累计确认:ACK 表示该序号之前的报文段均已按序到达
- 实际 TCP 会缓存乱序报文段,丢包重传时只重传丢失的那一个(结合选择重传)
TCP 粘包是怎么回事,如何处理
TCP 默认启用 Nagle 算法(延迟传送),将短时间内多次发送的数据缓冲合并一次发出,导致接收端可能一次收到多个消息合并在一起(粘包)。
粘包情况:
- 发送 data1、data2,可能接收到的是 data1 的部分 + data2 全部(或其他组合)
处理方案:
- 发送间隔等待:两次发送之间加等待时间(适用于低频交互,性能差)
- 关闭 Nagle 算法:通过
socket.setNoDelay()禁用,让每次发送立即传出(适用于数据量较大、频率不高的场景) - 封包/拆包:在数据包前后加特征标识符,接收端根据特征标识拆分各数据包(业界主流方案)
为什么 UDP 不会粘包
- TCP 是面向流协议,数据无边界,Nagle 等机制会合并小包,接收端无法区分消息边界
- UDP 是面向消息协议,每个 UDP 报文就是一条独立消息,有消息头(来源地址、端口等),接收端以消息为单位提取,天然有消息边界
- UDP 接收端一次只能接收发送端发出的一个数据包,数据过大会丢失部分,但不会"合并"
DNS 记录和报文
DNS 服务器以**资源记录(Resource Record)**形式存储信息,格式为:
(Name, Value, Type, TTL)TTL 是资源记录的生存时间,定义了其他 DNS 服务器可以缓存该记录多久。
常用 Type 值:
| Type | Name | Value | 说明 |
|---|---|---|---|
| A | 主机名 | IP 地址 | 标准的主机名到 IP 映射 |
| NS | 域名 | 负责该域名的 DNS 服务器主机名 | DNS 链式查询时指向下一级 DNS |
| CNAME | 别名 | 规范主机名 | 为复杂主机名提供便于记忆的别名 |
| MX | 邮件服务器别名 | 邮件服务器规范主机名 | 邮件路由 |