一个数据包从浏览器到服务器,经历了什么?(OSI七层)

3 阅读18分钟

OSI 七层与 TCP/IP 四层

一句话:分层不是让你背的,是让你出问题时知道该去哪儿看。

阅读路径:先看两张模型(二、四)→ 再看一个包怎么走(五、六)→ 最后看怎么用它排错(七)。

一、先搞懂"分层"到底在分什么

把数据从 A 送到 B,要做很多事:找地址、保证不丢、加密、找应用……如果全塞给一个程序做,根本没法维护。

所以拆成流水线,每一层只干一件事,只关心自己的头:

应用层  →  "这是什么业务"        (HTTP/FTP/DNS)
传输层  →  "交给哪个进程"        (端口)
网络层  →  "送到哪台机器"        (IP)
链路层  →  "下一跳是谁"          (MAC)
物理层  →  "怎么变成电信号"      (网线/光纤)

核心思想:每一层只加自己的头,不关心别人加了什么。 这就是"信封套信封"。

二、OSI 七层(理论模型)

层名称一句话职责典型协议设备
7应用层给用户/程序提供接口HTTP、FTP、DNS、SMTP、SSH—
6表示层编码、压缩、加密TLS/SSL、JPEG、ASCII—
5会话层建立/管理/终止会话RPC、NetBIOS—
4传输层进程到进程通信TCP、UDP—
3网络层主机到主机寻址与路由IP、ICMP、IGMP路由器、三层交换机
2数据链路层帧、MAC 寻址Ethernet、ARP、VLAN交换机、网桥
1物理层比特流传输电信号/光信号网线、光纤、网卡

记忆口诀(自下往上):物、链、网、传、会、表、应。

三、TCP/IP 四层(实际模型)

3.1 应用层

对应 OSI 的应用层 + 表示层 + 会话层(三合一),为用户提供所需要的各种服务。

例如:FTP、Telnet、DNS、SMTP。

为什么三合一? 因为在实际实现里,编码、加密、会话管理都是应用自己处理的——浏览器自己管 Cookie 和会话,自己调 OpenSSL 做加密。OSI 把它们拆成三层,理论上很漂亮,但实践中没人这么实现。

3.2 传输层

对应 OSI 的传输层,为应用层提供端到端(进程到进程)的服务,保证数据包的顺序传送以及数据的完整性。

该层定义了两个主要协议:TCP(传输控制协议) 和 UDP(用户数据报协议)。

这一层最关键的是"端口"——它是应用层与传输层的接口:

IP 地址  →  哪台机器(大楼地址)
端口     →  哪个应用(房间号)
  • 往上:应用 bind() 占住 80 端口,说"80 的数据给我"
  • 往下:传输层看目的端口 80,把数据交给 nginx

同一台机器靠端口区分不同服务——这就是为什么 Web、SSH、MySQL 能同时跑在一台机上。

为什么叫"进程到进程",为什么是 TCP / UDP

一台服务器上同时跑着三个服务:

192.168.1.10
  ├── nginx  → 监听 80
  ├── sshd   → 监听 22
  └── mysql  → 监听 3306

一个包到达时,IP 头里只有"目的 IP",最多只能判断"这是给我这台机器的",没法判断"给哪个进程"。补上这一环的就是端口。

三种地址,对应三个层级:

层地址标识的是
数据链路层MAC 地址哪块网卡(下一跳)
网络层IP 地址哪台机器
传输层端口号哪个进程

所以"进程到进程"不是"必须由 TCP/UDP 来完成",而是这一层的职责是用端口把包交给正确的进程。TCP 和 UDP 只是承担这个职责的两个协议。

类比:"交通工具"不是"必须由汽车和火车组成",只是这两种最常见。

反例:ICMP 没有端口。

ping 走 ICMP,而 ICMP 头里根本没有端口字段——因为 ping 的回复由内核直接代答,不需要交给某个用户进程。

ICMP(ping):  跑在 IP 上,没有端口    ← 不需要区分进程
TCP / UDP:     跑在 IP 上,有端口      ← 要交给具体应用

