news 2026/10/2 23:33:16

协议为什么存在-协议栈IP与MAC和缓冲区里的生产者消费者

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
协议为什么存在-协议栈IP与MAC和缓冲区里的生产者消费者

协议为什么存在:协议栈、IP 与 MAC、缓冲区里的生产者消费者

我的github:(https://github.com/xcx55/ubuntu-linux-project)
感谢各位大佬参观我的github!!

源笔记:http3(26-9-10)

学 HTTP 学到一半,这篇笔记突然掉头往下挖:为什么需要协议?协议栈为什么叫"栈"?IP 和 MAC 各管什么?发送缓冲区为什么是生产者消费者模型?这些问题平时不敢问,手搓过协议之后终于有底气问了。

一、为什么需要协议

反着问:计算机之间不约定行不行?——不行。

  • 计算机本身就是一种协议、一种约定——CPU 的指令集、总线的时序、文件的格式,全是约定;
  • 具体到通信:发给谁?保证传输控制特性等等!——传输需要协议,处理数据也需要协议;
  • 还有一个精妙的说法:消息是临界资源。缓冲区里的数据被两个"人"(用户和内核)共同读写,没有约定就会乱套。

而手搓过协议之后,你对协议的描述会变得更精确:协议 = 双方都认的报文格式 + 解析规则 + 行为约定。它不再是教科书上一句空话,而是你亲手写过的len\r\n value \r\n。

二、协议栈为什么叫"栈"

因为报头的添加和拆除,类似栈的加减!

  • 发送方向:数据从应用层往下走,每过一层就压入一层报头(应用层加包 → TCP 报头 → IP 报头 → MAC 帧)——像 push;
  • 接收方向:数据从底层往上走,每过一层就弹出对应报头、解析后交给上层——像 pop。

每一层协议收到数据后怎么解包?只需要回答两个问题:

  1. 怎么分离——报头和有效载荷怎么分开(定长报头?自描述字段?分隔符?);
  2. 交给谁——载荷里的内容该交付给上层的哪个协议(IP 报头里的上层协议字段、TCP 报头里的端口号,都是在回答这个问题)。

这两个问题是每一层的通用考题,从链路层一路问到应用层。

三、IP 与 MAC:目标不变,接力棒一直在换

一个反直觉的事实:

IP 不变,但 IP 指向的中转站——MAC 帧——是一直在变化的。IP 只是目标!

数据包从你的电脑到太平洋对面的服务器,路上要跳过很多路由器:每跳一跳,源 MAC 和目的 MAC 就被换一次(这一段的接力棒),但源 IP 和目的 IP 从头到尾不变(终点不变)。

  • IP 地址只需要4 个字节就能表示;
  • 还可以做一个推断:通过发送去那里的目的 IP,推断其目的 IP 是不是当前局域网的——网段一比对就知道对方是本网段还是外网,这直接决定了是直接投递还是交给网关。

网络层:屏蔽底层网络差异

为什么要发明 IP 这一层?往历史看:

  • 先有局域网,后有广域网;
  • 每一个局域网发明的技术是不一样的(以太网、令牌环……),后面广域网出来了,要全世界互联,必然要规避这种差异;
  • 所以分工定了:底层数据链路层保证局域网的实现,网络层 IP 协议保证广域网的实现;
  • 网络层以上,大家都是 IP 协议,一样!——让我们程序员看待网络:一切皆 IP。

一句话:网络层屏蔽了底层网络的差异。底层是什么类型的网络无所谓,IP 层以上统一世界观。

再补一条时间线:先有 MAC 地址,才有 IP 地址——网卡出厂全球唯一是硬件层的事,IP 是后来为了组大网而设计的逻辑层。

至于路由怎么找到全网唯一的 IP?靠逐跳转发 + 路由表(目的网段 → 下一跳),目标 IP 不变,接力棒一棒一棒传过去。

(插一句随手记:为什么使用大端?因为大端比较利于人类——高位在前的顺序和人类读数字的顺序一致,抓包打印出来一眼能对上。)

四、缓冲区:又一个生产者消费者模型

发送和接收数据,各有两个缓冲区。以发送缓冲区为例:

  • 进程向里面写数据,操作系统往里面读数据——生产者消费者模型!

所以两个主机之间跑 TCP:TCP 是由 4 个生产者消费者模型构成的(每台主机发送缓冲区一个 + 接收缓冲区一个)。

这个过程和文件何其相似——一切皆文件:

网络缓冲区文件内核缓冲区
用户做什么向缓冲区写数据write 到内核缓冲区
内核做什么创建线程把数据发送到网卡异步线程把数据刷入磁盘
何时刷,用户管不管管不了,OS 决定管不了,OS 决定
同步约束OS 往里放的时候用户不能写;OS 读的时候用户最好别读同左
  • 我们写文件,也只是写到内核缓冲区,后面操作系统什么时候刷到磁盘自己决定——内核缓冲区将数据刷入磁盘,走的是异步线程;
  • 网络这边一模一样:网络协议栈就是创建了一个线程,把数据发送到网卡;文件系统线程则是把数据给到磁盘;
  • 所以规则也一样:1、操作系统往里面放的时候,用户不能写;2、操作系统往里面读取的时候,用户最好不要读——这就是 read/write 阻塞的又一层解释。

线程章的同步互斥、文件章的缓冲区、网络章的收发缓冲区——三个章节,一个模型。

五、报头与可拓展性

最后收回到协议本身:因为 TCP 是面向字节流的,所以我们需要添加报头(就是多了一些字段),每一次 read 都要依据这个报头来决定怎么处理。

而只要报文结构里有"类型""长度"这些字段,序列化和反序列化就有拓展性——新功能加字段,老代码照样能解析老报文。这就是协议设计的余量思维。

总结

  • 协议 = 约定:传输需要它,处理数据需要它,因为消息是临界资源;手搓过协议才有资格精确描述它;
  • 协议栈的"栈":报头层层压入、层层弹出;每层解包只需回答"怎么分离"和"交给谁";
  • IP 不变,MAC 一直在换:IP 是目标,MAC 是接力棒;4 字节 IP 还能推断是否本局域网;
  • 网络层屏蔽底层差异:先有局域网后有广域网,链路层保局域网、IP 保广域网,程序员眼里一切皆 IP;先有 MAC 后有 IP;
  • 每个缓冲区都是生产者消费者模型,TCP 两主机 4 个模型;网络发网卡 ≈ 文件刷磁盘,都是异步线程,OS 说了算;
  • 面向字节流 → 加报头 → 每次 read 依据报头 → 序列化反序列化有拓展性。

回到 HTTP:下一篇把请求和应答的报文格式全部铺开看。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 23:32:32

Vue的v-if和v-for混用居然是个天坑

![封面](https://i-blog.csdnimg.cn/direct/9961ae8208434575b6d18b8089015089.png)上周上线一个订单管理后台,凌晨3点被电话叫醒——页面卡死,CPU直接拉满。打开监控一看,一个表格渲染了2000多条数据,每条数据都带着5个嵌套的v-i…

作者头像 李华
网站建设 2026/10/2 23:31:55

超强 Java 代码审计 Skills 合集:把 Claude Code 的 settings 改到 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/2 23:27:41

U-Boot启动流程深度解析:从_start汇编入口到C语言世界的进阶指南

1. 为什么嵌入式开发总要啃U-Boot启动流程做嵌入式Linux开发的人,几乎都绕不开U-Boot。它不是操作系统,却决定了你的内核能不能跑起来、DDR初始化对不对、启动参数传没传对。很多新手卡在"U-Boot到底是怎么跑起来的"这个问题上,有经…

作者头像 李华