计算机网络面试题¶
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:恶意脚本存入数据库(如评论区),每次页面加载都执行
-
反射型XSS:恶意脚本在URL中,服务器将其反射回页面
-
DOM型XSS:通过修改DOM结构在客户端执行恶意脚本
防御方法: 1. 输入过滤和输出编码:对用户输入进行HTML实体编码
2. CSP(Content Security Policy):限制资源加载来源 3. HttpOnly Cookie:防止JavaScript读取Cookie 4. 使用安全的模板引擎(自动转义)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. 参数化查询(预编译语句):最有效的防御方式
2. ORM框架:使用ORM避免直接写SQL 3. 输入验证:白名单过滤,限制输入类型和长度 4. 最小权限:数据库用户使用最小必要权限 5. WAF(Web应用防火墙):过滤恶意SQL36. 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);
poll:
struct pollfd fds[MAX]; // struct结构体:自定义复合数据类型
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, nfds, timeout);
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为什么安全"等
最后更新:2025年