计算机网络面试题

嵌入式面试中网络知识越来越重要(IoT/车联网/工业以太网)。本章覆盖TCP/IP协议栈、Socket编程、HTTP/DNS等高频考点。 侧重嵌入式相关内容(lwIP/MQTT/CoAP),配秒懂和代码。


★ 网络核心概念图解

◆ TCP/IP四层模型

  • 应用层(HTTP/MQTT)是你写的信
  • 传输层(TCP/UDP)是快递公司,保证送达或只管发
  • 网络层(IP)是路由规划走哪条路
  • 链路层(以太网/WiFi)是具体的卡车和公路
Terminal window
1
┌──────────────┬─────────────┬──────────────────────┐
2
OSI七层 TCP/IP四层 典型协议
3
├──────────────┼─────────────┼──────────────────────┤
4
应用/表示/会话│ 应用层 HTTP/MQTT/DNS/DHCP // MQTT协议
5
传输层 传输层 TCP/UDP
6
网络层 网络层 IP/ICMP/ARP
7
数据链路/物理│ 链路层 以太网/WiFi/PPP
8
└──────────────┴─────────────┴──────────────────────┘
9
10
数据封装:
11
应用数据 [TCP头|数据] [IP头|TCP头|数据] [帧头|IP头|TCP头|数据|帧尾]
12
段(Segment) 包(Packet) 帧(Frame)

★ 网络协议栈与嵌入式全景对比

Terminal window
1
OSI七层 vs TCP/IP四层 vs 嵌入式常用:
2
3
OSI七层 TCP/IP四层 嵌入式常用
4
┌──────────┐
5
应用层 应用层 MQTT/CoAP/HTTP/cJSON // MQTT协议
6
表示层 Protobuf/JSON
7
会话层 TLS/SSL(mbedTLS)
8
├──────────┤ ─────
9
传输层 传输层 TCP/UDP
10
├──────────┤ ─────
11
网络层 网络层 IP/ICMP/ARP
12
├──────────┤ ─────
13
数据链路层│ 网络接口层 以太网/WiFi/BLE
14
物理层 PHY/RJ45/天线
15
└──────────┘
10 collapsed lines
16
17
嵌入式网络方案选型:
18
┌──────────┬──────────┬──────────┬──────────┐
19
方案 协议栈 适用MCU 典型芯片
20
├──────────┼──────────┼──────────┼──────────┤
21
自带协议栈│ W5500内部 任意MCU W5500
22
lwIP 软件协议栈│ ≥64KB RAM│ STM32+PHY│
23
AT指令 模组内部 任意MCU ESP8266
24
Linux 内核协议栈│ Cortex-A i.MX6
25
└──────────┴──────────┴──────────┴──────────┘

一、TCP/UDP基础(Q1~Q20)

Q1: TCP和UDP的区别?

🧠 秒懂: TCP是电话(先拨号连接→可靠通话→挂断),UDP是寄信(写好地址直接扔邮筒,不保证送达)。TCP可靠但慢,UDP快但可能丢。嵌入式中控制命令用TCP,实时音视频用UDP。

TCPUDP
连接面向连接(三次握手)无连接
可靠可靠(重传/排序/流控)不可靠(尽力而为)
有序保证顺序不保证顺序
速度慢(有开销)快★
头部20字节8字节
传输字节流数据报(有边界)
广播不支持支持★
适合文件传输/Web/OTADNS/NTP/视频/MQTT(QoS0)

嵌入式选择:资源受限的MCU用UDP更轻量(如CoAP);需要可靠的用TCP(如OTA固件下载)。

💡 面试追问: OSI七层和TCP/IP四层怎么对应?每层的代表协议? 🔧 嵌入式建议: 嵌入式工程师重点掌握:传输层(TCP/UDP)+应用层(MQTT/HTTP)+物理层(以太网/WiFi/蓝牙)。


📊 OSI七层 vs TCP/IP四层 对比

OSI七层TCP/IP四层功能典型协议/设备
应用层应用层用户接口/数据表示HTTP/MQTT/CoAP
表示层加密/压缩/编码SSL/TLS/JSON
会话层会话管理-
传输层传输层端到端可靠传输TCP/UDP
网络层网际层路由/寻址IP/ICMP/ARP
数据链路层网络接口层帧封装/MAC寻址Ethernet/WiFi
物理层比特流传输网线/光纤/无线

Q2: TCP三次握手?

🧠 秒懂: 三次握手建立连接:①客户端发SYN ②服务器回SYN+ACK ③客户端发ACK。三次的目的是确认双方都能收发——少于三次无法确认,多于三次浪费。

1
Client Server
2
│ │
3
│──── SYN, seq=x ──────────→ │ ① 客户端发SYN
4
│ │
5
│←── SYN+ACK, seq=y, ack=x+1 ──│ ② 服务器回SYN+ACK
6
│ │
7
│──── ACK, ack=y+1 ────────→ │ ③ 客户端发ACK
8
│ │
9
│ ═══════ 连接建立 ═══════ │
10
11
为什么是三次而不是两次?
12
两次: 服务器收到SYN就确认连接
13
问题: 如果是一个"历史SYN"(网络延迟很久才到达)
14
→ 服务器建立了错误的连接 → 浪费资源
15
三次: 客户端确认ACK → 证明这个SYN是"新鲜的"

Q3: TCP四次挥手?

🧠 秒懂: 四次挥手关闭连接:①主动方发FIN ②被动方回ACK(半关闭) ③被动方发FIN ④主动方回ACK。比建立多一次是因为被动方可能还有数据没发完,需要分两步关闭。

四次挥手的时序和TIME_WAIT状态是网络面试必考内容:

1
Client Server
2
│ │
3
│──── FIN, seq=u ──────────→ │ ① 客户端请求关闭
4
│ │
5
│←── ACK, ack=u+1 ───────── │ ② 服务器确认(但可能还有数据要发)
6
│ │
7
│←── FIN, seq=v ─────────── │ ③ 服务器也请求关闭
8
│ │
9
│──── ACK, ack=v+1 ────────→ │ ④ 客户端确认
10
│ │
11
│ ═══ TIME_WAIT(2MSL) ═══ │ 客户端等2MSL后释放
12
│ │
13
14
为什么要TIME_WAIT?
15
① 如果最后的ACK丢了,服务器会重发FIN → 客户端能重发ACK
2 collapsed lines
16
② 确保旧连接的数据包在网络中消失(不影响新连接)
17
MSL = Maximum Segment Lifetime ≈ 2分钟

💡 面试追问: 三次握手每步做了什么?为什么不是两次?SYN Flood攻击原理? 🔧 嵌入式建议: 嵌入式TCP编程必须处理:连接超时/重连机制/心跳保活/半开连接检测。

Q4: TCP的可靠性如何保证?

🧠 秒懂: TCP可靠性靠:序列号(保证顺序)、确认应答(保证到达)、超时重传(丢了再发)、校验和(保证完整)、流量控制(不溢出)、拥塞控制(不堵网)。六大机制缺一不可。

TCP通过多种机制保证数据可靠传输,面试中需要能列举核心机制:

机制作用
序列号/确认号保证数据有序、检测丢包
超时重传丢包后自动重发
滑动窗口流量控制,防止接收方溢出
拥塞控制防止网络拥塞崩溃
校验和检测数据损坏
快速重传3个重复ACK立即重传
1
发送方 接收方
2
│ ─── Seq=1, Data ──→ │
3
│ ─── Seq=2, Data ──→ (丢失)│
4
│ ─── Seq=3, Data ──→ │
5
│ ←── ACK=2(期望收到2) ──── │
6
│ ←── ACK=2(重复ACK) ───── │
7
│ ←── ACK=2(重复ACK) ───── │
8
│ ─── Seq=2, 重传! ──→ │ ← 快速重传(3个重复ACK)
9
│ ←── ACK=4 ──────────────── │ ← 确认收到1,2,3

Q5: TCP滑动窗口机制?

🧠 秒懂: 滑动窗口控制发送方一次能发多少未确认的数据。接收方通过窗口大小告诉发送方’我还能收N字节’。窗口=0时暂停发送。实现了流量控制——发送速度适配接收能力。

滑动窗口实现流量控制,限制发送速率使接收方不溢出:

1
发送窗口(发送方维护):
2
┌────────┬────────────────┬────────────┬──────────┐
3
│已确认 │已发送未确认 │可发送 │不可发送 │
4
└────────┴────────────────┴────────────┴──────────┘
5
└────────────── 窗口大小 ──────────────┘
6
7
接收窗口(接收方通告):
8
TCP头部 Window字段 → 告诉发送方"我还能接收多少数据"
9
10
窗口为0: 发送方停止发送(零窗口探测定时器)
11
窗口缩小: 出现Silly Window Syndrome(小段数据) → Nagle算法优化

💡 面试追问: TCP和UDP各适合什么场景?MQTT用TCP还是UDP?视频流呢? 🔧 嵌入式建议: IoT设备首选MQTT(基于TCP,轻量);传感器突发上报用UDP;音视频用RTP/UDP。


📊 TCP vs UDP 对比表

特性TCPUDP
连接面向连接(三次握手)无连接
可靠性可靠(确认重传)不可靠(尽力交付)
顺序有序(序列号)无序
流量控制滑动窗口
拥塞控制有(慢启动等)
头部开销20字节8字节
传输效率较低(确认机制)
嵌入式场景HTTP/MQTT/OTA升级DNS/NTP/音视频流/IoT上报

Q6: TCP拥塞控制算法?

🧠 秒懂: 慢启动(指数增长)→拥塞避免(线性增长)→拥塞发生(快重传+快恢复或超时)→降速重来。像开车:先踩油门加速→匀速巡航→遇到堵车就减速→路况好了再加速。

TCP拥塞控制防止网络过载,是面试高频考点:

