gOdm@'s BLOG

网络模型之上两层讲解

版权信息

warning

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


对于我,如果不是想要深入计算机网络,只是做应用的话,上两层需要理解得更深一点,下三层只需了解即可

1. 传输层

如果说网络层(IP层)解决的是“如何把数据从主机 A 送到主机 B”,那么传输层解决的就是“如何把数据从主机 A 的某个程序,送达主机 B 的另一个特定程序”。这被称为端到端(进程到进程) 的通信。

1.1. 传输层的核心作用

1.2. 传输层协议TCP和UDP

传输层最主要的两个协议。它们具有两种不同的设计哲学。

1.2.1. TCP(传输控制协议,Transmission Control Protocol)

1.2.2. UDP(用户数据报协议,User Datagram Protocol)**

1.3. 补充1:TCP的“三次握手”“四次挥手”

TCP为了建立可靠的数据连接,会在连接时进行“三次握手”,在断开连接时“四次挥手”。

1.3.1. “三次握手”

当你调用 Socket API 的 connect() 函数时,底层网络栈就开始执行三次握手。其核心目的是:双方互换初始序列号(Sequence Number,简称 seq),并确认双方的收发能力都正常。

为了方便理解,我们把主动发起连接的设备称为客户端(Client),等待连接的称为服务端(Server)

1.3.2. “四次挥手”

当你调用 close() 函数准备关闭 Socket 时,底层执行的是四次挥手。TCP 是全双工通信(数据可以同时双向流动),这就意味着关闭连接也必须是双向独立的。一方不想发数据了,不代表另一方也没数据要发了。

任何一方都可以主动发起挥手,我们假设还是客户端先发起。

1.3.3. 为什么建立是三次,断开却是四次?

1.4. 补充2:UDP的通信

UDP是无连接的协议。

UDP把极致的速度和灵活性交给了开发者。代价就是开发者必须自己承担处理网络不确定性的风险。如果你使用 UDP,但又想保证数据不丢,你必须在自己的应用层代码里实现一套简单的确认和重传机制。

2. 应用层

应用层(Application Layer)位于最顶层。它是整个网络体系结构中唯一直接与应用程序和终端用户交互的一层。

如果说底下的四层解决了“如何将数据从A点可靠/不可靠地搬运到B点”的底层硬件通信和路由问题,那么应用层解决的就是“这些数据代表什么,以及应用程序应该如何去理解和处理这些数据”。可以说,如果没有应用层,通信双方收到的只是一连串毫无意义的 01 二进制流。

2.1. 应用层的核心作用

2.2. 常见的应用层协议

应用层包含了数量最庞大、种类最繁多的协议,以满足各种不同的业务需求:

2.3. 应用层在嵌入式开发者中的视角

从底层系统或 RTOS 开发的视角来看,应用层的界限非常清晰:

当你在开发一个带网络功能的嵌入式系统时,底层通常由芯片厂商提供的网络协处理器或软件协议栈(如 LwIP)来包揽物理层到传输层(MAC/PHY 控制、IP 路由、TCP/UDP 封装)。而应用层的工作就是你手写的代码逻辑。

  1. 打包(发送方向): 将裸数据(如传感器采集的 ADC 原始值或计算后的浮点数)按照指定的格式(比如 JSON 字符串)拼接好,加上特定协议(如 MQTT)的固定报头(Header),然后调用类似 send()write() 的 Socket 函数,把这坨内存数据丢给传输层。

  2. 解析(接收方向): 从 Socket 缓冲区里 recv() 读出一条字节流。底层只保证这是无误的字节,而你的应用层代码需要去解析这段字节——提取命令、校验应用层业务逻辑,最后驱动硬件执行动作(比如控制继电器或调节 PID 参数)。

2.4. 应用层和传输层之关系

  1. 服务提供者与消费者的关系 (Socket 接口)

    传输层是服务提供者,应用层是服务消费者。 它们之间的物理/代码界限通常就是 Socket API(套接字接口)。当你在应用层写代码时(无论是 Python 脚本还是 C 语言的嵌入式程序),你不需要手写 TCP 三次握手的逻辑。你只需要调用 socket() 创建一个套接字,指定它是 TCP 还是 UDP,然后调用 connect()send()recv()。操作系统内核(或者 RTOS 中的 LwIP 协议栈)会在底层自动帮你处理复杂的传输层状态机。

  2. 数据封装的关系

    当应用层产生数据(Payload)并准备发送时,数据会向下传递给传输层。传输层不会关心应用层的数据是什么内容(无论它是 JSON 还是加密的二进制码),它只把这些数据当成一个整体。传输层会在这些数据前面加上自己的报头(Header),其中最关键的就是源端口号目标端口号

    • 应用层数据 + TCP 报头 = TCP 报文段(Segment)

    • 接收端则是一个逆向的“拆快递”过程:传输层剥离 TCP 报头,检查端口号,然后将里面的“纯应用层数据”顺着 Socket 丢给对应的应用程序去解析。

3. Socket 套接字概述