这正好反过来证明:传输层的价值不在协议本身,而在"需要区分进程"这件事。

TCP 和 UDP 都解决"进程到进程",但服务级别不同:

TCPUDP
连接面向连接(三次握手)无连接
可靠性保证不丢、不重、有序不保证
传输方式字节流数据报(一次一个包)
速度慢一些快
典型应用HTTP、SSH、MySQL、FTPDNS、SNMP、视频流、游戏

一句话:TCP 是"打电话"(先接通、确认对方在听);UDP 是"寄明信片"(写好就扔出去)。

补充:传输层不只这两个,还有 SCTP、DCCP,以及 QUIC(HTTP/3 的底层,跑在 UDP 上)。说"定义了 TCP 和 UDP"指的是它们最主流。

3.3 网际互联层

对应 OSI 的网络层,主要解决主机到主机的通信问题。

该层有三个主要协议:IP(网际协议)、IGMP(互联网组管理协议)、ICMP(互联网控制报文协议)。

三个协议各干什么:

协议干什么
IP寻址 + 路由,把包从一台主机送到另一台
ICMP报错和探测(ping、traceroute 都靠它)
IGMP组播成员管理(一般业务用不到)

这一层怎么把包送到别的网段? 靠网关和路由表接力——详见第六节。

3.4 网络接口层

对应 OSI 的物理层和数据链路层,负责监视数据在主机和网络之间的交换。

地址解析协议(ARP,把 IP 解析成 MAC 地址)工作在此层,即 OSI 的数据链路层。

ARP 的位置有点特殊——它的职责是"用 IP 找 MAC",横跨网络层和链路层。教材一般归到链路层,但你在排障时要记住:ARP 不通,就等于这一跳走不过去。

四、两张模型的对照(这张表要背下来)

OSI 七层TCP/IP 四层对应关系
应用层应用层┐
表示层应用层├ 三合一
会话层应用层┘
传输层传输层一对一
网络层网际互联层一对一
数据链路层网络接口层┐ 二合一
物理层网络接口层┘
OSI  7                TCP/IP  4
应用 ┐
表示 ├──────→       应用层       (3 合 1)
会话 ┘
传输 ───────→       传输层
网络 ───────→      网际互联层
链路 ┐
物理 ┘──────→      网络接口层     (2 合 1)

验算:3 + 1 + 1 + 2 = 7 ✅

五、数据封装:一层加一个信封

这是理解整套模型的关键动作。发数据时从上往下"套信封",收数据时从下往上"拆信封"。

5.1 发送方:逐层加头

┌──────────────────────────────────────┐
│ 应用层:HTTP 报文                      │
│   GET /index.html HTTP/1.1            │
└──────────────────────────────────────┘
                ↓ 加 TCP 头(源端口 54321、目的端口 443)
┌─────────┬────────────────────────────┐
│ TCP 头  │        HTTP 数据            │  ← 叫「段 segment」
└─────────┴────────────────────────────┘
                ↓ 加 IP 头(源 IP、目的 IP)
┌─────────┬─────────┬──────────────────┐
│ IP 头   │ TCP 头  │     HTTP 数据     │  ← 叫「包 packet」
└─────────┴─────────┴──────────────────┘
                ↓ 加帧头(源 MAC、目的 MAC)+ 帧尾(FCS 校验)
┌────────┬─────────┬─────────┬─────────┬──────┐
│ 帧头   │ IP 头   │ TCP 头  │ HTTP数据 │ FCS  │  ← 叫「帧 frame」
└────────┴─────────┴─────────┴─────────┴──────┘
                ↓ 转成电信号/光信号
                0101010101010101                   ← 叫「比特 bit」

四种数据单元的叫法(面试爱问):

层单位英文
传输层段segment
网络层包packet
链路层帧frame
物理层比特bit

5.2 接收方:逐层拆头

物理层 收到电信号 → 还原成帧
链路层 拆帧头 → 检查 FCS,目标 MAC 是不是我?
网络层 拆 IP 头 → 目标 IP 是不是我?不是就转发
传输层 拆 TCP 头 → 目的端口 443 → 交给监听 443 的进程
应用层 拿到 HTTP 报文 → 处理