Terminal window
1
┌──────────────────────────────────────────────────┐
2
cwnd(拥塞窗口)
3
4
/\
5
/ \ 拥塞避免(线性增)
6
/ \───────────────
7
/ 慢启动(指数增)
8
/ 超时→cwnd=1
9
/ 3次重复ACK→cwnd减半
10
└───────────────────────────── 时间
11
└──────────────────────────────────────────────────┘
12
13
四个阶段:
14
1. 慢启动: cwnd从1开始,每个RTT翻倍(指数增长)
15
2. 拥塞避免: cwnd超过ssthresh后,每个RTT+1(线性增长)
2 collapsed lines
16
3. 快速重传: 3个重复ACK 立即重传 cwnd减半
17
4. 快速恢复: 不回到慢启动,从cwnd/2开始拥塞避免

Q7: TCP的TIME_WAIT状态?为什么需要?

🧠 秒懂: 主动关闭方经历TIME_WAIT等待2MSL(约60秒)。原因:①确保最后一个ACK能到达对方 ②等待残余报文在网络中消失(防止新连接收到旧数据)。服务器重启时SO_REUSEADDR跳过等待。

💡 面试高频 | 服务端开发/嵌入式网络调试高频 | GitHub面经高赞题

TIME_WAIT是TCP四次挥手后主动关闭方的状态,持续2MSL(通常60秒):

为什么需要TIME_WAIT?

  1. 确保最后一个ACK能到达对方(如果丢失,对方会重发FIN)
  2. 让网络中残留的数据包消亡(防止新连接收到旧数据)

嵌入式中的问题: 大量TIME_WAIT占用端口和内存

Terminal window
1
# 查看TIME_WAIT数量
2
ss -s | grep TIME-WAIT
3
4
# 解决方案
5
# 1. SO_REUSEADDR(允许复用TIME_WAIT的端口)
6
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
7
# 2. 内核参数调节
8
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
9
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout

Q8: TCP的Nagle算法和延迟ACK?

🧠 秒懂: Nagle算法:小数据包凑大了再发(减少小包数量)。延迟ACK:等一会再发ACK(可能捎带数据一起发)。两者同时开启可能导致延迟增大。嵌入式低延迟场景用TCP_NODELAY关闭Nagle。

Nagle算法减少小包发送,延迟ACK减少ACK数量:

1
Nagle算法规则:
2
如果有已发送未确认的数据 → 缓存小数据 → 等ACK到后一起发
3
如果没有未确认数据 → 立即发送
4
5
延迟ACK:
6
收到数据不立即回ACK → 等待一段时间(~40ms)或等待可以捎带ACK
7
8
二者冲突: Nagle等ACK才发数据, ACK延迟等数据才发ACK → 死锁40ms!
9
解决: 关闭Nagle → TCP_NODELAY
1
// 嵌入式实时通信: 关闭Nagle算法
2
int flag = 1;
3
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));

Q9: TCP Keep-Alive机制?

🧠 秒懂: TCP Keep-Alive定期发探测包检测连接是否存活。默认2小时才开始探测——太慢。嵌入式中通常在应用层自己实现心跳包(间隔几秒到几十秒),更灵活可控。

TCP长连接保活检测对方是否存活:

1
int keepalive = 1;
2
int idle = 60; // 60秒无数据开始探测
3
int interval = 10; // 每10秒探测一次
4
int count = 3; // 3次无响应认为断开
5
6
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
7
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle));
8
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));
9
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &count, sizeof(count));

应用层心跳 vs TCP Keep-Alive:

  • TCP Keep-Alive:系统级,检测物理连接(默认2小时太久)
  • 应用层心跳:更灵活,可检测应用层存活

Q10: TCP半连接队列和全连接队列?

💡 面试高频 | 服务端开发高频 | 追问SYN Flood

🧠 秒懂: 半连接队列存SYN_RECV状态(收到SYN但没完成握手),全连接队列存ESTABLISHED状态(已完成握手等accept)。队列满时新连接被丢弃。listen的backlog参数控制全连接队列大小。

服务器端accept之前的两个队列是面试常考:

1
客户端SYN到达 → 半连接队列(SYN Queue)
2
↓ 完成三次握手
3
全连接队列(Accept Queue) → accept()取出
4
5
半连接队列满: SYN Flood攻击 → 解决:SYN Cookie
6
全连接队列满: 服务器accept太慢 → 丢弃新连接
7
8
查看:
9
cat /proc/sys/net/ipv4/tcp_max_syn_backlog # 半连
10
ss -ltn # Recv-Q=当前全连接数, Send-Q=listen backlog

💡 面试追问:

  1. HTTP和HTTPS的区别?
  2. MQTT为什么适合IoT?QoS三个等级是什么?
  3. CoAP和MQTT在嵌入式中怎么选?

嵌入式建议: 嵌入式网络协议选型:资源充足用MQTT(TCP,可靠),极度受限用CoAP(UDP,轻量)。HTTP仅用于配置页面/OTA下载。面试说清楚各自特点和选型依据。

Q11: 什么是SYN Flood攻击?如何防御?

🧠 秒懂: SYN Flood用大量伪造SYN包填满半连接队列,正常连接无法建立。防御:SYN Cookie(不保存半连接状态,用加密Cookie验证)、增大队列、缩短超时、防火墙过滤。

💡 面试高频 | 安全相关岗位必考 | 大疆/海康网络安全追问

SYN Flood是经典的DoS攻击方式:

Terminal window
1
攻击原理:
2
攻击者伪造大量SYN包(源IP伪造) → 服务器创建半连接 → 等待ACK
3
半连接队列满 正常用户连接被拒绝
4
5
防御方法:
6
1. SYN Cookie: 不保存半连接状态,通过时间戳+哈希验证ACK合法性
7
echo 1 > /proc/sys/net/ipv4/tcp_syncookies
8
9
2. 增大队列:
10
echo 4096 > /proc/sys/net/ipv4/tcp_max_syn_backlog
11
12
3. 减少SYN+ACK重试次数:
13
echo 1 > /proc/sys/net/ipv4/tcp_synack_retries

Q12: UDP如何实现可靠传输?

💡 面试高频 | 牛客高频 | 游戏/音视频方向常考

🧠 秒懂: UDP本身不可靠。应用层实现可靠传输:序列号+确认+超时重传+乱序缓存。参考QUIC/KCP协议。嵌入式中可用简单的确认重传机制:发一帧→等ACK→超时重发。

UDP本身不可靠, 但应用层可以加可靠性(如QUIC, KCP):

1
实现方案(仿TCP):
2
1. 序列号: 每个包编号
3
2. 确认应答: 接收方发ACK
4
3. 超时重传: 未收到ACK则重传
5
4. 滑动窗口: 流量控制
6
7
简易可靠UDP伪代码:
8
发送方:
9
packet.seq = next_seq++;
10
send(packet);
11
timer_start(packet.seq, TIMEOUT);
12
// 收到ACK → 确认, 超时 → 重传
13
14
接收方:
15
recv(packet);
7 collapsed lines
16
if (packet.seq == expected_seq) {
17
deliver(packet.data);
18
expected_seq++;
19
} else {
20
// 乱序: 缓存或丢弃
21
}
22
send_ack(expected_seq);

Q13: OSI七层模型和TCP/IP四层模型?

💡 面试高频 | 网络基础第一题 | 必须能说出每层干什么

🧠 秒懂: OSI七层(物理→数据链路→网络→传输→会话→表示→应用)是理论模型。TCP/IP四层(网络接口→网络→传输→应用)是实际使用。面试画图+对应关系+每层协议举例。

网络分层是面试基础题:

Terminal window
1
OSI七层: TCP/IP四层: 协议示例:
2
┌────────────┐
3
应用层 应用层 HTTP/FTP/MQTT/DNS
4
├────────────┤
5
表示层 SSL/TLS
6
├────────────┤
7
会话层 RPC
8
├────────────┤
9
传输层 传输层 TCP/UDP
10
├────────────┤
11
网络层 网络层 IP/ICMP/ARP
12
├────────────┤
13
数据链路层 网络接口层 Ethernet/WiFi/PPP
14
├────────────┤
15
物理层 电缆/光纤
1 collapsed line
16
└────────────┘

Q14: IP地址分类和子网划分?

🧠 秒懂: IPv4地址32位分为网络号+主机号。子网掩码划分子网(如255.255.255.0表示前24位是网络号)。CIDR表示法:192.168.1.0/24。私有地址:10.x/172.16-31.x/192.168.x.x。

网络工程师面试必备(嵌入式IoT也常涉及):

Terminal window
1
IPv4地址分类:
2
A类: 1.0.0.0 ~ 126.255.255.255 /8 (大型网络)
3
B类: 128.0.0.0 ~ 191.255.255.255 /16 (中型网络)
4
C类: 192.0.0.0 ~ 223.255.255.255 /24 (小型网络)
5
D类: 224~239 (多播)
6
E类: 240~255 (保留)
7
8
私有地址:
9
10.0.0.0/8
10
172.16.0.0/12
11
192.168.0.0/16
12
13
子网划分示例:
14
192.168.1.0/24 分成4个子网:
15
- 192.168.1.0/26 (0~63, 可用1~62)
3 collapsed lines
16
- 192.168.1.64/26 (64~127)
17
- 192.168.1.128/26 (128~191)
18
- 192.168.1.192/26 (192~255)

Q15: ARP协议的工作原理?

🧠 秒懂: ARP将IP地址解析为MAC地址:广播’谁是192.168.1.1?‘→目标回复自己的MAC→缓存到ARP表。同一局域网内通信必须知道对方MAC——ARP就是翻译官。

ARP(地址解析协议)将IP地址映射为MAC地址:

