文章目录

下面我会完整解释 OSI 七层模型的每一层作用、在代码编程中的具体体现、拆包/组包属于哪一层
我会结合你日常 C++ / Qt / 网络开发的视角讲解,让你能“看到”每一层在代码里出现在哪里。


🏛️ OSI 七层模型:作用 & 代码开发中的体现

我用一句话+工程场景快速说明每一层。


第 7 层:应用层(Application Layer)

📌 作用

提供应用程序直接使用的高级协议和接口。

📌 代码中体现

你写的绝大多数逻辑都在这一层!

  • HTTP 请求/响应
  • WebSocket 逻辑
  • MQTT publish/subscribe
  • FastDDS/Protobuf 数据结构
  • Qt 网络高层模块(QNetworkAccessManager)

📌 示例

QNetworkAccessManager mgr;
QNetworkRequest req(QUrl("http://example.com"));
mgr.get(req);

你调用的是HTTP客户端,属于 应用层协议的调用


第 6 层:表示层(Presentation Layer)

📌 作用

  • 数据格式化
  • 序列化/反序列化
  • 加密/解密
  • 压缩/解压

📌 代码中体现

应用最常出现的就是 序列化 / 数据格式转换

  • JSON / XML 解析
  • Protobuf 序列化(FastDDS IDL)
  • Base64 编码/解码
  • TLS/SSL 加密传输

📌 示例(Protobuf)

message Person {
  string name = 1;
  int32 id = 2;
}

这是典型的表示层工作:结构编码、格式定义、二进制序列化


第 5 层:会话层(Session Layer)

📌 作用

管理会话、连接维持、断线重连、状态保持

📌 代码中体现

你使用的:

  • WebSocket 长连接
  • MQTT 会话(Session)
  • token 登录后保持状态
  • FastDDS Participant 的 Session 管理

这些都是会话层工作。

📌 示例

websocket->open(QUrl("ws://server"));

保持连接、重连逻辑,就属于会话层。


第 4 层:传输层(Transport Layer)

📌 作用

提供端到端通信,端口号就在这一层:

  • TCP(可靠流)
  • UDP(不可靠包)

📌 代码中体现

如果你用以下 API,就是在“自己实现传输层”:

QTcpSocket
QUdpSocket
select/epoll
recv/send

你在处理:

  • 分片重组(TCP 自动)
  • 重传(TCP 自动)
  • 超时(TCP/UDP 都需要处理)

第 3 层:网络层(Network Layer)

📌 作用

管理网络地址与路由

  • IPv4 / IPv6
  • ICMP
  • 路由转发
  • ARP/NDP 逻辑与邻居发现(辅助)

📌 代码中体现

一般开发者不会直接写网络层,但常用到:

  • 设置 IP、子网掩码
  • 构造原始 IP 报文(Raw Socket)
  • Ping (ICMP)
  • 路由表操作

📌 示例(构造 ICMP Ping)

socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

这是手写网络层协议。


第 2 层:数据链路层(Data Link Layer)

📌 作用

局域网内进行帧的传输

  • MAC 地址
  • ARP 协议
  • 交换机学习 MAC
  • VLAN / PPPoE

📌 代码中体现

一般程序员不写二层,但在下面情况会接触到:

  • 抓包(pcap/WinPcap/NPcap)
  • 构造以太网帧
  • 解析 MAC / VLAN 标签
  • ARP 处理或分析
  • 二层过滤器(BPF)

📌 示例(抓包)

pcap_loop(handle, 0, packet_handler, NULL);

在回调里你能看到完整的二层帧结构:

| Ethernet Header | IP Header | TCP Header | Data |


第 1 层:物理层(Physical Layer)

📌 作用

负责比特流传输

  • 电信号
  • 光信号
  • 网线/光纤
  • 调制与解调

📌 代码中体现

几乎不会写,但会遇到:

  • 串口波特率
  • 网络带宽与 MTU 限制
  • 信号质量
  • 硬件丢包统计

🎯 “拆包 / 组包”属于哪一层?

取决于你拆/组的是什么协议:

你处理的协议 属于哪一层 拆包/组包属于 示例
Qt / HTTP / MQTT / WebSocket 应用层 7 层 JSON、MQTT Publish、HTTP Header
自己封装协议(自定义命令帧) 表示层/应用层 6/7 层 “头+长度+CRC+数据”
UDP/TCP 粘包、拆包 传输层 4 层 Stream → Message
实现 ICMP/IP 头部 网络层 3 层 Raw Socket
以太网帧封装、抓包解析 数据链路层 2 层 Ethernet + ARP + IP

🔥 在你日常 C++/Qt/FastDDS 开发中,各层体现在哪里?

层级 你经常做的事情
应用层 Qt UI、业务协议、JSON、MQTT、HTTP
表示层 Protobuf、结构体序列化、协议格式定义
会话层 WebSocket、MQTT 会话保持、DDS Participant
传输层 UDP/TCP 收发、粘包拆包
网络层 IP 地址、子网设置、Raw Socket、ICMP
数据链路层 抓包、ARP 分析、MAC 地址处理
物理层 几乎不写,但受 MTU 影响

🧠 总结(非常关键)

✔ 应用层是你平时写得最多的(HTTP、MQTT、DDS)

✔ 表示层是序列化/反序列化(Protobuf、JSON)

✔ 会话层是保持连接(WebSocket、MQTT、DDS)

✔ 传输层是端口、TCP/UDP、粘包拆包

✔ 网络层是 IP 地址、ICMP、路由

✔ 链路层是 MAC、ARP、以太帧

✔ 物理层是信号、比特流


🟦 1. 什么是 MTU?

MTU(Maximum Transmission Unit)= 最大传输单元

👉 它指的是“二层以太网帧中 IP 包的最大大小”
常见值:

网络类型 MTU
有线以太网(Ethernet) 1500 字节(最常见)
PPPoE (宽带拨号) 1492
VPN(IPsec) 1400~1472
IPv6 自动配置 1280(最低要求)

⚠️ MTU 是链路层(第 2 层)的限制

它限定的是 IP 包的最大大小(不含以太网头)


🟦 2. UDP 发送“一包有效数据”需要被包装多少层?(非常关键)

你的业务数据会被逐层加头:


最终从应用发出去的完整链路层帧结构如下:

┌──────────────────────────────┐
│  以太网帧(Ethernet II)      │  ← 第 2 层
│  ┌──────────────────────────┐│
│  │ IP Header (IPv4/IPv6)    │  ← 第 3 层
│  │ ┌────────────────────────┤│
│  │ │ UDP Header             │  ← 第 4 层
│  │ │ ┌──────────────────────┤│
│  │ │ │   UDP Payload(你的数据) ← 第 7/6 层
│  │ │ └──────────────────────┘│
│  │ └──────────────────────────┘│
│  └──────────────────────────────┘
└──────────────────────────────────┘

🧱 UDP 包数据的最小开销是多少?

层级 Header 大小
UDP header 8 字节
IPv4 header 20 字节(无选项)
IPv6 header 40 字节
Ethernet II header 14 字节
Ethernet FCS(尾部 CRC) 4 字节

🧮 以太网 + IPv4 + UDP 的实际包大小计算

➤ 要发送 N 字节业务数据

完整帧大小:

以太网头(14) +
IP头(20) +
UDP头(8) +
数据(N) +
FCS(4)

➤ 加起来:

项目 大小
Ethernet Header 14B
IP Header 20B
UDP Header 8B
FCS 4B
合计包头开销 46 字节

因此一个 UDP 包有效负载 N 字节
最终在网线上发送的大小约为:

N + 46 字节

🟦 3. MTU 限制能放多少 有效数据(Payload)

MTU = 1500,指的是 IP 层的最大包

IP Header + UDP Header + Payload <= 1500

🧮 所以最大 Payload:

1500 - 20(IP) - 8(UDP) = 1472 字节

UDP 单包最大有效数据 = 1472 字节(IPv4)

如果超过这个数 → IP 分片 → 丢包概率暴增。


🟦 4. IPv6 情况

IPv6 Header = 40 字节:

1500 - 40(IP6) - 8(UDP) = 1452 字节

🟦 5. 总结:一包 UDP 数据经过几层封装?

最完整答案:4 层封装 + 2 层头尾

是否加头 描述
应用层 你自己的协议格式 自定义数据
传输层 UDP header (8B) 端口号 + 长度
网络层 IP header (20B/40B) 源 IP、目标 IP
数据链路层 Ethernet header (14B) MAC 地址
数据链路尾部 FCS 校验 (4B) CRC32