关键理解:每一层只拆自己那层头,不看别人的内容。

这也解释了为什么中间的路由器只看 IP 头就够——它不需要知道你在传 HTTP 还是 SSH。

5.3 一个必须记住的细节

IP 地址:端到端不变(源 IP 和目的 IP 从头到尾一样)
MAC 地址:每过一跳路由器就变(重写成下一跳的)

下一节就把这句话走一遍。

六、数据在中间怎么走:路由与网关

6.1 为什么必须有网关

二层(交换机 + MAC)只能在一个广播域里转发。192.168.1.10 想访问 10.0.1.5,它们根本不在一个广播域,MAC 寻址完全够不着。

跨网段必须靠三层转发,于是需要两样东西:

网关  →  告诉主机"出不了本网段,就把包交给谁"
路由  →  告诉中间设备"下一步该往哪踢"

6.2 网关和路由分别是什么

网关(Gateway)路由(Routing)
是什么一个 IP 地址,本网段的出口一套决策规则(路由表)
谁用主机用主机和转发设备都用
回答什么"我出不去,交给谁?""交给我之后,我再往哪?"
类比小区大门门卫手里的路线图

网关是主机的视角,路由是转发设备的视角。但主机自己也有一张路由表(至少一条直连 + 一条默认)。

6.3 一次跨网段的完整过程

       192.168.1.0/24              10.0.0.0/30            10.0.1.0/24
   ┌──────────────┐         ┌──────────────┐         ┌──────────────┐
   │      PC      │         │      R1      │         │      R2      │        ┌────────┐
   │ 192.168.1.10 │────────▶│ 192.168.1.1  │────────▶│  10.0.0.2    │───────▶│ Server │
   │    /24       │         │   10.0.0.1   │         │  10.0.1.1    │        │10.0.1.5│
   └──────────────┘         └──────────────┘         └──────────────┘        └────────┘
       网关=.1                 两边的"门"                  网关=.1
① PC 要发给 10.0.1.5 → 不在本网段 → 查路由 → 匹配默认路由 → 下一跳是网关 192.168.1.1

② PC 需要网关的 MAC
   → ARP 广播"谁是 192.168.1.1?"      ← 注意:问的是网关,不是目标主机

③ PC 发出第一帧
   源 MAC = PC,目的 MAC = R1 的 MAC     ← 链路层只到下一跳
   源 IP = 192.168.1.10,目的 IP = 10.0.1.5   ← 网络层是端到端

④ R1 收到,拆帧看目的 IP = 10.0.1.5
   → 查路由表 → 10.0.1.0/24 via 10.0.0.2
   → ARP 问 10.0.0.2 的 MAC
   → 重新封装:源 MAC = R1,目的 MAC = R2,IP 完全不变   ★ MAC 变了

⑤ R2 收到,目的网段我直连 → ARP 问 10.0.1.5 的 MAC
   → 发出最后一帧:源 MAC = R2,目的 MAC = Server,IP 依然不变

⑥ Server 回包:目的 IP 不在我的网段 → 交给它的网关 10.0.1.1 → 反向走一遍

把三帧摆在一起看:

链路段源 MAC目的 MAC源 IP目的 IP
PC → R1PCR1192.168.1.1010.0.1.5
R1 → R2R1R2192.168.1.1010.0.1.5
R2 → ServerR2Server192.168.1.1010.0.1.5

MAC 每跳重写,IP 从头到尾不变。 这就是 5.3 那句话的完整过程。

6.4 运维怎么用它排查

# ① 我有网关吗?
ip route | grep default
# 没有 default → 你根本出不了网段

# ② 去目标会走哪?
ip route get 8.8.8.8
# 8.8.8.8 via 192.168.1.1 dev eth0 src 192.168.1.10
#   ↑网关          ↑出口网卡    ↑源IP

# ③ 网关通吗?
ping -c 2 192.168.1.1
结果说明
ip route 里没有 default没配网关,只能内网通
ip route get 走的网关不对路由配错了,或有两条默认路由
ping 网关不通二层问题:网线、VLAN、交换机、ARP
ping 网关通、ping 目标不通三层之后的问题:路由、对端防火墙、对端主机
ping 目标通、端口不通不是网络层问题,去看传输层和服务