1
场景: 主机A(192.168.1.1)想发数据给主机B(192.168.1.2)
2
3
1. A查ARP缓存: 有B的MAC吗?
4
└→ 有: 直接用
5
└→ 没有: 发送ARP请求(广播)
6
7
2. ARP请求(广播):
8
"谁是192.168.1.2? 请告诉192.168.1.1(MAC: AA:BB:CC:DD:EE:FF)"
9
10
3. B收到后回复(单播):
11
"我是192.168.1.2, 我的MAC是11:22:33:44:55:66"
12
13
4. A更新ARP缓存, 后续直接用
14
15
命令:
2 collapsed lines
16
arp -a # 查看ARP表
17
arping 192.168.1.2 # 主动探测

Q16: ICMP协议的作用?

🧠 秒懂: ICMP是IP层的’信差’——报告网络错误和状态。ping用ICMP Echo请求/回复测连通性,traceroute用ICMP TTL超时报文探测路由。网络诊断的基础协议。

ICMP(互联网控制消息协议)用于网络诊断和错误报告:

ICMP类型作用工具
Echo Request/Reply连通性检测ping
Destination Unreachable主机/端口不可达自动报告
Time ExceededTTL减为0traceroute
Redirect路由重定向路由器发送
Terminal window
1
ping -c 3 192.168.1.1 # ICMP Echo
2
traceroute 8.8.8.8 # 利用TTL递增发送UDP/ICMP

Q17: NAT(网络地址转换)的原理?

🧠 秒懂: NAT把私有IP转换为公网IP(一个公网IP供多台设备共享上网)。路由器维护转换表:内网IP:端口↔公网IP:端口。嵌入式设备通常在NAT内网中,外部主动连入需要端口映射。

NAT让私网主机通过少量公网IP访问互联网:

1
内网主机: NAT设备: 外网服务器:
2
192.168.1.2:5000 ──→ 转换为202.1.1.1:10001 ──→ 8.8.8.8:53
3
192.168.1.3:6000 ──→ 转换为202.1.1.1:10002 ──→ 8.8.8.8:53
4
5
NAT表:
6
内网地址:端口 公网地址:端口
7
192.168.1.2:5000 ←→ 202.1.1.1:10001
8
192.168.1.3:6000 ←→ 202.1.1.1:10002
9
10
嵌入式相关: IoT设备在内网, 需要NAT穿透才能被外部访问
11
穿透方法: STUN/TURN/ICE, 或MQTT等长连接协议

Q18: DNS解析过程?

🧠 秒懂: 浏览器输入域名→查本地DNS缓存→问本地DNS服务器→递归/迭代查询(根→顶级→权威DNS)→返回IP地址。嵌入式IoT设备连云服务器时也走DNS解析。

域名解析流程(面试可能问递归查询和迭代查询的区别):

Terminal window
1
浏览器输入www.example.com:
2
1. 查浏览器DNS缓存
3
2. 查操作系统DNS缓存(/etc/hosts)
4
3. 查本地DNS服务器(递归解析器)
5
4. 本地DNS 根服务器: "谁管.com?"
6
5. 本地DNS .com顶级域: "谁管example.com?"
7
6. 本地DNS example.com权威服务器: "www.example.com的IP是?"
8
7. 返回IP地址,缓存TTL时间
9
10
嵌入式中: 设备通常用静态DNS或mDNS(局域网设备发现)

Q19: HTTP协议基础?

🧠 秒懂: HTTP是无状态的请求-响应协议(基于TCP)。请求:方法(GET/POST)+URL+头部+体。响应:状态码(200/404/500)+头部+体。嵌入式HTTP通常用轻量库(如microhttpd)。

嵌入式设备越来越多提供Web接口(RESTful API):

Terminal window
1
HTTP请求格式:
2
GET /api/status HTTP/1.1\r\n
3
Host: 192.168.1.100\r\n
4
Content-Type: application/json\r\n
5
\r\n
6
7
HTTP响应格式:
8
HTTP/1.1 200 OK\r\n
9
Content-Type: application/json\r\n
10
Content-Length: 25\r\n
11
\r\n
12
{"status":"running"}
13
14
常用状态码:
15
200 OK
5 collapsed lines
16
301 永久重定向
17
400 请求错误
18
403 禁止
19
404 未找到
20
500 服务器错误

Q20: HTTPS和TLS的工作原理?

🧠 秒懂: HTTPS = HTTP + TLS加密。TLS握手:交换证书→验证身份→协商密钥→加密通信。嵌入式IoT设备连接云服务必须用TLS(如MQTT over TLS),用mbedTLS库实现。

嵌入式设备安全通信需要TLS:

1
TLS握手简化过程:
2
1. Client Hello: 支持的加密套件列表, 随机数
3
2. Server Hello: 选定加密套件, 证书, 随机数
4
3. 客户端验证证书(CA签名)
5
4. 客户端生成预主密钥(用服务器公钥加密发送)
6
5. 双方用三个随机数生成会话密钥
7
6. 后续用对称加密通信(AES等, 高效)
8
9
嵌入式:
10
- mbedTLS/wolfSSL(轻量级TLS实现)
11
- 证书体积和内存是挑战
12
- 硬件加密加速(AES-NI/ARM CE)

二、网络编程进阶(Q21~Q40)

💡 面试追问:

  1. ARP欺骗攻击原理?
  2. 嵌入式设备如何防御ARP攻击?
  3. RARP是什么,现在还用吗?

嵌入式建议: 工业嵌入式设备(PLC/网关)暴露在网络中容易被ARP攻击。防御方法:静态ARP表、ARP检测、VLAN隔离。面试可作为安全意识的加分项。

Q21: socket编程中常见的错误和处理?

🧠 秒懂: 常见错误:ECONNREFUSED(对方没有监听)、ETIMEDOUT(连接超时)、EADDRINUSE(端口被占)、EPIPE(对方已关闭)、EAGAIN(非阻塞暂时无数据)。每个errno要有对应处理策略。

嵌入式网络编程中常遇到的errno:

errno含义处理方法
ECONNREFUSED对方拒绝连接检查服务是否启动
ETIMEDOUT连接超时重试/检查网络
ECONNRESET对方强制关闭重连
EPIPE写入已关闭的socket捕获SIGPIPE
EAGAIN非阻塞无数据继续epoll等待
EINTR被信号中断重试
1
// 健壮的读取函数
2
ssize_t read_n(int fd, void *buf, size_t count) {
3
size_t left = count;
4
char *ptr = buf;
5
while (left > 0) {
6
ssize_t n = read(fd, ptr, left);
7
if (n < 0) {
8
if (errno == EINTR) continue;
9
if (errno == EAGAIN) break;
10
return -1;
11
}
12
if (n == 0) break; // 对端关闭
13
left -= n;
14
ptr += n;
15
}
2 collapsed lines
16
return count - left;
17
}

Q22: TCP连接的状态转换图?

🧠 秒懂: CLOSED→SYN_SENT→ESTABLISHED→FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSED(主动关闭方)。CLOSED→LISTEN→SYN_RCVD→ESTABLISHED→CLOSE_WAIT→LAST_ACK→CLOSED(被动关闭方)。

面试必须能画出TCP状态机:

1
主动打开 被动打开
2
│ │
3
┌─────────▼──────────┐ ┌──────────▼─────────┐
4
│ SYN_SENT │ │ LISTEN │
5
│ (发送SYN) │ │ (等待连接) │
6
└────────┬───────────┘ └──────────┬─────────┘
7
│收到SYN+ACK │收到SYN
8
┌────────▼───────────┐ ┌──────────▼─────────┐
9
│ ESTABLISHED │ │ SYN_RCVD │
10
│ (连接建立) │ │ (发送SYN+ACK) │
11
└────────┬───────────┘ └──────────┬─────────┘
12
│ │收到ACK
13
│ ┌────────▼─────────┐
14
│ │ ESTABLISHED │
15
│ └──────────────────┘
3 collapsed lines
16
关闭连接:
17
主动关闭: ESTABLISHED→FIN_WAIT_1→FIN_WAIT_2→TIME_WAIT→CLOSED
18
被动关闭: ESTABLISHED→CLOSE_WAIT→LAST_ACK→CLOSED

Q23: 什么是TCP的MSS和MTU?

🧠 秒懂: MTU是链路层一帧能装的最大数据量(以太网1500字节)。MSS是TCP一个段能装的最大数据量(MTU-IP头-TCP头=1460字节)。超过MTU会IP分片,影响性能。

理解MTU/MSS对嵌入式网络调优很重要:

1
MTU(Maximum Transmission Unit): 数据链路层最大帧长度
2
以太网MTU = 1500字节
3
4
MSS(Maximum Segment Size): TCP单次发送最大数据量
5
MSS = MTU - IP头(20) - TCP头(20) = 1460字节
6
7
┌─────────────────────────────────────────────────┐
8
│ 以太网帧头 │ IP头(20B) │ TCP头(20B) │ 数据(≤1460B) │ FCS │
9
│ 14B │ │ │ │ 4B │
10
└─────────────────────────────────────────────────┘
11
12
如果数据>MTU → IP分片(尽量避免,影响性能)
13
TCP通过MSS协商避免IP层分片

Q24: 什么是Socket的backlog参数?

🧠 秒懂: backlog是listen()的参数,指定已完成握手待accept的连接队列长度。设太小会在高并发时拒绝连接。嵌入式服务器根据预期并发量设置(通常不大,5-128)。

listen(fd, backlog)中backlog的含义:

1
// backlog定义了全连接队列的最大长度
2
listen(listen_fd, 128); // 最多允许128个完成三次握手但未accept的连接
3
4
// 如果队列满: 新的SYN可能被丢弃或返回RST
5
// 嵌入式设备: 根据预期并发连接数设置合理值
6
// 高并发服务器: 设置较大值(如1024)

Q25: 广播和多播的区别?

🧠 秒懂: 广播发给子网内所有设备(一对所有),多播发给特定组的设备(一对多)。多播更节省带宽——只有加入多播组的设备才收到数据。嵌入式设备发现常用多播。

嵌入式局域网通信中常用:

广播(Broadcast)多播(Multicast)
范围同一子网所有主机加入多播组的主机
地址255.255.255.255或子网广播224.0.0.0~239.255.255.255
路由不跨路由器可跨路由器(IGMP)
效率低(所有主机都处理)高(只有组员处理)
用途设备发现、DHCP视频流、传感器数据分发

Q26: TCP和UDP的头部格式?

🧠 秒懂: TCP头20字节(源端口/目标端口/序号/确认号/标志位/窗口大小/校验和)。UDP头8字节(源端口/目标端口/长度/校验和)。UDP头部简单所以开销小。

理解报文格式对wireshark抓包分析很重要:

1
TCP头部(20字节+可选):
2
┌────────────────────────────────────────┐
3
│ 源端口(16) │ 目的端口(16) │
4
├────────────────────────────────────────┤
5
│ 序列号(32) │
6
├────────────────────────────────────────┤
7
│ 确认号(32) │
8
├────────────────────────────────────────┤
9
│ 头部长度│保留│URG ACK PSH RST SYN FIN│窗口(16)│
10
├────────────────────────────────────────┤
11
│ 校验和(16) │ 紧急指针(16) │
12
└────────────────────────────────────────┘
13
14
UDP头部(8字节):
15
┌────────────────────────────────────────┐
4 collapsed lines
16
│ 源端口(16) │ 目的端口(16) │
17
├────────────────────────────────────────┤
18
│ 长度(16) │ 校验和(16) │
19
└────────────────────────────────────────┘

Q27: 什么是DHCP?工作流程?

🧠 秒懂: DHCP自动分配IP地址:DISCOVER(客户端广播找DHCP服务器)→OFFER(服务器提供IP)→REQUEST(客户端选择并请求)→ACK(服务器确认)。嵌入式联网设备通常用DHCP自动获取IP。

DHCP自动分配IP地址(嵌入式设备入网常用):

1
DHCP四步交互(DORA):
2
1. Discover(广播): 客户端:"谁是DHCP服务器?"
3
2. Offer(广播): 服务器:"我可以给你192.168.1.100"
4
3. Request(广播): 客户端:"我要192.168.1.100"
5
4. ACK(广播): 服务器:"确认,有效期24小时"
6
7
嵌入式中:
8
- 设备启动时通过DHCP获取IP
9
- 服务器可根据MAC分配固定IP(静态绑定)
10
- 没有DHCP服务器: 使用链路本地地址(169.254.x.x)

Q28: Wireshark/tcpdump抓包分析?

🧠 秒懂: tcpdump命令行抓包:tcpdump -i eth0 port 80 -w file.pcap。Wireshark图形化分析pcap文件——过滤条件+协议解析+流追踪。嵌入式网络调试的必备技能。

嵌入式网络问题排查的核心技能:

Terminal window
1
# tcpdump抓包(嵌入式开发板)
2
tcpdump -i eth0 -w capture.pcap # 抓所有包
3
tcpdump -i eth0 port 8080 # 指定端口
4
tcpdump -i eth0 host 192.168.1.100 # 指定主机
5
tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0' # 只看SYN包
6
7
# Wireshark过滤示例
8
tcp.port == 8080
9
ip.addr == 192.168.1.100
10
tcp.flags.syn == 1 and tcp.flags.ack == 0 # 新连接SYN
11
tcp.analysis.retransmission # 重传包

Q29: 什么是WebSocket?和HTTP的关系?

🧠 秒懂: WebSocket在HTTP基础上升级为全双工通信——服务器可以主动推送数据(HTTP只能客户端请求)。嵌入式Web控制界面常用WebSocket实时推送传感器数据。

WebSocket是全双工通信协议(嵌入式Web实时数据推送):

Terminal window
1
HTTP: 请求-响应模型(客户端主动)
2
WebSocket: 双向实时通信(服务器可主动推送)
3
4
建立过程(HTTP升级):
5
客户端: GET /ws HTTP/1.1
6
Upgrade: websocket
7
Connection: Upgrade
8
服务器: HTTP/1.1 101 Switching Protocols
9
Upgrade: websocket
10
11
嵌入式应用:
12
- 实时传感器数据推送到Web页面
13
- 远程控制命令下发
14
- 比轮询HTTP效率高得多

Q30: 什么是MQTT协议?嵌入式IoT中的应用?

🧠 秒懂: MQTT是轻量级发布/订阅消息协议:设备publish发布消息→Broker中转→订阅者subscribe接收。最小报文2字节。嵌入式IoT首选协议,适合低带宽、不稳定网络。

MQTT是轻量级发布/订阅消息协议,IoT设备最常用:

Terminal window
1
架构:
2
设备(Publisher) ──→ Broker(服务器) ──→ 订阅者(Subscriber)
3
4
特点:
5
- 小开销(最小2字节头)
6
- 发布/订阅模式(解耦)
7
- QoS三级(0:最多一次, 1:至少一次, 2:恰好一次)
8
- 支持遗嘱消息(Will, 设备异常断开时通知)
9
- 保持连接(Keep Alive心跳)
10
11
嵌入式典型用法:
12
传感器 发布 sensor/temp Broker 订阅者(云平台/APP)
13
APP 发布 device/cmd Broker 订阅者(设备)

Q31: lwIP协议栈在嵌入式中的使用?

🧠 秒懂: lwIP是轻量级TCP/IP协议栈(几十KB RAM),适合没有OS或RTOS的嵌入式设备。支持TCP/UDP/DHCP/DNS等核心协议。STM32+lwIP是嵌入式网络的经典组合。

lwIP是MCU级嵌入式设备的轻量TCP/IP协议栈:

1
// lwIP基本使用(raw API)
2
#include "lwip/tcp.h"
3
4
struct tcp_pcb *pcb = tcp_new();
5
tcp_bind(pcb, IP_ADDR_ANY, 8080);
6
pcb = tcp_listen(pcb);
7
tcp_accept(pcb, accept_callback);
8
9
err_t accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) {
10
tcp_recv(newpcb, recv_callback);
11
return ERR_OK;
12
}
13
14
err_t recv_callback(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) {
15
if (p != NULL) {
6 collapsed lines
16
// 处理数据
17
tcp_write(pcb, response, resp_len, TCP_WRITE_FLAG_COPY);
18
pbuf_free(p);
19
}
20
return ERR_OK;
21
}

lwIP vs Linux TCP/IP:

  • lwIP适合MCU(RAM<100KB),裸机或RTOS
  • Linux TCP/IP功能完整,适合Cortex-A等高性能平台

Q32: CoAP协议(嵌入式RESTful)?

🧠 秒懂: CoAP是面向受限设备的轻量级RESTful协议(基于UDP)。类似HTTP但报文极小(4字节头)。支持GET/PUT/POST/DELETE、观察模式(observe)。适合传感器数据上报。

CoAP是为受限设备设计的轻量级RESTful协议:

Terminal window
1
CoAP vs HTTP:
2
- 运行在UDP上(比TCP轻量)
3
- 二进制格式(HTTP是文本)
4
- 4字节头(HTTP头可能几百字节)
5
- 支持资源观察(类似订阅)
6
7
CoAP方法: GET/POST/PUT/DELETE (和HTTP相同)
8
9
嵌入式场景:
10
GET coap://sensor.local/temperature
11
2.05 Content "23.5"

Q33: TCP连接的优雅关闭和强制关闭?

🧠 秒懂: 优雅关闭:shutdown(SHUT_WR)告诉对方’我发完了但还能收’→等对方也关闭→close。强制关闭:SO_LINGER设置超时0后close,直接发RST。嵌入式中优雅关闭避免数据丢失。

关闭策略影响数据完整性:

1
// 优雅关闭(等待数据发完)
2
shutdown(fd, SHUT_WR); // 关闭写端,发FIN
3
// 继续读取对方剩余数据...
4
while (read(fd, buf, sizeof(buf)) > 0);
5
close(fd);
6
7
// 强制关闭(RST,丢弃未发数据)
8
struct linger lg = { .l_onoff = 1, .l_linger = 0 };
9
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg));
10
close(fd); // 发RST而非FIN
11
12
// 半关闭
13
shutdown(fd, SHUT_WR); // 关写不关读(常用于通知对方数据发完)
14
shutdown(fd, SHUT_RD); // 关读不关写

Q34: 网络编程中的大端小端问题?

🧠 秒懂: 网络字节序是大端,x86/ARM小端。发送前htonl/htons转网络序,接收后ntohl/ntohs转主机序。多字节字段(IP/端口/长度)都需要转换,单字节不需要。

嵌入式跨设备通信经常遇到字节序问题:

1
// 网络字节序=大端
2
// x86/ARM(通常)=小端
3
4
// 不同字节序的表示:
5
// 数值: 0x12345678
6
// 大端(网络): [12][34][56][78] (高位在前)
7
// 小端(主机): [78][56][34][12] (低位在前)
8
9
// 转换
10
uint32_t host_val = 0x12345678;
11
uint32_t net_val = htonl(host_val); // 主机→网络
12
13
// 结构体网络传输(必须处理字节序)
14
typedef struct __attribute__((packed)) {
15
uint16_t cmd; // 必须htons
2 collapsed lines
16
uint32_t param; // 必须htonl
17
} NetPacket;

Q35: 什么是零窗口和窗口探测?

🧠 秒懂: 零窗口:接收方缓冲区满时通告窗口=0,发送方停止发送。窗口探测:发送方定期发1字节探测包检查窗口是否恢复。防止因窗口更新报文丢失导致的死锁。

TCP流控的边界情况:

1
零窗口场景:
2
接收方处理慢 → 接收缓冲满 → 通告窗口=0 → 发送方停止
3
4
发送方如何知道窗口恢复?
5
→ 窗口探测机制(Persist Timer):
6
定期发送1字节探测包 → 等待对方回复新窗口值
7
8
问题: 如果窗口更新ACK丢失 → 死锁(双方互相等待)
9
→ 所以需要Persist Timer

