你在 Controller 里写了一个 @GetMapping,浏览器敲下回车,数据就回来了。
可你有没有想过,这短短几十毫秒里,你的数据经历了多少次“变装”和“安检”?
从 HTTP 报文到 TCP 段,再到 IP 包、以太网帧——每一层都在给数据“穿衣服”。
对端收到后,再一层层“脱衣服”。这就是 OSI 七层模型 和 TCP/IP 四层模型 干的事。
而你在 SpringBoot 里写的每一行代码,都对应着其中某一层的组件。

大家好,我是 Evan,一个从“只会写 Controller”到“能用 tcpdump 抓包分析”的 Java+AI 学生。
今天,我不背八股,而是用一次真实的 SpringBoot 请求,带你走一遍 数据从你的代码到网线,再从网线回到你的代码 的完整旅程。
读完这篇,你再看到 @RestControllerRestTemplateSocketNetworkInterface,都会会心一笑:哦,原来你在这里!

📌 从一次 SpringBoot 请求说起

假设你有一个 SpringBoot 服务,提供了一个 GET 接口 /user/123,返回 JSON。
客户端(可能是浏览器、Postman、另一个微服务)发来请求。

在开发者眼里:HTTP 请求 → Controller → 返回 JSON。
在计算机眼里:这是一场数据在七层(或四层)模型中的接力赛。

一、OSI 七层 vs TCP/IP 四层:一张表看懂

二、你的数据“穿衣服”与“脱衣服”过程

2.1 发送端:从上往下封装(穿衣服)

每一步都在“套娃”

  • 应用层:你的 JSON 数据加上 GET /user/123 HTTP/1.1Host: localhost 等 HTTP 头。

  • 传输层:加上源端口、目标端口、序列号、校验和等 TCP 头(如果是 TCP)。

  • 网络层:加上源 IP、目标 IP、TTL 等 IP 头。

  • 链路层:加上源 MAC、目标 MAC、类型等以太网头,以及尾部校验(FCS)。

2.2 接收端:从下往上解封装(脱衣服)

接收端收到比特流后,反向操作:

你的 SpringBoot Controller 收到的是已经“脱光”的应用层数据,直接就是 HTTP 请求体里的 JSON。

三、每一层对应 Java 中的什么组件?

3.1 应用层(你最熟)

  • @RestController + @RequestMapping:定义 HTTP 资源。

  • RestTemplate / WebClient:发起 HTTP 请求。

  • HttpServletRequest / HttpServletResponse:操作 HTTP 报文。

  • 序列化库(Jackson、Gson)处理 JSON/XML → 这其实在 OSI 中属于表示层,但在 TCP/IP 中被归入应用层。

3.2 传输层(你时常打交道)

  • Socket / ServerSocket:TCP 编程。

  • DatagramSocket:UDP 编程。

  • Netty 的 ChannelPipeline 中的编解码器(如 LengthFieldBasedFrameDecoder)处理 TCP 粘包。

  • SocketChannel + Selector:NIO 非阻塞传输。

例子:你调用 new Socket("localhost", 8080),就是在告诉操作系统:我要在传输层建立一个 TCP 连接。

3.3 网络层(你可能没直接写过,但一直在用)

  • InetAddress.getByName("www.baidu.com"):DNS 解析,得到 IP 地址。

  • NetworkInterface:获取本机 IP、网络接口信息。

  • Linux 命令 ip routeiptables:配置路由和 NAT,影响网络层。

  • Java 本身没有直接写 IP 包的 API(除非用 JNI 或 Socket 的 IP_TTL 等选项)。

代码示例

// 获取本机所有 IP
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
while (nets.hasMoreElements()) {
    NetworkInterface ni = nets.nextElement();
    ni.getInterfaceAddresses().forEach(addr -> 
        System.out.println(ni.getName() + " -> " + addr.getAddress()));
}

3.4 链路层(你很少直接碰,但离不开)

  • NetworkInterface 可以获取 MAC 地址。

  • ARP 协议(IP 找 MAC)由操作系统自动完成,Java 无直接 API。

  • 在 Docker 容器中,你可以看到虚拟网卡(如 eth0)的 MAC 地址。

// 获取 MAC 地址
NetworkInterface ni = NetworkInterface.getByName("eth0");
byte[] mac = ni.getHardwareAddress();

3.5 物理层(Java 管不到)

网卡、网线、WiFi 信号。这是硬件和驱动的领域。

四、一个完整的“穿衣服”故事

假设你用 RestTemplate 发 GET 请求到 http://192.168.1.100:8080/user/1

RestTemplate rest = new RestTemplate();
String result = rest.getForObject("http://192.168.1.100:8080/user/1", String.class);

底层栈

  1. 应用层RestTemplate 构建 HTTP GET 请求(URL、headers 等),交给 HttpURLConnection

  2. 传输层:操作系统创建 TCP socket,与 192.168.1.100:8080 三次握手,然后将 HTTP 请求数据作为 TCP 载荷发送。

  3. 网络层:本机 IP(如 192.168.1.50)和对方 IP 192.168.1.100 被封装成 IP 包,路由表决定从哪个网卡发出。

  4. 链路层:通过 ARP 获取对方 MAC(或网关 MAC),封装以太网帧,通过网卡发出去。

  5. 物理层:网线或 WiFi 传输电信号/电磁波。

接收端反向操作,最终 String result 拿到响应 JSON。

五、为什么分层这么重要?

  • 解耦:你改应用层代码(比如从 HTTP 换到 HTTPS),不需要重写 TCP 栈。

  • 复用:TCP 层可以同时为 HTTP、FTP、SMTP 服务。

  • 标准化:不同厂商的设备(思科路由器、Intel 网卡、你的 Java 程序)可以互联。

  • 排错:网络不通时,从物理层往上逐层排查(看网卡灯、ping IP、telnet 端口、抓 HTTP 包)。

Java 开发者的启发

  • 当你的 HTTP 请求超时时,不一定是应用层问题,可能是 TCP 握手失败(防火墙)、IP 路由不可达、或者 ARP 解析不到 MAC。

  • 使用 tcpdump 抓包时,你能看到每一层的头部信息,帮你定位问题。

📝 总结

核心结论
OSI 七层模型不是象牙塔里的理论,它就是你每一次 RestTemplate 调用、每一次 Socket 连接背后的真实工程架构。
理解分层,你才能从“调接口的人”变成“懂网络的人”。

🤔 思考题
你用 RestTemplate 调用 http://localhost:8080/hello。请求成功返回。
现在你把 localhost 换成 127.0.0.1,依然成功。
问题:当目标 IP 是 127.0.0.1(回环地址)时,数据包会经过物理层和真正的网卡吗?从 OSI 模型的角度,它在哪一层被“折返”了?
(提示:考虑回环接口的链路层行为)

欢迎在评论区留下你的答案 —— 下一篇我会聊聊 “TCP 粘包与拆包:Netty 中的解码器是如何解决‘边界丢失’的?”

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