前面我们聊了应用层和传输层,也提到了它们之间的桥梁就是 Socket(套接字)。现在我们就简单了解一下套接字。

3.1. 什么是套接字?

“Socket” 这个词的本义是“插座”。想象一下你家墙上的电源插座:插座背后隐藏着极其复杂的国家电网(发电厂、变电站、高压线),但作为用户,你完全不需要懂电力工程,你只需要把电器的插头(Plug)插进插座,就能通电。

在网络世界里也是一样:

通过这组 API,你的应用程序就可以把数据“插”进网络中,或者从网络中“拔”出数据,而无需手写底层的协议控制代码。

3.2. 一切皆文件

你一定知道 Unix/Linux 的核心哲学:一切皆文件(Everything is a file)

Socket 完美地贯彻了这一哲学。在内核层面,Socket 其实就是一个特殊的文件。

当你调用 socket() 函数创建一个网络连接时:

  1. 操作系统内核会为你分配一小块内存(通常包含发送缓冲区和接收缓冲区),并维护这个连接的各种状态(比如 TCP 的 ESTABLISHED 状态)。

  2. 内核会向你的应用程序返回一个 文件描述符(File Descriptor,简称 fd,本质上就是一个整数索引)

从这一刻起,在代码层面,操作网络就变得和操作普通的本地文件,或者是操作一个 UART 串口、一个 I2C 设备节点没有任何区别了。

3.3. Socket 必要的参数

如何确定一个独一无二的 Socket 通道?内核通过一个经典的 “五元组” 来精确定位一个 Socket 通信:

  1. 协议(Protocol): TCP 还是 UDP?

  2. 本地 IP 地址: 你的设备可能有多张网卡(比如同时连着 Wi-Fi 和以太网),走哪个 IP?

  3. 本地端口(Local Port): 你的应用在哪个端口?

  4. 目标 IP 地址: 数据要发给哪台机器?

  5. 目标端口(Remote Port): 对方机器上的哪个程序来接这个数据?

只要这五个参数确定了,一条端到端的逻辑数据线就确立了。

3.4. Socket 的生命周期

以Berkeley Sockets(BSD Sockets)标准接口为例。我们以一个 TCP 服务端为例,看看它的全生命周期:

  1. socket():买个新插座

    向内核申请创建一个指定协议(TCP/UDP)的 Socket 文件描述符。

  2. bind():把插座钉在墙上

    把这个 Socket 和某个特定的本地 IP 及端口号绑定起来。意思是:“我就守在这个端口听消息了。”

  3. listen():通电并开始接客(仅限 TCP)

    告诉内核,这个 Socket 不是用来主动发起连接的,而是用来被动等待别人连接的。内核会为此准备一个等待队列。

  4. accept():诞生一个“分身”插座

    这是服务端最核心的一步。当客户端的三次握手完成时,accept() 会被唤醒。注意:它不会用原来的那个 Socket 和客户端通信,而是由内核动态“克隆”出一个全新的 Socket 文件描述符(专用于跟这个客户端聊天),而最初的那个 Socket 继续回去监听新客户。

  5. send() / recv():读写数据

    在应用层缓冲区和内核 Socket 缓冲区之间搬运字节流。

  6. close():拔掉插头,拆毁插座

    触发四次挥手,释放内核中的控制块(TCB)内存,回收文件描述符。

3.5. MCU/SoC上的Socket

在资源受限的嵌入式系统中(比如跑在微控制器 MCU 上的 RTOS),由于内存极其有限,通常跑不动完整的 Linux 庞大网络栈。

这个时候,通常会使用轻量级的网络协议栈(比如最著名的 LwIP)。LwIP 在底层精简了大量的内存拷贝操作,但为了方便开发者,它依然在最上层封装了一套与标准 BSD Sockets 几乎一模一样的 API。

3.6. 总结

无论你是写一个庞大的后端服务器,还是仅仅想让一个小小的芯片通过传感器采集数据并推送到云端,Socket 就是那个屏蔽了所有底层硬件交互、网络路由和协议状态机的“超级隔离带”。你只要会读写 Socket 这个特殊的“文件”,就能玩转整个网络通信。


共计约4.7k字。于2026/04/20首次发布,最后更新于2026/04/20。

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

  1. 1. 传输层
    1. 1.1. 传输层的核心作用
    2. 1.2. 传输层协议TCP和UDP
    3. 1.3. 补充1:TCP的“三次握手”“四次挥手”
    4. 1.4. 补充2:UDP的通信
  2. 2. 应用层
    1. 2.1. 应用层的核心作用
    2. 2.2. 常见的应用层协议
    3. 2.3. 应用层在嵌入式开发者中的视角
    4. 2.4. 应用层和传输层之关系
  3. 3. Socket 套接字概述
    1. 3.1. 什么是套接字?
    2. 3.2. 一切皆文件
    3. 3.3. Socket 必要的参数
    4. 3.4. Socket 的生命周期
    5. 3.5. MCU/SoC上的Socket
    6. 3.6. 总结