Q36: 什么是带外数据(OOB/紧急数据)?

🧠 秒懂: 带外数据(TCP紧急指针)标记’紧急’数据——接收方优先处理。实际很少使用(只能传1字节且实现不一致)。嵌入式中不建议使用,用自定义协议的优先级字段替代。

TCP的紧急数据机制(实际使用较少):

1
// 发送紧急数据
2
send(fd, "X", 1, MSG_OOB);
3
4
// 接收紧急数据
5
recv(fd, buf, 1, MSG_OOB);
6
7
// 或通过SIGURG信号通知
8
signal(SIGURG, oob_handler);
9
fcntl(fd, F_SETOWN, getpid());
10
11
// 实际应用: Telnet中Ctrl+C中断、FTP中止传输
12
// 嵌入式: 紧急停止命令

Q37: 什么是端口复用和SO_REUSEPORT?

🧠 秒懂: SO_REUSEADDR允许绑定处于TIME_WAIT的端口(服务器重启不用等)。SO_REUSEPORT允许多个进程/线程绑定同一端口(负载均衡)。嵌入式服务器必须设SO_REUSEADDR。

端口复用在高并发中的作用:

1
// SO_REUSEADDR: TIME_WAIT状态也能绑定(基本必设)
2
int opt = 1;
3
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
4
5
// SO_REUSEPORT: 多个socket绑定同一端口(内核负载均衡)
6
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
7
// 多进程/多线程各自listen同一端口 → 内核分发连接

Q38: IPv6基础知识?

🧠 秒懂: IPv6地址128位(解决IPv4地址不够的问题)。简化的头部、内置安全性(IPsec)、支持自动配置(SLAAC)。嵌入式IoT的大规模部署未来必然用IPv6(如Thread协议)。

IoT设备越来越多使用IPv6:

1
IPv6地址: 128位, 如 2001:0db8:85a3::8a2e:0370:7334
2
IPv4地址: 32位, 如 192.168.1.1
3
4
IPv6优势:
5
- 地址空间巨大(每个IoT设备都可有全球唯一地址)
6
- 无需NAT(端到端直达)
7
- 内置IPsec
8
- 自动配置(SLAAC)
9
10
嵌入式:
11
struct sockaddr_in6 addr6;
12
addr6.sin6_family = AF_INET6;
13
inet_pton(AF_INET6, "::1", &addr6.sin6_addr);

Q39: 什么是心跳包设计?

🧠 秒懂: 心跳包是应用层定期发送的’我还活着’消息。目的:①检测连接是否正常 ②防止NAT/防火墙超时断开。设计要点:间隔适当(10-30秒)、超时几次判断断开、重连机制。

嵌入式设备长连接必须实现心跳机制:

1
// 应用层心跳设计
2
#define HEARTBEAT_INTERVAL 30 // 30秒
3
#define HEARTBEAT_TIMEOUT 90 // 3倍间隔无心跳则断开
4
5
// 发送心跳
6
void *heartbeat_thread(void *arg) {
7
while (connected) {
8
send_heartbeat_packet(fd);
9
sleep(HEARTBEAT_INTERVAL);
10
}
11
}
12
13
// 检测心跳超时
14
void check_timeout(void) {
15
time_t now = time(NULL);
5 collapsed lines
16
if (now - last_heartbeat_time > HEARTBEAT_TIMEOUT) {
17
// 连接断开,执行重连
18
reconnect();
19
}
20
}

Q40: 什么是Protobuf/MessagePack? 嵌入式中的应用?

🧠 秒懂: Protobuf(Google)和MessagePack是高效二进制序列化格式——比JSON体积小很多、解析快。嵌入式中用于低带宽网络传输(如IoT数据上报)和存储紧凑的结构化数据。

高效的序列化协议(替代JSON):

1
JSON: {"temp":23.5,"humi":65} → 28字节(文本)
2
Protobuf: [二进制编码] → ~8字节(紧凑)
3
MessagePack: [二进制] → ~12字节
4
5
嵌入式优势:
6
- 更小体积(节省带宽)
7
- 更快解析(无字符串解析)
8
- 跨语言兼容(设备C/云端Python)
9
10
嵌入式常用:
11
- nanopb(C语言Protobuf实现,适合MCU)
12
- CBOR(类JSON的二进制格式)
13
- FlatBuffers(零拷贝反序列化)

★ 嵌入式/IoT协议选型对比(系统设计题常考):

协议层级适用场景特点典型设备
MQTT应用层IoT云端通信轻量/发布订阅/QoS智能家居/传感器
CoAP应用层资源受限IoT类REST/UDP/低功耗NB-IoT设备
HTTP应用层Web设备管理通用/重量级路由器/网关
Modbus应用层工业控制简单/主从式PLC/变频器
WebSocket应用层实时推送全双工/持久连接实时监控面板

三、嵌入式网络协议(Q41~Q55)

Q41: MQTT QoS三级的区别和实现?

🧠 秒懂: QoS 0(最多一次,可能丢)→QoS 1(至少一次,可能重复)→QoS 2(恰好一次,最可靠但最慢)。嵌入式中传感器数据用QoS 0/1即可,控制命令用QoS 1或2确保送达。

💡 面试高频 | IoT/智能家居岗位必考 | 涂鸦/小米IoT面经常见

MQTT三种服务质量等级的对比:

QoS含义实现适用场景
0最多一次(Fire&Forget)直接发,不确认传感器频繁上报(丢一条无所谓)
1至少一次等PUBACK,否则重发控制命令(可能重复)
2恰好一次四次握手(PUBREC/PUBREL/PUBCOMP)计费/支付(不能重复)
1
QoS 1流程:
2
Publisher → PUBLISH(msgid=1) → Broker
3
Publisher ← PUBACK(msgid=1) ← Broker
4
(如果超时没收到PUBACK → 重发PUBLISH)
5
6
QoS 2流程(四步):
7
Publisher → PUBLISH → Broker
8
Publisher ← PUBREC ← Broker (收到了)
9
Publisher → PUBREL → Broker (确认释放)
10
Publisher ← PUBCOMP ← Broker (完成)

Q42: MQTT的遗嘱(Will)消息?

🧠 秒懂: 遗嘱消息:设备连接Broker时预设’我掉线后发这条消息’。设备异常断开时Broker自动发布遗嘱消息通知订阅者。用于设备离线检测和告警。

遗嘱消息用于设备异常离线时通知其他订阅者:

Terminal window
1
连接时设置遗嘱:
2
CONNECT {
3
will_topic: "device/status"
4
will_message: "offline"
5
will_qos: 1
6
will_retain: true
7
}
8
9
正常断开: 发送DISCONNECT Broker不发遗嘱
10
异常断开: TCP断开/超时 Broker自动发布遗嘱消息
11
12
应用场景:
13
- IoT设备状态监控(在线/离线)
14
- 设备故障告警

Q43: HTTP RESTful API设计(嵌入式Web)?

🧠 秒懂: RESTful API:用HTTP方法操作资源。GET /api/sensor(读)、PUT /api/led(写)、POST(创建)、DELETE(删除)。嵌入式Web服务器提供JSON格式的API供前端/APP调用。

嵌入式设备提供Web管理接口的设计:

Terminal window
1
RESTful规范:
2
GET /api/sensors 获取所有传感器数据
3
GET /api/sensors/1 获取传感器1数据
4
POST /api/config 更新配置
5
PUT /api/device/reboot 重启设备
6
DELETE /api/logs 清除日志
7
8
响应格式:
9
{
10
"code": 200,
11
"data": {
12
"temperature": 23.5,
13
"humidity": 65,
14
"timestamp": 1703001234
15
}
5 collapsed lines
16
}
17
18
嵌入式实现:
19
- 轻量级HTTP库: mongoose, libmicrohttpd
20
- 嵌入式Web框架: Boa, uhttpd(OpenWrt)

Q44: Modbus协议(工业嵌入式)?

🧠 秒懂: Modbus是工业控制领域的标准通信协议。RTU(串口二进制)和TCP(以太网)两种模式。功能码:03读寄存器、06写单个寄存器、16写多个寄存器。PLC和传感器的通用语言。

💡 面试高频 | 工业自动化/PLC岗位必考 | 汇川/中控面经

Modbus是工业自动化最常用的通信协议:

Terminal window
1
Modbus RTU帧格式(RS-485/串口):
2
[地址(1B)] [功能码(1B)] [数据(nB)] [CRC(2B)]
3
4
常用功能码:
5
0x01: 读线圈(开关量)
6
0x03: 读保持寄存器(模拟量)
7
0x06: 写单个寄存器
8
0x10: 写多个寄存器
9
10
示例(读保持寄存器):
11
请求: 01 03 00 00 00 02 C4 0B
12
地址 功能 起始 数量 CRC
13
响应: 01 03 04 00 64 00 FF xx xx
14
地址 功能 字节数 数据 CRC
15
1 collapsed line
16
Modbus TCP: 在TCP上传输, 去掉CRC加MBAP头

Q45: mDNS/DNS-SD(局域网设备发现)?

🧠 秒懂: mDNS(多播DNS)让局域网设备用名字互相发现(如device.local)。DNS-SD(服务发现)发现设备提供的服务(如打印机、摄像头)。嵌入式设备免配IP直接用名字访问。

无需配置DNS服务器的局域网设备互相发现:

1
mDNS:
2
设备宣告: "我是mydevice.local, IP是192.168.1.50"
3
其他设备解析: ping mydevice.local → 192.168.1.50
4
使用多播地址: 224.0.0.251:5353
5
6
DNS-SD(Service Discovery):
7
设备宣告服务: "_http._tcp.local" → "MyDevice Web Server"
8
其他设备查询: "有哪些HTTP服务?" → 列出所有
9
10
嵌入式应用:
11
- 打印机发现(AirPrint)
12
- IoT设备自动发现(不需要知道IP)
13
- Apple Bonjour / Linux Avahi