"ping 网关"是分水岭——它把"局部问题"和"远端问题"干净地切开了。

6.5 两个常见误区

① 网关必须和本机同网段

192.168.1.10/24 的网关不能写成 10.0.0.1——ARP 根本问不到它。

② 默认网关只能有一个有效

可以配多个,但只会用 metric 最小的那个。两条相同 metric 的默认路由会导致"时通时不通"。

七、运维视角:这套模型到底怎么用

你不是网络工程师,不需要会设计协议,需要会"按层定位问题"。

7.1 排障:按层找命令

层出问题会怎样用什么查
应用层502/504、证书错、服务没起curl -v、nginx -t、journalctl
传输层端口不通、连接被拒ss -lntp、nc -zv
网络层ping 不通、走错路由ping、ip route get、traceroute
链路层ARP 学不到、MAC 冲突ip neigh、tcpdump -e
物理层网卡 down、链路丢包ethtool、ip -s link

7.2 抓包时怎么判断

tcpdump -i any -nn port 80
抓到什么卡在哪
没有 SYN应用层没发起 / DNS 没解析出来
有 SYN,没 SYN+ACK网络层或传输层(对方没听 / 被防火墙 DROP)
有 SYN,回 RST传输层(端口没开,但机器是通的)
有请求,没响应应用层(服务处理慢或挂了)

7.3 四层 vs 七层负载均衡(你项目里的东西)

四层(LVS)七层(Nginx)
工作在哪层传输层(看 IP + 端口)应用层(看 URL、Header)
能看到什么只知道"要连 80 端口"知道"要访问 /api/users"
能做什么转发、负载均衡路径路由、改 Header、限流、缓存
性能高(不拆包)低一些(要解析 HTTP)
你的项目202 上的 LVS DR 模式前端 Nginx 的 /prod-api/ 反代

"LVS 快、Nginx 灵活"不是玄学,是它们在第几层干活的必然结果。

八、几个容易搞混的点

① ARP 到底属于哪层?

严格说它横跨网络层和链路层(用 IP 找 MAC)。教材通常归在链路层,你按"链路层"记就行,但排障时要知道它是跨层的东西。

② TLS 属于哪层?

OSI 里对应表示层/会话层的职责(加密、会话),但实际实现位于应用层和传输层之间。所以 HTTPS 可以说成"HTTP over TLS over TCP"。

③ 为什么网络层管"主机到主机",传输层管"进程到进程"?

网络层:把包送到 192.168.1.10 这台机器        ← 不管机器上跑什么
传输层:送到机器后,再交给 80 端口那个进程    ← 才真正交付

"送到楼"和"送到房间"的区别。

④ 隧道、VPN 多出来的层怎么办?

GRE/IPsec 这类隧道协议会在原有头部外面再套一层 IP,所以有人叫它"2.5 层"或"3.5 层"。这也是为什么隧道场景下 MTU 会变小、大包容易不通。

九、共同点与不同点

共同点

  1. 都能提供面向连接和无连接两种通信服务机制
  2. 都具有层次结构

不同点

  1. OSI 是在协议开发前设计的,具有通用性;TCP/IP 是先有协议集、然后倒推建立模型,不适用于非 TCP/IP 网络
  2. 实际市场应用不同:OSI 只是理论模型,没有成熟产品;TCP/IP 已经成为**"实际上的国际标准"**

一句话记:OSI 是"先画图纸再造房子",TCP/IP 是"先盖了房子再补图纸"。 所以工程里用的永远是 TCP/IP。

疑惑:

同主机、同网段通信,到底能省掉哪一层?

一句话:这两个问题看起来都是"能不能少走一层",但真正的答案取决于——这一层有没有"下一跳"要定位。

一、先看总答案