🟩 最终发送出去的包 必须包含:

  1. 应用层数据 N 字节
  2. UDP 头 8 字节
  3. IP 头 20(IPv4) 或 40(IPv6)
  4. 以太网头 14
  5. 以太网尾 FCS 4

🟩 为什么 UDP 不可能粘包?

🟥 为什么 TCP 一定会粘包?(也会拆包)

读完你就不会再被网上那些模糊说法误导。


🟦 一、UDP 为什么不会粘包?

因为 UDP 是“消息(报文)边界协议”——Message-Oriented Protocol。

一次 sendto() 对应一次 recvfrom()。
一发一收,永远是一个完整的包。


🧩 原因 1:UDP 包在链路层就是独立的“帧”

每一个 UDP 数据包在网络中都是一个独立的 IP 包

[IP Header][UDP Header][Payload]

每个包都有:

  • 包长度字段
  • 校验字段
  • 独立的发送、转发、接收过程

路由器永远不会把两个 UDP 包合并,也不会拆开(除非分片)


🧩 原因 2:recvfrom() 会一次性取回完整的一个 UDP 包

recvfrom(sock, buf, sizeof(buf), 0, ...);
  • 如果接收缓冲区够大 ⇒ 你永远得到完整的一个包
  • 如果太小 ⇒ 包 被丢弃(系统直接丢,应用拿不到任何数据)

‼️ 不会出现半包情况(不会收到前半部分)
‼️ 不会粘在一起(不会收到两个包合并)


🧩 原因 3:内核缓冲区以“包”为单位排队

UDP 接收队列是:

包1
包2
包3
……

不是像 TCP 那样的连续字节流。


✅ **结论:UDP 不会粘包,也不会拆包。

只可能“丢包”。**


🟥 二、那为什么 TCP 会粘包?

因为 TCP 是字节流协议(Stream-Oriented Protocol)

TCP:

  • 没有边界
  • 没有包的概念
  • 只有字节序列

现实中你收到的数据像这样连续:

ABCD|EFGHIJKL|MNOPQRST

但你的应用程序可能这样读取:

recv 读到:  ABCDEFG
下一次读: HIJKLMNOPQRST

你不知道这些字节原来是哪几次 send() 发出来的。


🟦 TCP 粘包 + 拆包 的三个主要原因

🔶 1. Nagle 算法(合并小包,提高效率)

TCP 认为频繁发小包浪费性能,于是自动合并。

例:

send("A");
send("B");
send("C");

TCP 可能合成:

ABC

🔶 2. TCP 是流,不会保留“消息边界”

发送三次:

send(100字节)
send(50字节)
send(80字节)

接收可能是:

recv(120字节)
recv(110字节)

你根本不知道哪里切。


🔶 3. MTU 决定了数据是否会被拆包

例如你 send() 了 5000 字节的数据,
以太网 MTU 是 1500,所以:

5000 → 1500 + 1500 + 1500 + 500(IP 分片)

接收端会逐段收到。


🟦 关键结论:

UDP:一发一收,永远完整,没有边界问题

TCP:流式,需要你自己定义消息边界


🟩 那么 TCP 如何处理粘包?(非常重要)

你必须在应用层定义协议格式,例如:

📌 方法 1:固定包长(简单但死板)

每个包固定 64 字节

📌 方法 2:包头 + 长度字段(最常用)

包格式:

[4 字节长度][数据...]

接收端:

  1. 先读 4 字节
  2. 得到 payload 长度
  3. 按长度读取剩余数据
  4. 完成一帧解析

这是 所有框架正在用的方法(QT、FastDDS、protobuf RPC 等)


📌 方法 3:特殊分隔符(如 \n)

HTTP 就使用 CRLF 分隔行。


🟦 总结对比表

项目 UDP TCP
是否会粘包 ❌ 不会 ✔ 会
是否会拆包 ❌ 不会(小 buffer 会丢) ✔ 会
是否保持消息边界 ✔ 是 ❌ 否
是否丢包 ✔ 可能 ❌ 不会
顺序是否保证 ❌ 不保证 ✔ 保证
是否可靠 ❌ 不可靠 ✔ 可靠
典型应用 视频、语音、传感器 Web、文件传输、数据库

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