Q46: TLS/DTLS在嵌入式中的实现?

🧠 秒懂: 嵌入式TLS用mbedTLS或wolfSSL(体积小适合MCU)。DTLS是基于UDP的TLS(保持数据报语义)。证书验证可能耗时(几秒)——预加载根证书和会话缓存优化。

嵌入式设备的安全通信:

Terminal window
1
mbedTLS(轻量级, 适合MCU):
2
- 支持TLS 1.2/1.3
3
- 可裁剪(只编译需要的算法)
4
- RAM使用可控(~50KB起)
5
6
wolfSSL:
7
- 更小的代码体积
8
- 支持FIPS认证
9
- 适合安全要求高的场景
10
11
DTLS(TLS over UDP):
12
- CoAP/MQTT-SN的安全层
13
- 适合丢包环境(重传机制)
14
15
证书管理:
3 collapsed lines
16
- 设备出厂预置CA根证书
17
- 设备私钥存在安全芯片(SE)
18
- 在线证书验证(OCSP)

Q47: 网络协议自定义设计原则?

🧠 秒懂: 自定义协议设计原则:帧头(同步字)→版本→类型→长度→数据→校验(CRC/Checksum)。可扩展(预留字段)、可解析(长度明确)、可校验(防止传输错误)。

嵌入式产品经常需要设计私有协议:

Terminal window
1
协议设计要素:
2
1. 帧头/帧尾(定界): 0xAA55...55AA
3
2. 长度字段(分包)
4
3. 命令字段(区分功能)
5
4. 序列号(重传/去重)
6
5. 校验(CRC/校验和)
7
6. 版本号(兼容升级)
8
9
典型帧格式:
10
┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐
11
帧头 版本 长度 序号 命令 数据 CRC
12
2B 1B 2B 2B 1B nB 2B
13
└──────┴──────┴──────┴──────┴──────┴──────┴──────┘
14
15
状态机解析(适合串口/网络字节流):
1 collapsed line
16
WAIT_HEAD RECV_LEN RECV_DATA CHECK_CRC DISPATCH

Q48: WebSocket在嵌入式设备中的实现?

🧠 秒懂: 嵌入式WebSocket用轻量库(如libwebsockets)。流程:HTTP升级握手→全双工帧收发。适合嵌入式Web配置界面的实时数据推送(比轮询HTTP高效得多)。

嵌入式设备提供实时Web数据推送:

1
// 嵌入式WebSocket简化实现(基于mongoose库)
2
#include "mongoose.h"
3
4
void event_handler(struct mg_connection *c, int ev, void *ev_data) {
5
if (ev == MG_EV_HTTP_MSG) {
6
struct mg_http_message *hm = ev_data;
7
if (mg_http_match_uri(hm, "/ws")) {
8
mg_ws_upgrade(c, hm, NULL);
9
}
10
} else if (ev == MG_EV_WS_MSG) {
11
struct mg_ws_message *wm = ev_data;
12
// 收到WebSocket消息
13
mg_ws_send(c, "ACK", 3, WEBSOCKET_OP_TEXT);
14
}
15
}

Q49: SNMP网络管理协议?

🧠 秒懂: SNMP(简单网络管理协议)用于监控和管理网络设备。Agent运行在设备上→Manager查询/设置MIB对象→Trap主动告警。大型网络设备和工业设备常用。

嵌入式网络设备管理(路由器/交换机):

Terminal window
1
SNMP组成:
2
- Agent(设备端): 响应查询/发送Trap
3
- Manager(管理站): 查询/设置/接收告警
4
- MIB(管理信息库): 设备可管理的参数树
5
6
操作:
7
GET: 读取参数值
8
SET: 修改参数
9
TRAP: 设备主动上报异常
10
11
OID示例:
12
.1.3.6.1.2.1.1.1.0 系统描述
13
.1.3.6.1.2.1.2.2.1.10.1 接口1的入包数

Q50: gRPC在嵌入式中的应用?

🧠 秒懂: gRPC基于HTTP/2+Protobuf,高效的RPC框架。嵌入式中高性能设备(如边缘计算)可以用gRPC与云端通信。资源受限MCU更适合用MQTT或CoAP。

高性能RPC框架(适合嵌入式网关/边缘设备):

1
// 定义服务(sensor.proto)
2
syntax = "proto3";
3
service SensorService {
4
rpc GetData(SensorRequest) returns (SensorData);
5
rpc StreamData(SensorRequest) returns (stream SensorData);
6
}
7
message SensorRequest { int32 sensor_id = 1; }
8
message SensorData { float temperature = 1; float humidity = 2; }
Terminal window
1
优势:
2
- Protobuf序列化(高效紧凑)
3
- HTTP/2(多路复用/流式)
4
- 跨语言(设备C/云端Go/Python)
5
- 适合微服务架构的边缘网关

Q51: 蓝牙BLE协议栈?

🧠 秒懂: BLE(低功耗蓝牙)协议栈:物理层→链路层→HCI→L2CAP→ATT/GATT→应用。GATT定义Service和Characteristic的层次结构。BLE是可穿戴设备和传感器的首选短距通信。

BLE(低功耗蓝牙)是嵌入式近场通信主流:

Terminal window
1
BLE协议栈:
2
应用层: GATT Profile(通用属性配置文件)
3
GAP: 广播/连接管理
4
ATT: 属性传输
5
L2CAP: 逻辑链路
6
HCI: 主控接口
7
链路层: 广告/数据信道
8
物理层: 2.4GHz ISM频段
9
10
GATT模型:
11
Service(服务) → Characteristic(特征值) → Descriptor(描述)
12
13
示例(温度传感器):
14
Service: 温度服务(UUID: 0x1809)
15
├── Characteristic: 当前温度(Read/Notify)
3 collapsed lines
16
└── Value: 23.5°C
17
└── Characteristic: 采集间隔(Read/Write)
18
└── Value: 1000ms

Q52: WiFi连接和配网方案?

🧠 秒懂: WiFi连接:扫描→认证→关联→获取IP(DHCP)。配网方案:SmartConfig(手机广播)、AP模式(设备开热点手机连入配置)、BLE配网。嵌入式WiFi模块如ESP32常用SmartConfig。

嵌入式WiFi设备的配网是产品化难点:

1
配网方案:
2
1. AP模式: 设备开热点→手机连接→配置WiFi信息→切回
3
2. SmartConfig: 手机APP广播WiFi密码→设备嗅探获取
4
3. BLE辅助: 通过BLE传递WiFi凭证(更可靠)
5
4. WPS: 按路由器WPS键→自动配对(简单但不安全)
6
5. 二维码: 扫码获取设备信息→APP配网
7
8
代码流程(Linux wpa_supplicant):
9
// 生成配置
10
echo 'network={ssid="MyWiFi" psk="password"}' > /etc/wpa_supplicant.conf
11
// 启动连接
12
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf
13
dhclient wlan0 // 获取IP

Q53: 网络拥塞控制算法对比?

🧠 秒懂: 拥塞控制算法演进:TCP Reno(经典)→TCP CUBIC(Linux默认,适合高带宽)→BBR(Google,基于带宽估计,高延迟网络表现好)。嵌入式IoT网络通常带宽低用默认CUBIC即可。

不同场景选择不同拥塞控制算法:

算法特点适用
Reno传统AIMD一般场景
CubicLinux默认,快速恢复大带宽高延迟
BBR基于带宽估计长肥管道/丢包网络
Vegas基于延迟低延迟场景
Terminal window
1
# Linux查看/设置拥塞算法
2
cat /proc/sys/net/ipv4/tcp_congestion_control
3
echo bbr > /proc/sys/net/ipv4/tcp_congestion_control

Q54: 嵌入式网络安全(常见攻击和防御)?

🧠 秒懂: 常见攻击:中间人攻击(→TLS加密)、重放攻击(→时间戳/序列号)、固件逆向(→加密固件)、缓冲区溢出(→输入验证)。嵌入式安全:最小攻击面+加密通信+安全启动。

嵌入式设备面临的网络安全威胁:

攻击描述防御
中间人(MITM)窃听/篡改通信TLS/证书验证
重放攻击重发旧数据时间戳/序列号/Nonce
缓冲区溢出覆盖返回地址输入校验/ASLR/栈保护
固件逆向提取加密密钥安全启动/加密存储
DoS耗尽设备资源速率限制/连接数限制

Q55: 网络性能指标和测试方法?

🧠 秒懂: 性能指标:带宽(最大传输速率)、延迟(RTT往返时间)、丢包率、抖动(延迟变化)。测试工具:iperf(带宽)、ping(延迟/丢包)、netperf(吞吐量)。嵌入式用iperf测网络性能。

嵌入式网络性能评估:

Terminal window
1
# 带宽测试
2
iperf3 -s # 服务端
3
iperf3 -c 192.168.1.1 -t 10 # 客户端测试10秒
4
5
# 延迟测试
6
ping -c 100 192.168.1.1 | tail -1
7
# rtt min/avg/max/mdev = 0.5/1.2/3.4/0.5 ms
8
9
# 丢包测试
10
ping -c 1000 -i 0.01 192.168.1.1 | grep "packet loss"
11
12
# 网络吞吐量计算
13
# 理论最大: bandwidth * (1 - packet_loss) / (RTT)
14
# 实际受制于: 窗口大小, 拥塞, CPU处理能力

四、网络安全与加密(Q56~Q70)

Q56: 对称加密和非对称加密的区别?

🧠 秒懂: 对称加密:加密和解密用同一把钥匙(AES/DES),速度快。非对称加密:公钥加密私钥解密(RSA/ECC),安全但慢。实际TLS先用非对称交换密钥,再用对称加密传输数据。

密码学基础(嵌入式安全通信必备):