场景想省掉的东西能省吗为什么
同一主机的两个端口TCP 三次握手❌ 不能TCP 是协议状态机,和距离无关
IP 头❌ 不能TCP 四元组要靠 IP 定位 socket
以太网帧头✅ 能回环没有下一跳,不需要 MAC
同一网段的两台主机网关✅ 能直连路由没有 via
MAC 寻址❌ 不能二层转发全靠 MAC,而且指向目标主机

记住这张表,两个问题的答案就都有了。


二、同一主机的两个端口通信

2.1 先纠正:"端到端"不是"跨机器"

层说法"端"是什么
数据链路层点到点相邻两个设备
网络层主机到主机两台机器
传输层端到端 / 进程到进程两个 socket
跨主机:  192.168.1.10:54321  →  1.2.3.4:80
同主机:  127.0.0.1:54321     →  127.0.0.1:80

结构完全一样,只是 IP 恰好相同。

2.2 三次握手:省不掉

TCP 握手要完成四件事,同主机一样都得做:

握手要完成的事同位主机为什么也必须做
交换初始序列号 ISN双方各生成随机 ISN,用于序号和确认
协商参数窗口大小、MSS、SACK、时间戳
确认双向可达两个 socket 都要能收发
建立全双工状态两个方向独立维护序号

同主机只是路径短(走 loopback 不出网卡),不是省了握手。

# 验证:抓回环网卡
tcpdump -i lo -nn port 8080
# 能看到完整的 SYN → SYN-ACK → ACK

2.3 IP 头:省不掉

IP 头在这里不是"用来找路",而是用来标识 socket 端点:

① TCP 四元组靠它

源 IP : 源端口  ←→  目的 IP : 目的端口
127.0.0.1:54321 ←→ 127.0.0.1:8080

② 路由查找仍然要做(命中 local 表)

ip route get 127.0.0.1
# local 127.0.0.1 dev lo src 127.0.0.1

③ iptables / 防火墙在 IP 层生效

iptables -A INPUT -i lo -j ACCEPT    # 很多系统默认放行 lo

封了 lo,127.0.0.1 也会被拦——说明它确实过了 IP 层。

2.4 以太网帧头:可以省

帧头的核心是源 MAC 和目的 MAC,作用是"找到下一跳的网卡"。而回环上:

  • 没有交换机,不需要下一跳 MAC
  • 没有 ARP,因为没有链路层寻址
  • 数据在内核内存里直接转交
# 抓 lo:输出从 IP 头开始,没有 ether 段
tcpdump -i lo -nn
# 12:00:01 IP 127.0.0.1.54321 > 127.0.0.1.8080: Flags [S] ...

# 抓 eth0:能看到 MAC
tcpdump -i eth0 -nn -e
# 12:00:01 00:0c:29:ab:cd:ef > 00:50:56:11:22:33, ethertype IPv4 ...

顺带:lo 的 MTU 是 65536(ip link show lo 可见),远大于以太网的 1500,所以回环基本不分片。

2.5 真正"什么都不加"的:Unix Domain Socket

TCP:  应用 → TCP → IP →(lo)→ IP → TCP → 应用
UDS:  应用 → 内核 socket 缓冲区 → 应用

没有 IP、没有端口、没有握手、没有校验和。 这才是同机通信里最快的方式。

/var/run/docker.sock          # Docker
/var/lib/mysql/mysql.sock     # MySQL

一个绝佳对照:连本机 MySQL 的两种方式

mysql -h 127.0.0.1 -P 3306 -u root -p        # 走 TCP,有三次握手
mysql -S /var/lib/mysql/mysql.sock -u root -p # 走 UDS,无握手

运维坑:开了 skip-networking 后,-h 127.0.0.1 连不上,但 -S socket 还能连——因为它们根本不是同一条路。

2.6 特别注意:容器是"同物理机",但不是"同主机"

容器A (172.17.0.2) ──veth──┐
                            ├── docker0 网桥 ── veth ── 容器B (172.17.0.3)
容器A 的网络命名空间         └─ 宿主机命名空间

容器之间通信:IP 头 + 以太网帧头 + ARP,一样都不能少。

"同一台物理机" ≠ "同一个网络命名空间"。这条对做 Docker / K8s 的人特别重要。


三、同一网段的两台主机通信