对称加密非对称加密
密钥加密解密同一把公钥加密,私钥解密
速度快(硬件加速)慢(数学运算复杂)
典型算法AES, DES, ChaCha20RSA, ECC, Ed25519
应用数据加密密钥交换/签名
密钥分发困难(需安全通道)公钥可公开

实际使用(混合): TLS用非对称加密协商出对称密钥,后续通信用对称加密(兼顾安全和性能)。

Q57: 哈希函数的作用?常见算法?

🧠 秒懂: 哈希函数将任意长度数据映射为固定长度摘要(不可逆)。用途:数据完整性校验、密码存储、数字签名。常见算法:MD5(已不安全)、SHA-256(推荐)、CRC(校验用,非安全)。

哈希用于数据完整性校验:

1
特性:
2
- 固定长度输出
3
- 不可逆(不能从哈希值还原数据)
4
- 雪崩效应(输入微小变化→输出完全不同)
5
- 抗碰撞(不同输入很难产生相同哈希)
6
7
常用:
8
MD5: 128位(已不安全,仅用于校验)
9
SHA-256: 256位(安全标准)
10
CRC32: 32位(错误检测,非加密)
11
12
嵌入式应用:
13
- 固件校验: SHA-256(firmware.bin) = 已知哈希?
14
- 密码存储: bcrypt(password + salt)
15
- 数据去重: 内容hash作为唯一标识

Q58: 数字签名的原理?

🧠 秒懂: 数字签名:发送方用私钥对数据哈希加密→接收方用公钥解密并对比哈希。验证了数据来源(身份认证)和完整性(未被篡改)。固件签名验证是安全启动的基础。

数字签名验证数据来源和完整性(嵌入式安全启动):

1
签名过程(发送方):
2
1. 计算数据的哈希: H = SHA256(data)
3
2. 用私钥加密哈希: Sig = RSA_Encrypt(H, private_key)
4
3. 发送: data + Sig
5
6
验证过程(接收方):
7
1. 用公钥解密签名: H1 = RSA_Decrypt(Sig, public_key)
8
2. 计算数据哈希: H2 = SHA256(data)
9
3. 比较: H1 == H2? → 验证通过
10
11
嵌入式安全启动:
12
BootROM中固化公钥 → 验证U-Boot签名 → 验证Kernel签名

Q59: X.509证书和PKI体系?

🧠 秒懂: X.509证书包含公钥+身份信息+CA签名。PKI(公钥基础设施)是证书签发和验证的体系:根CA→中间CA→终端证书。TLS握手时服务器发送证书链供客户端验证。

证书是公钥的可信包装(TLS/HTTPS基础):

Terminal window
1
证书内容:
2
- 持有者信息(设备ID/域名)
3
- 公钥
4
- 有效期
5
- CA签名(证书链上级签发)
6
7
证书链:
8
Root CA(自签,预置在设备中)
9
└→ 中间CA(Root签发)
10
└→ 设备证书(中间CA签发)
11
12
嵌入式设备证书管理:
13
- 出厂预置: 根CA证书 + 设备私钥/证书
14
- 运行时: TLS握手出示证书,验证对方证书
15
- 存储: 私钥存在安全元件(SE)/eFuse中

Q60: CRC校验的原理和实现?

🧠 秒懂: CRC(循环冗余校验)用多项式除法计算校验值。发送方计算CRC附在数据后→接收方重新计算CRC对比。嵌入式中串口、SPI、CAN等协议都用CRC校验数据完整性。

CRC是嵌入式通信中最常用的错误检测码:

1
// CRC-16/MODBUS实现
2
uint16_t crc16_modbus(uint8_t *data, uint16_t len) {
3
uint16_t crc = 0xFFFF;
4
for (uint16_t i = 0; i < len; i++) {
5
crc ^= data[i];
6
for (uint8_t j = 0; j < 8; j++) {
7
if (crc & 0x0001)
8
crc = (crc >> 1) ^ 0xA001;
9
else
10
crc >>= 1;
11
}
12
}
13
return crc;
14
}
15
5 collapsed lines
16
// 使用(通信帧校验)
17
uint8_t frame[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02};
18
uint16_t crc = crc16_modbus(frame, 6);
19
frame[6] = crc & 0xFF; // CRC低字节
20
frame[7] = (crc >> 8) & 0xFF; // CRC高字节

Q61: AES加密在嵌入式中的使用?

🧠 秒懂: AES是最广泛使用的对称加密算法(128/192/256位密钥)。嵌入式中用硬件AES加速器(STM32有CRYP模块)或软件库(mbedTLS)。ECB模式不安全,用CBC或GCM模式。

AES是嵌入式最常用的对称加密算法:

1
// 使用mbedTLS的AES-128-CBC
2
#include "mbedtls/aes.h"
3
4
uint8_t key[16] = "0123456789abcdef";
5
uint8_t iv[16] = "fedcba9876543210";
6
uint8_t plaintext[32] = "Hello, Embedded!";
7
uint8_t ciphertext[32];
8
9
mbedtls_aes_context ctx;
10
mbedtls_aes_init(&ctx);
11
mbedtls_aes_setkey_enc(&ctx, key, 128);
12
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, iv, plaintext, ciphertext);
13
mbedtls_aes_free(&ctx);

AES模式选择:

  • ECB:简单但不安全(相同明文→相同密文)
  • CBC:安全,需要IV(嵌入式常用)
  • GCM:加密+认证(AEAD,最推荐)

Q62: 嵌入式安全启动(Secure Boot)?

🧠 秒懂: 安全启动:每级引导程序验证下一级的签名。BootROM(ROM内固化)→验证U-Boot签名→U-Boot验证内核签名→内核验证rootfs。链式信任确保固件未被篡改。

安全启动链保证固件完整性:

1
安全启动链:
2
BootROM(不可修改) → 验证SPL签名 → SPL验证U-Boot → U-Boot验证Kernel
3
4
实现方法:
5
1. 密钥对: 服务器持有私钥(签名), 设备持有公钥(验证)
6
2. 签名: 每次构建固件时用私钥签名
7
3. 验证: 启动时每级验证下一级签名
8
4. 回退: 验证失败→不启动/回退旧版本
9
10
密钥安全存储:
11
- OTP/eFuse: 一次性可编程(写入后不可修改)
12
- Secure Element(SE): 硬件安全芯片
13
- TrustZone: ARM安全世界隔离

Q63: 嵌入式设备的密钥管理?

🧠 秒懂: 嵌入式密钥管理挑战:不能硬编码(反编译会泄露)。方案:硬件安全模块(HSM/SE)、eFuse一次性编程、TrustZone安全存储。密钥绝不能出现在代码或日志中。

密钥安全是整个安全体系的基础:

1
密钥类型:
2
- 设备密钥对(身份认证)
3
- 通信会话密钥(每次连接不同)
4
- 固件签名密钥(服务器端保管)
5
- 存储加密密钥(保护本地数据)
6
7
存储方案:
8
最安全: 安全芯片(SE/TPM)→私钥永远不出芯片
9
次安全: ARM TrustZone隔离
10
基本: 加密存储在Flash中(用设备唯一ID派生密钥加密)
11
12
生成:
13
- 出厂注入(生产线安全环境)
14
- 设备自生成(TRNG/硬件随机数)
15
3 collapsed lines
16
轮换:
17
- 定期更新证书/密钥
18
- 支持远程密钥更新(需认证通道)

Q64: 什么是HMAC?和普通哈希的区别?

🧠 秒懂: HMAC = 哈希(密钥 + 消息)。比普通哈希多了一个共享密钥——只有知道密钥的人才能生成和验证。用于API认证、消息完整性校验。常用HMAC-SHA256。

HMAC(Hash-based Message Authentication Code)提供消息认证:

1
普通哈希: SHA256(message) → 只能检测篡改
2
HMAC: SHA256(key + message) → 既检测篡改又验证发送者
3
4
公式: HMAC(K, M) = H((K⊕opad) || H((K⊕ipad) || M))
5
6
嵌入式应用:
7
- API请求签名(防篡改)
8
- OTA包校验(防伪造)
9
- 通信认证(双方共享密钥)
1
// mbedTLS HMAC-SHA256
2
#include "mbedtls/md.h"
3
4
uint8_t key[] = "secret_key";
5
uint8_t msg[] = "important data";
6
uint8_t hmac[32];
7
8
mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
9
key, sizeof(key)-1, msg, sizeof(msg)-1, hmac);

Q65: 嵌入式设备固件加密?

🧠 秒懂: 固件加密保护知识产权和安全:用AES加密固件镜像→设备启动时用内置密钥解密→执行。密钥存储在安全硬件中(eFuse/TrustZone),防止固件被逆向分析。

保护固件不被逆向和盗用:

Terminal window
1
固件保护方案:
2
1. 加密存储: AES加密固件存入Flash
3
启动时BootROM用硬件密钥解密到RAM执行
4
5
2. 代码混淆: 增加逆向难度(非加密)
6
7
3. 读保护: MCU的读保护位(RDP/Code Protection)
8
防止通过JTAG/SWD读出Flash内容
9
10
4. 固件签名: 验证完整性(不是加密内容)
11
12
组合方案:
13
签名(完整性) + 加密(保密性) + 读保护(物理防护)

Q66: 随机数生成(TRNG vs PRNG)?

🧠 秒懂: TRNG(真随机数)基于物理噪声(热噪声等),安全性高。PRNG(伪随机数)基于算法(种子确定则输出确定),速度快但可预测。加密场景必须用TRNG或安全PRNG(如HMAC-DRBG)。

加密安全要求高质量随机数:

Terminal window
1
PRNG(伪随机数,软件):
2
- 确定性算法(同种子→同序列)
3
- 快速, 但可预测
4
- 用途: 模拟/测试, 非安全场景
5
6
TRNG(真随机数,硬件):
7
- 物理噪声源(热噪声/抖动)
8
- 不可预测
9
- 用途: 密钥生成/Nonce/IV
10
11
嵌入式TRNG:
12
- STM32 RNG外设: HAL_RNG_GenerateRandomNumber()
13
- Linux: /dev/urandom (内核熵池)
14
- 启动时混合多种熵源(ADC噪声+时钟抖动+MAC地址)