3.1 网关:不需要

因为目的地址在直连网段里,路由表直接命中:

ip route
# 192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.10
#                ↑ 只有出口网卡,没有 via

via 就是"要不要找网关"的分水岭:

路由条目有 via?含义
192.168.1.0/24 dev eth0❌直连,直接发
default via 192.168.1.1✅交给网关
10.0.0.0/8 via 192.168.1.254✅交给网关

3.2 MAC 寻址:必须要,而且指向目标主机

① PC1 想发给 192.168.1.20
   一算:同网段 → 不走网关

② ARP 广播:"谁是 192.168.1.20?"
   → PC2 回答:"我是,我的 MAC 是 B"

③ PC1 封装帧
   源 MAC   = A
   目的 MAC = B          ← 直接就是目标主机

④ 交换机查 MAC 表 → 从 B 连着的那条线发出去

交换机只认 MAC、不认 IP。 没有 MAC,它根本不知道该往哪个口发。

3.3 同网段 vs 跨网段

同网段跨网段
网关不用必须用
ARP 问谁目标主机网关
目的 MAC目标主机网关
目的 IP目标主机目标主机(不变)
谁转发交换机(二层)路由器(三层)

跨网段:IP 找网关,MAC 也找网关。
同网段:IP 直接找主机,MAC 也直接找主机。

3.4 什么情况下真的没有 MAC

MAC 是以太网的地址,换一种链路层技术就没有了:

链路类型有 MAC说明
以太网 / Wi-Fi✅最常见
回环 lo❌同机内部,内存转发
PPP / 串行链路❌严格点对点,只有两端
WireGuard 等 VPN❌虚拟点对点
InfiniBand❌用自己的 LID 寻址

判断标准:有"多个设备共享介质"就需要链路层地址;严格点对点就不需要。


四、把两个问题合成一条规律

看起来是两件事,其实是同一个判断:

问题:这一层能不能省?
答案:看它有没有"下一跳"要定位。

回环通信:没有下一跳 → 不需要帧头
         但仍需要 IP(因为要定位 socket)

同网段:  有下一跳(目标主机)→ 必须有 MAC,且指向目标主机

跨网段:  有下一跳(网关)→ 必须有 MAC,且指向网关

核心结论:

链路层地址的目的,永远是"下一跳",而不是"最终目标"。

没有下一跳(回环)→ 不需要链路层头。
有下一跳 → 必须有,只是"是谁"取决于同网段还是跨网段。


五、验证命令汇总

# 【同主机】
tcpdump -i lo -nn port 8080          # 能看到三次握手
tcpdump -i lo -nn                    # 输出从 IP 头开始,没有 ether 段
ss -tan | grep 127.0.0.1             # 看到 ESTABLISHED
ip route get 127.0.0.1               # local ... dev lo
ip link show lo                      # 看 MTU 65536

# 【同网段】
ip route get 192.168.1.20            # 没有 via → 同网段直连
ip neigh show 192.168.1.20           # lladdr 是目标主机的 MAC
tcpdump -i eth0 -nn -e host 192.168.1.20   # 看目的 MAC

# 【容器之间】
docker exec -it 容器 tcpdump -i eth0 -nn -e  # 有完整以太网头

六、面试速答

Q:同主机的两个端口通信要走三次握手吗?

要。TCP 是协议状态机,握手用来交换 ISN、协商参数、确认双向可达,和两台机器距离无关。同主机只是走 loopback 不出网卡,快在路径不在协议。

Q:同主机通信要加 IP 头吗?

要。TCP 四元组里包含 IP,内核要靠它找对应的 socket;防火墙也在 IP 层。但要加以太网帧头吗?不要——回环没有下一跳,不需要 MAC。

Q:同网段通信需要网关吗?

不需要。路由表命中的是直连路由,没有 via。但必须用 MAC,而且 ARP 问的是目标主机,不是网关。

Q:为什么同网段反而更依赖 MAC?

因为"同网段"的定义就是"我能用 ARP 直接问到目标主机的 MAC"。二层转发靠 MAC 表,没有 MAC 就送不出去。