Q67: TLS 1.3的改进?

🧠 秒懂: TLS 1.3简化了握手(1-RTT甚至0-RTT)、移除了不安全的算法(RC4/SHA-1等)、握手过程加密(防止中间人窥探)。嵌入式新项目应直接使用TLS 1.3。

TLS 1.3相比1.2的重大改进:

1
TLS 1.2: TLS 1.3:
2
2-RTT握手 1-RTT握手(0-RTT恢复)
3
支持旧算法(RC4,SHA1) 去除不安全算法
4
RSA密钥交换(无前向保密) 仅ECDHE(完美前向保密)
5
ChangeCipherSpec 去除(简化)
6
7
嵌入式影响:
8
- 握手更快(省电/省流量)
9
- 代码更小(去除旧算法)
10
- 更安全(密钥泄露不影响历史会话)
11
12
mbedTLS 3.x 支持TLS 1.3

Q68: 什么是前向保密(Perfect Forward Secrecy)?

🧠 秒懂: 前向保密:即使服务器私钥将来泄露,过去的通信仍然安全。原理:每次会话用临时密钥对(DHE/ECDHE)协商会话密钥,密钥不保存。TLS 1.3强制使用前向保密。

即使长期密钥泄露,历史通信仍安全:

1
无前向保密(RSA密钥交换):
2
攻击者录制密文 → 未来获取服务器私钥 → 解密所有历史密文!
3
4
有前向保密(ECDHE密钥交换):
5
每次会话生成临时密钥对(ephemeral key)
6
会话结束后临时密钥销毁
7
→ 即使私钥泄露也无法解密历史会话
8
9
实现: TLS中使用ECDHE-RSA或ECDHE-ECDSA套件

Q69: 嵌入式TCP Socket编程完整示例?

🧠 秒懂: 完整TCP客户端/服务器示例:socket→bind→listen→accept→recv/send→close。注意:处理EINTR、缓冲区管理、超时设置、优雅关闭。嵌入式中加上心跳和重连机制。

面试可能要求手写一个完整的TCP通信程序:

1
// 完整的TCP客户端(含错误处理/超时/重连)
2
#include <sys/socket.h>
3
#include <netinet/in.h>
4
#include <arpa/inet.h>
5
#include <unistd.h>
6
#include <errno.h>
7
8
int tcp_connect(const char *ip, int port, int timeout_sec) {
9
int fd = socket(AF_INET, SOCK_STREAM, 0);
10
if (fd < 0) return -1;
11
12
// 设置非阻塞connect(带超时)
13
int flags = fcntl(fd, F_GETFL);
14
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
15
27 collapsed lines
16
struct sockaddr_in addr = {
17
.sin_family = AF_INET,
18
.sin_port = htons(port)
19
};
20
inet_pton(AF_INET, ip, &addr.sin_addr);
21
22
int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
23
if (ret < 0 && errno == EINPROGRESS) {
24
fd_set wfds;
25
FD_ZERO(&wfds);
26
FD_SET(fd, &wfds);
27
struct timeval tv = { .tv_sec = timeout_sec };
28
29
if (select(fd + 1, NULL, &wfds, NULL, &tv) > 0) {
30
int err;
31
socklen_t len = sizeof(err);
32
getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
33
if (err != 0) { close(fd); return -1; }
34
} else {
35
close(fd); return -1; // 超时
36
}
37
}
38
39
// 恢复阻塞模式
40
fcntl(fd, F_SETFL, flags);
41
return fd;
42
}

Q70: 嵌入式通信协议设计实战?

🧠 秒懂: 通信协议设计实战:需求分析(可靠性/速率/方向)→帧格式设计(帧头/类型/长度/数据/CRC)→状态机解析→超时重传→测试验证。嵌入式中协议设计是核心基本功。

完整的私有协议设计示例:

1
// 协议帧定义
2
#pragma pack(1)
3
typedef struct {
4
uint16_t head; // 帧头: 0xAA55
5
uint8_t version; // 协议版本
6
uint16_t length; // 数据长度
7
uint16_t seq; // 序列号
8
uint8_t cmd; // 命令字
9
uint8_t data[0]; // 柔性数组(变长数据)
10
} Frame;
11
#pragma pack()
12
13
// CRC放在frame末尾: data[length]开始的2字节
14
15
// 协议解析状态机
24 collapsed lines
16
typedef enum {
17
STATE_WAIT_HEAD1,
18
STATE_WAIT_HEAD2,
19
STATE_RECV_HEADER,
20
STATE_RECV_DATA,
21
STATE_CHECK_CRC
22
} ParseState;
23
24
void protocol_input(uint8_t byte) {
25
static ParseState state = STATE_WAIT_HEAD1;
26
static uint8_t buf[1024];
27
static int pos = 0;
28
29
switch (state) {
30
case STATE_WAIT_HEAD1:
31
if (byte == 0xAA) { buf[pos++] = byte; state = STATE_WAIT_HEAD2; }
32
break;
33
case STATE_WAIT_HEAD2:
34
if (byte == 0x55) { buf[pos++] = byte; state = STATE_RECV_HEADER; }
35
else { pos = 0; state = STATE_WAIT_HEAD1; }
36
break;
37
// ... 继续解析header和data ...
38
}
39
}


★ 面经高频补充题(来源:GitHub面经仓库/牛客讨论区/大厂真题整理)

Q71: 输入URL到页面显示的全过程?

🧠 秒懂: URL→DNS解析→TCP三次握手→(TLS握手)→HTTP请求→服务器处理→HTTP响应→浏览器渲染→TCP四次挥手。面试经典题,从网络到应用层层递进回答。

💡 面试高频 | 牛客/LeetCode面经出现率>80% | 考察全栈网络知识

回答框架(嵌入式设备HTTP请求同理):

1
1. DNS解析: URL → IP地址
2
浏览器缓存 → OS缓存 → 路由器缓存 → ISP DNS → 递归查询
3
4
2. TCP三次握手建立连接
5
SYN → SYN+ACK → ACK
6
7
3. TLS握手(HTTPS): 证书验证 → 密钥协商 → 加密通道建立
8
9
4. 发送HTTP请求:
10
GET /index.html HTTP/1.1
11
Host: www.example.com
12
13
5. 服务器处理请求 → 返回HTTP响应(200 OK + HTML)
14
15
6. 浏览器渲染: HTML → DOM树 → CSSOM → 渲染树 → 布局 → 绘制
2 collapsed lines
16
17
7. TCP四次挥手断开(HTTP/1.0) 或保持连接(HTTP/1.1 Keep-Alive)

嵌入式关联: 嵌入式设备做HTTP请求(如OTA检查更新)走同样的流程,但通常用轻量级HTTP客户端(如libcurl/mongoose),DNS解析可能直接用硬编码IP跳过。


Q72: HTTPS的TLS握手过程?

🧠 秒懂: TLS 1.2握手:ClientHello→ServerHello+证书→密钥交换→ChangeCipherSpec→Finished。TLS 1.3简化为1-RTT。理解握手过程是回答HTTPS安全性问题的基础。

💡 面试高频 | 安全通信必考 | IoT设备TLS是趋势

Terminal window
1
简化的TLS 1.2握手过程:
2
3
Client Server
4
5
│──── ClientHello ─────────────────→│ (支持的密码套件/随机数)
6
7
│←─── ServerHello ─────────────────│ (选定密码套件/随机数)
8
│←─── Certificate ─────────────────│ (服务器证书)
9
│←─── ServerHelloDone ─────────────│
10
11
│──── ClientKeyExchange ──────────→│ (预主密钥,用服务器公钥加密)
12
│──── ChangeCipherSpec ───────────→│ (切换到加密模式)
13
│──── Finished ───────────────────→│ (验证握手完整性)
14
15
│←─── ChangeCipherSpec ────────────│
5 collapsed lines
16
│←─── Finished ────────────────────│
17
18
│←──── 加密通信 ──────────────────→│
19
20
嵌入式TLS: mbedTLS(轻量) / wolfSSL(适合MCU)

Q73: 粘包和拆包问题?如何解决?

💡 面试高频 | TCP编程必考 | 牛客面经高频 | 嵌入式串口也有类似问题

🧠 秒懂: TCP字节流没有消息边界。粘包:read一次读到多条消息混在一起。解决方案:定长协议、长度+数据、分隔符。嵌入式自定义协议必须考虑边界。

解决方案:

方案原理适用场景
固定长度每个包固定N字节简单场景/定长数据
分隔符用\n或特殊字符分隔文本协议(AT指令)
长度前缀包头带长度字段最通用★
自定义协议帧头+长度+数据+CRC嵌入式首选
1
// 嵌入式最常用: 长度前缀协议
2
// 帧格式: [0xAA] [0x55] [LEN] [CMD] [DATA...] [CRC]
3
typedef struct {
4
uint8_t header[2]; // 帧头: 0xAA 0x55
5
uint8_t length; // 数据长度
6
uint8_t cmd; // 命令字
7
uint8_t data[256]; // 数据
8
uint8_t crc; // 校验
9
} frame_t;

Q74: socket编程的基本流程?

🧠 秒懂: TCP服务端:socket→bind→listen→accept→read/write→close。客户端:socket→connect→read/write→close。UDP:socket→bind→sendto/recvfrom。核心流程面试必须脱口而出。

💡 面试高频 | 网络编程基础 | 手画流程图

1
TCP服务端: TCP客户端:
2
socket() socket()
3
│ │
4
bind() │
5
│ │
6
listen() │
7
│ │
8
accept() ←─── 三次握手 ───── connect()
9
│ │
10
recv/send ←────────────────→ recv/send
11
│ │
12
close() ←──── 四次挥手 ───── close()