§ 2.2 · Section

TCP/IP 四层模型

The Internet Protocol Suite

OSI 七层是"理论上的完美",TCP/IP 四层是"实际上的好用"——互联网真正跑的就是这套。它把 OSI 的 7 层压缩成 4 层,更简洁、更实用。

生活场景
🏗️ 盖房子:设计院图纸 vs 施工队做法

OSI 像设计院的图纸——完美、严谨、考虑周全,但施工队可能觉得太复杂。
TCP/IP 像施工队的实际做法——把设计院 7 个工种合并成 4 个,照样把楼盖起来,而且更快。
不是设计院错了,是施工队找到了更高效的协作方式。

它的真名叫"互联网协议族"

先纠正一个说法上的小误区:"TCP/IP"这个名字其实是一种以偏概全的简称。它的正式名字是互联网协议族(Internet Protocol Suite),里面装着上百个协议——IP、TCP、UDP、ICMP、ARP、DNS、DHCP、BGP、OSPF……之所以用 TCP 和 IP 两个名字代指整个体系,是因为这两个是最早、最核心的两块基石。这就像用"柴米油盐"代指全部厨房用品。

换成大白话:"TCP/IP"这个叫法,就像你说"去菜市场买点葱姜",其实指的是把一整趟采购都办了。真正的名字是"互联网协议族",里头装着上百个协议——好比一整间厨房里的锅碗瓢盆刀铲勺,只不过用得最勤的那两件(锅和铲)被拿来当了整间厨房的招牌。

它的诞生比 OSI 早得多。1974 年 5 月,Vint Cerf 和 Bob Kahn 在 IEEE 上发表了论文《A Protocol for Packet Network Intercommunication》,提出了一个统一的"网际互联协议"。当时论文里的 TCP 是一个大一统的协议,同时干着今天 TCP 和 IP 两个人的活。1978 年,两人做了一个后来被证明极其英明的决定:把 TCP 一刀劈成两半——需要可靠传输的部分留在 TCP,只负责寻址转发的部分独立成 IP。

那次"一刀劈两半"打个比方就是把原来"一个人既跑腿又签收"的岗位拆成了两个:跑腿的(IP)只管把包裹送到那栋楼,签收核对的(TCP)另找一个人干。为什么要拆?因为有些货压根不需要签收——你往楼下扔一句话喊人吃饭,非要人家签个字才算数,那就太耽误事了。

为什么要劈开?因为他们发现有些应用根本不想要可靠性——比如实时语音,与其等一个丢失的包重传回来,不如直接跳过去继续放。如果可靠性和寻址捆在一个协议里,这类应用就无路可走。劈开之后,IP 只管"送到",要不要可靠由上层自己选:想要就用 TCP,不想要就用 UDP(RFC 768,1980 年发布)。这一次拆分直接催生了今天"传输层有两个选择"的格局。

这个决定的好处在生活里也随处可见:快递公司同时提供"要签收的挂号件"和"塞进快递柜就完事的普通件",让寄件人自己选。要是全国只有挂号件一种选项,你给邻居送一把葱也得让人签字画押。TCP 是挂号件,UDP 是塞快递柜——分开之后,两类需求各得其所。

正式的规范编号值得记一下,看文档时会反复遇到:RFC 791(IPv4)、RFC 793(TCP,2022 年被 RFC 9293 整合替代)、RFC 768(UDP)、RFC 792(ICMP),都在 1980~1981 年间定稿;模型本身的描述在 RFC 1122(主机需求,1989 年)里最为权威——注意这份文档描述的正是四层结构。

四层模型总览

层数名称对应 OSI干什么代表协议
4应用层 · Application5+6+7用户服务 + 数据格式 + 会话管理HTTP、FTP、SMTP、DNS
3传输层 · Transport4端到端可靠传输TCP、UDP
2网络层 · Internet3寻址、路由IP、ICMP
1网络接口层 · Network Interface1+2物理传输 + 相邻节点传输Ethernet、WiFi

这四层用点一份外卖就能一口气串完:应用层=你在 App 里说"来一份宫保鸡丁不要辣"(点菜这件事本身);传输层=选"要不要送到手上让你确认"(挂号还是随便放门口);网络层=填清楚送到哪个小区哪栋楼(地址与路线);网络接口层=骑手的那辆电动车和他实际骑过的马路。四件事,一件不落,一件也不多。

为什么恰好是四层:两次合并的理由

TCP/IP 不是"把 OSI 砍掉三层"——它压根就不是从 OSI 推导出来的,两者是平行独立发展的。但既然结果上看像是做了两次合并,我们就来看看这两次合并各有什么道理。

第一次合并:把会话层、表示层塞进应用层。理由是这两层的内容完全是"应用自己的私事",没有通用性可言。HTTP 的会话管理靠 Cookie,SMTP 靠 MAIL FROM 命令序列,FTP 靠控制连接上的状态机——三种完全不同的做法,硬要抽象出一个"通用会话层",结果只能是一个谁都用不上的空壳。表示层同理:JSON、Protobuf、H.264、JPEG 之间毫无共性,编解码只能交给应用自己的库。与其定义一个没人实现的层,不如干脆不定义。

第一次合并的道理说白了就是:每家餐厅的"上菜规矩"都不一样,硬要定一份全行业通用的规矩,最后只能定出一句"要把菜端到桌上"这种谁都用不上的空话。与其定一条没人执行的规矩,不如让各家自己定。HTTP 靠 Cookie 记住你、SMTP 靠一串命令、FTP 靠状态机——三家三种做法,压根没有共同点可抽。

第二次合并:把物理层和数据链路层揉成"网络接口层"。理由更务实:这两层在现实中总是被打包实现在同一块硬件里。你买一块网卡,它同时提供了以太网帧的封装(链路层)和电信号的收发(物理层),你不可能只买"半块网卡"。同理,802.11 标准本身也是同时定义 MAC 层和 PHY 层的。既然实现上不可分割,模型里分开就只有教学意义没有工程意义。

第二次合并的道理更直白:你买一块网卡,"封装帧"和"发电信号"这两件事是同一块板子上焊死的,你没法只买半块。好比洗衣机的"转筒"和"电机",教科书上可以分开讲原理,但市面上压根没人卖只有转筒没有电机的洗衣机。实现上不可分割的东西,模型里分开就只剩教学价值了。

还有一个更根本的哲学差异:TCP/IP 压根不打算规定底下是什么。RFC 1122 对网络接口层的描述极其简略,态度是"你只要能帮我把一个 IP 包从这台机器挪到相邻那台机器就行,怎么挪我不管"。这种刻意的"不规定"造就了 IP 极强的适应性:它跑过以太网、跑过 WiFi、跑过电话拨号、跑过卫星链路、跑过 4G/5G、甚至有人拿信鸽跑过(RFC 1149:IP over Avian Carriers,愚人节文档,但 2001 年真有挪威工程师用鸽子完成了实验,9 个包丢了 5 个,往返延迟约 1 小时)。

"刻意不规定底下是什么"这一手极其高明,打个比方就是:快递公司只规定"把箱子送到那栋楼",但从不规定你必须用货车——三轮车、船、飞机、甚至骑自行车,只要能把箱子送到就行。正因为不挑运输工具,这套体系才能一路从电话拨号跑到 5G 卫星,中间连一行应用代码都不用改。至于那个用信鸽跑 IP 的实验——9 个包丢了 5 个,往返一小时,居然还真跑通了。

沙漏模型:IP 那根"细腰"

如果只让你记住 TCP/IP 的一件事,就记这个图形——沙漏(Hourglass Model)。这是整个互联网架构最核心的美学:

沙漏这个形状说白了就是:上面随便长、下面随便换、中间死死钉住一根细腰。打个比方,这好比全国的邮政编码体系——上面你想寄什么就寄什么(信、包裹、生鲜、文件),下面用什么运(火车、飞机、卡车、船)也随便换,但中间那套邮编规则四十年一个数字都不许改,因为全国所有分拣中心都是按它建的。

          应用层:宽 —— 几百种协议,随便加
   HTTP  SMTP  DNS  SSH  FTP  RTP  MQTT  gRPC  QUIC ...
      \     |     |     |     |     |     |     /
       \    |     |     |     |     |     |    /
        --------------------------------------
              传输层:窄 —— 基本只有 TCP 和 UDP
        --------------------------------------
                          ||
                          ||  ← 网络层:细腰!只有 IP 一个
                       【 IP 】
                          ||
        --------------------------------------
        链路层:又宽 —— 几十种介质,随便换
   Ethernet  WiFi  4G  5G  PPP  卫星  光纤  蓝牙  信鸽 ...

这个形状不是巧合,而是设计出来的。它带来的效果是"上面随便长,下面随便换,中间死死钉住一个 IP"。要发明一个新应用协议(比如 2009 年的 MQTT、2021 年的 HTTP/3),你不需要征得任何人同意,只要跑在 IP 上就行;要部署一种新的物理介质(5G、Starlink 卫星),你也不需要修改任何一个应用,只要能承载 IP 包就行。

细腰的代价是IP 本身极难改。这就是 IPv6 悲剧的根源:细腰一旦要换,上下所有东西都得跟着动。1998 年 IPv6 定稿(RFC 2460,2017 年被 RFC 8200 替代),到 2026 年全球采用率也才刚过半。相比之下同时期出现的应用协议早就换了三四代了。细腰给了互联网无与伦比的演进能力,也给了它一个几乎无法治愈的硬伤。

换个说法就特别好懂:换个新应用(比如出个新的点餐 App)不需要任何人批准;换个新线路(比如通了地铁)也不需要改任何 App;但你要是想改邮政编码规则本身——那全国几十万个分拣中心、几亿个已经印好的地址簿都得跟着改。这就是 IPv6 花了二十多年才推到一半的全部原因。

顺便说一句,学术界有人认为今天的沙漏"腰变粗了"——因为 NAT、防火墙、TLS 中间盒、CDN 这些东西事实上也成了必经环节。有人把这个现象叫做"腰部僵化"(ossification):正是因为中间设备对 TCP 头部做了太多假设,QUIC 才不得不把整个传输层塞进 UDP 载荷里加密起来,好让中间盒看不懂、也就改不了。

"腰部僵化"这个词听着玄,通俗地说就是:本来只该负责分拣的中转站,慢慢学会了偷偷拆箱看内容、还顺手改点东西(NAT 改地址、防火墙查内容、CDN 存副本)。日子久了,你想换个新的箱子格式,中转站压根认不出来,直接给你退回。QUIC 的应对办法很鸡贼:把整个箱子锁进一个中转站认得的标准纸箱里(UDP)——它看不懂,自然也就改不了。

端到端原则:互联网最重要的一句设计信条

1981 年,MIT 的 Saltzer、Reed、Clark 三人发表了论文《End-to-End Arguments in System Design》,提出了一条后来影响整个互联网的原则。它的核心表述是:如果某个功能只能在通信的两个端点上被正确完整地实现,那么就不应该把它放进网络中间。

这句话听着学术,翻译成人话就是:能不能靠中转站保证货物完好?不能——因为货可能在装车前就磕坏了,也可能在收货人卸货时摔了。既然最后无论如何都得由收货人开箱验一遍,那中途每个站都验一次就纯属重复劳动。该谁负责的事就交给谁,中间人少管闲事。

举个具体例子理解这句话。假设你要保证"文件传输不出错",能不能靠每一段链路都做校验重传来实现?答案是不能。因为出错的地方可能是路由器的内存、可能是发送端从磁盘读文件时、可能是接收端写盘时——这些都在"链路"之外。所以无论中间做多少保障,最终还是必须由两个端点做一次端到端的完整性检查。既然如此,中间那些保障就成了纯粹的浪费

这条原则直接塑造了 TCP/IP 的形态,具体表现为三条设计决定:

再补个更日常的例子:你网购收到一箱易碎品,是收货时自己开箱验,还是指望快递公司每个中转站都开箱验一次?后者听着更保险,实际上又慢又贵,而且照样漏——因为它验不到"卖家发货前就有裂缝"这种情况。所以规矩定成了"中转站只管送、收货人负责验",这就是端到端原则的全部内容。

端到端原则在今天正被不断侵蚀,值得诚实指出。NAT 打破了"任意两台主机可直接通信"的假设;防火墙在中间做四层甚至七层判断;运营商用 DPI(深度包检测)识别流量类型;CDN 把内容缓存在中间。每一次侵蚀都换来了短期收益,也都造成了长期的架构债。QUIC 把传输层信息全部加密,某种意义上是端到端原则的一次"武力夺回"。

那三条设计决定用大白话讲就是: 中转站要笨——只看地址、扔上车、忘掉,绝不记账(笨网络、聪明端点); 麻烦事全推给两头——签收、加密、限速全由收发双方自己搞定,好处是你想搞个新玩法,压根不用跟中转站打招呼 记录只存在两头——所以某个中转站塌了,货绕道走别的路,你压根感觉不到。

四层 vs 七层 · 怎么对应

TCP/IP 把 OSI 的"会话层"和"表示层"合并到"应用层",把"物理层"和"数据链路层"合并到"网络接口层":

这张对应表说白了就是"七个工种合成四个工种"的对照单:上面三个工种(应用、表示、会话)合成一个人干,下面两个(链路、物理)也合成一个人干,中间两个原封不动。好比一家小饭馆把"采购、切配、炒锅、摆盘、传菜"五个建制并成两个人——活儿一件没少,只是不再各设一个岗。

OSI 七层TCP/IP 四层说明
应用层应用层TCP/IP 认为"数据格式"和"会话管理"是应用自己的事
表示层
会话层
传输层传输层完全对应
网络层网络层完全对应
数据链路层网络接口层TCP/IP 认为"物理传输"和"相邻传输"是一回事
物理层

第三种答案:教学界通用的"五层折中模型"

你翻不同的教材会发现一个混乱现象:有的书说四层,有的书说五层。这不是谁写错了,而是学术界确实存在一个折中方案——最著名的是 Kurose & Ross 的《计算机网络:自顶向下方法》,以及国内谢希仁的《计算机网络》,都采用五层。

为什么会有"第三种答案"?说白了就是教材作者觉得"合并上三层"合理,但"合并下两层"讲课时讲不清。因为对学生来说,"网线断了"和"MAC 地址冲突"明显是两码事——一个得去看插头,一个得去查配置,硬塞进一层里反而添乱。所以他们保留了下面两层,只合并了上面三层。

OSI 七层(1984 ISO)五层折中(教学常用)TCP/IP 四层(RFC 1122)Linux 内核实现
7 应用层应用层应用层用户空间(应用进程 + 库)
6 表示层
5 会话层
4 传输层传输层传输层内核:net/ipv4/tcp.c、udp.c
3 网络层网络层网络层(Internet)内核:net/ipv4/ip_*.c
2 数据链路层数据链路层网络接口层内核:net/core/dev.c + 网卡驱动
1 物理层物理层网卡硬件 / PHY 芯片

五层模型的取舍逻辑很清楚:它接受了 TCP/IP 对上三层的合并(因为那确实是应用自己的事),但拒绝了对下两层的合并(因为物理层和链路层的关注点差别太大,教学上分开更清楚)。事实上这也是最贴近工程直觉的划分——你调试网络时,"网线断了"和"MAC 地址冲突"确实是两类完全不同的问题。

面试或考试时怎么答?记住这条实用规则:被问"实际互联网用什么模型"答四层(TCP/IP);被问"分层排查思路"用五层;被问"术语和层号"用 OSI 七层。三个模型不是竞争关系,而是三种不同用途的镜子。

这三个模型的关系,打个比方就是同一间房子的三张图:OSI 七层是那张标注得极细的施工图纸(术语、层号都从它来);TCP/IP 四层是这房子实际的样子(真正跑着的东西);五层是老师上课用的那张板书(讲得最清楚)。三张图看的是同一间房,别问哪张才是"真的"。

为什么 TCP/IP 赢了

OSI 和 TCP/IP 在 1980-90 年代"打架",最终 TCP/IP 胜出,原因:

这四条是骨架,下面把肉补上——每一条背后都有具体的时间、人、和数字。

这四条压成一句大白话就是:一个免费、能马上用、而且大学生毕业时就已经会用的东西,赢了一个要花钱买标准、还得等好几年才有成品的东西。这跟哪家餐厅火起来的道理一样——不是菜谱写得最漂亮的那家,而是先开门、味道能吃、价格便宜的那家。

这六条里最容易被低估的是那个 socket API。它的天才之处在于把"网络通信"伪装成了"读写文件"——而读写文件是每个程序员早就会的事。打个比方,这好比新出的一款家电,操作方式跟你家用了十年的洗衣机一模一样:没有说明书也能上手。学习成本几乎为零,这就是它四十年没被换掉的原因。

Insight · 一个常被忽略的数字

1969 年 ARPANET 只有 4 个节点;1983 年切换 TCP/IP 时约 400 台主机;1989 年突破 10 万台;1992 年 100 万台;2000 年约 1 亿台。
也就是说,TCP/IP 是在网络规模每隔几年翻十倍的高压环境下被逐步打磨出来的。拥塞控制(1988 年 Van Jacobson 的算法,起因是 1986 年 NSFNET 的著名"拥塞崩溃"事件,吞吐量从 32 kbps 掉到 40 bps)、CIDR、NAT、DNS 缓存——每一项都是被真实事故逼出来的。
这种"在生产环境里边跑边改"的经历,是任何委员会设计都无法替代的。

那个"每几年翻十倍"的增长值得体会一下:从 4 个节点长到 1 亿台,中间每一次翻倍都会把某个原本够用的设计撑爆。1986 年那次著名的"拥塞崩溃",吞吐量从 32 kbps 掉到 40 bps——相当于原本一分钟能搬完的货,突然要搬十三个小时。拥塞控制算法就是被这次事故直接逼出来的。委员会永远设计不出这种东西,因为它压根想不到会有这种事。

Analogy · VHS vs Betamax

1980 年代录像带格式大战——Betamax 技术更先进(像 OSI),VHS 更便宜更普及(像 TCP/IP)。
最后 VHS 赢了,不是因为更好,而是因为更多人用
TCP/IP 赢 OSI,也是同样的逻辑——生态打败了完美

四层各干什么

第一层 · 网络接口层(Network Interface)

对应 OSI 的 1+2 层。管"怎么在物理介质上传数据,怎么在相邻设备间传数据"。

这一层说白了就是快递的最后一公里,加上那辆送货的电动车本身:既包括"骑手该怎么把货交给楼下的收发室"(帧的封装),也包括"这辆车怎么在马路上跑"(电信号)。它眼里的世界只有隔壁那一站,压根不知道这箱货最终要飞去哪个国家。

这一层在 RFC 里的描述非常克制,RFC 1122 的原话大意是"本文档不规定链路层协议,只规定主机必须能够在其所连接的网络上收发 IP 数据报"。这种刻意的模糊是一种战略:正因为 IP 不挑底层,所以任何新出现的物理技术只要写一个"IP over X"的适配规范就能接入互联网。已经写过的规范包括 IP over Ethernet(RFC 894)、IP over PPP(RFC 1332)、IP over ATM(RFC 2225)、IP over InfiniBand(RFC 4391),甚至 IP over Avian Carriers(RFC 1149,信鸽)。

那句 RFC 原话翻译成人话就是:"你只要能把我这个箱子从这栋楼挪到隔壁那栋楼就行,用推车、扛着走、还是坐电梯,我一概不管。"这种刻意的"不管"是一种战略——正因为不管,后来出现的每一种新交通工具(WiFi、4G、5G、卫星)只要写一份"怎么装我的箱子"的说明就能接进来,压根不用改上面任何东西。

这一层最重要的辅助协议是 ARP(Address Resolution Protocol,RFC 826,1982 年)。它解决一个具体问题:我知道对方的 IP 是 192.168.1.1,但要发以太网帧我必须填目的 MAC,MAC 从哪来?答案是在局域网里吼一嗓子

# 查看本机 ARP 缓存(Windows / Linux 通用)
$ arp -a
Interface: 192.168.1.20 --- 0x5
  Internet Address      Physical Address      Type
  192.168.1.1           a4-2b-b0-c1-d2-e3     dynamic   ← 网关
  192.168.1.33          3c-22-fb-11-22-33     dynamic
  192.168.1.255         ff-ff-ff-ff-ff-ff     static    ← 广播地址

# ARP 请求的本质:向整个局域网广播(目的 MAC = ff:ff:ff:ff:ff:ff)
#   "谁是 192.168.1.1?请把你的 MAC 告诉 192.168.1.20"
# 只有 IP 匹配的那台机器会单播回应:
#   "192.168.1.1 在这里,我的 MAC 是 a4:2b:b0:c1:d2:e3"
# 结果被缓存下来,默认几十秒到几分钟不等

ARP 没有任何认证机制——谁都可以冒充别人回应。这就是"ARP 欺骗"攻击的原理:攻击者不停地广播"网关的 MAC 是我的 MAC",把整个局域网的流量都吸引到自己机器上。这是 1982 年那个"局域网内大家都是好人"的年代留下的历史包袱,也是分层设计的一个副作用:安全性没有在任何一层被统一考虑,只能后期一层一层补。

ARP 干的活儿说白了就是在小区院子里喊一嗓子问路:"5 号楼 301 是哪位?"谁应声,你就把东西交给他。要命的是这套流程压根不查证件——只要有人抢先喊一句"我就是",你就真给他了。这就是 ARP 欺骗:攻击者在公共 WiFi 里不停抢答"我是网关",从此全楼的东西都先过他的手。这是 1982 年那个"院里都是熟人"的年代留下的老账。

第二层 · 网络层(Internet)

对应 OSI 的第 3 层。管"怎么从源地址找到目标地址"。

这一层的名字值得注意:TCP/IP 管它叫 "Internet Layer"(网际层)而不是 "Network Layer"(网络层)。这个用词差异不是随意的——"Internet"的本意是"网络之间"(inter-network),强调它要解决的是把许多互不相同的独立网络连成一个整体这件事,而不是在一个网络内部通信。这正是 1974 年 Cerf 和 Kahn 那篇论文的标题所指的"Internetworking"。

这个用词差异其实很重要,说白了就是:它要解决的不是"一个小区里怎么送快递",而是"全国几十万个各自为政的小区,怎么串成一张能互通的网"。"网际"这两个字的分量全在"际"上——每个小区自己内部怎么送货各有各的规矩,这一层负责的是让它们之间能对接。

IP 的核心服务契约可以写成一句话:"给我一个目的 IP 和一段最多 65515 字节的数据,我尽力(但不保证)把它送到那台机器。"它不承诺的东西包括:不保证送达、不保证顺序、不保证不重复、不保证不损坏(只校验头部,不校验数据)、不告知丢弃。这个"什么都不保证"的清单看起来很不负责,但正是它让 IP 能够简单到可以在任何设备上实现。

IP 的服务承诺写成大白话就一句:"给我个地址,我尽量送到,丢了别找我。"不保证送达、不保证顺序、不保证不重复、丢了还不通知你。听着像个极不负责的搬运队,但恰恰因为它承诺得少,它才能简单到在一颗 8 元的芯片上也跑得动。承诺越多,实现越重——这在工程上是铁律。

ICMP(RFC 792)是网络层的"报错通道"。它不传业务数据,只传"控制消息":目标不可达、TTL 超时、需要分片但设了不可分片、回显请求/应答。你天天用的两个命令全靠它:

# ping 用 ICMP Echo Request(type 8) / Echo Reply(type 0)
$ ping baidu.com
正在 Ping baidu.com [110.242.68.66] 具有 32 字节的数据:
来自 110.242.68.66 的回复: 字节=32 时间=28ms TTL=51
来自 110.242.68.66 的回复: 字节=32 时间=27ms TTL=51
                                            ↑ TTL=51 说明
                             对方初始 TTL 可能是 64,中间过了 13 跳

# traceroute / tracert 的原理:故意把 TTL 设小
$ tracert -d baidu.com
  1     1 ms  192.168.1.1        ← 发 TTL=1,第一跳减到 0,回 ICMP 超时
  2     8 ms  100.64.0.1         ← 发 TTL=2,第二跳回报
  3    12 ms  61.156.xxx.xxx     ← 发 TTL=3 ...
 ...  每一跳都靠"TTL 耗尽时对方会回一条 ICMP"这个规则暴露自己

注意 ICMP 在分层上是个"怪胎":它的报文封装在 IP 包里(所以看起来像上层协议),但它服务的对象是 IP 本身(所以功能上属于同层)。标准做法是把它归入网络层,因为它是 IP 不可分割的配套设施——RFC 1122 明确要求所有实现 IP 的主机必须同时实现 ICMP。

ICMP 说白了就是快递系统的"退件通知单"通道:它不运货,只运"送不到""地址错了""箱子太大了""转手次数用完了"这几句话。你天天用的 ping,本质就是寄一张"你在吗"的明信片,等对方回一张"我在"。而 traceroute 更绝,它靠故意寄一堆"只准转一次""只准转两次"的包裹,逼每个中转站自己回一张退件单来报出身份。

第三层 · 传输层(Transport)

对应 OSI 的第 4 层。管"端到端的可靠传输"。

传输层在 TCP/IP 里被赋予了一个 OSI 没那么强调的角色:它是"提供服务质量选项"的地方。因为 IP 什么都不保证,所以"要不要保证"这个决定权被推到了传输层,而传输层给了应用两个截然不同的选项。这就是那次 1978 年劈开 TCP 的最终成果:

传输层的角色说白了就是快递柜台上摆的那几种服务选项:要签收的挂号件(TCP)、扔柜子就完事的普通件(UDP)、还有两种新式服务(SCTP、QUIC)。正因为下面那位跑腿的什么都不保证,"要不要保证"这个选择权才落到了这个柜台上。

TCP(RFC 9293)UDP(RFC 768)SCTP(RFC 9260)QUIC(RFC 9000)
年份1981198020002021
头部大小20~60 字节固定 8 字节12 字节 + 块头跑在 UDP 载荷内
可靠传输是(可按流选择)
保序严格字节流保序不保序可选,支持多流独立按流保序,流间独立
拥塞控制无(应用自己做)有,且在用户空间可换
实际部署无处不在无处不在仅电信信令等小众领域已占相当比例的 Web 流量

SCTP 的命运特别值得一提。它技术上明显优于 TCP(支持多流避免队头阻塞、支持多宿主自动切换网络接口、天生防 SYN Flood),2000 年就定稿了,但至今几乎无人用。原因是它需要一个新的 IP 协议号(132),而世界上大量 NAT 和防火墙只认识协议号 6 和 17,看到 132 就直接丢包。这是"腰部僵化"最惨烈的案例——也正是这个教训告诉了 QUIC 的设计者:要想部署一个新传输协议,唯一可行的路是把它藏在 UDP 里伪装成普通流量。

SCTP 这个惨案值得记牢,因为它讲了一个特别现实的道理:东西再好,如果全国的安检口都认不出你手里那个箱子的形状,你就一步也走不了。SCTP 用了一个新的协议号,而全世界的 NAT 和防火墙只认得 6 和 17 那两种——看到陌生编号,二话不说直接扔掉。QUIC 学到了这个教训:它干脆钻进 UDP 这个大家都认的标准箱子里,装成普通件混过安检。

第四层 · 应用层(Application)

对应 OSI 的 5+6+7 层。管"用户直接用的服务"。

TCP/IP 的应用层是唯一完全跑在用户空间的一层,这个位置带来两个直接后果。第一,发明新应用协议的成本极低——你写个程序调用 socket API 就完成了一个新协议的一端,不需要改内核、不需要管理员权限、不需要任何标准组织批准。互联网上应用协议数量的爆炸式增长完全归功于这一点。

"完全跑在用户空间"这句话的意思,说白了就是这一层的活儿全在你自己家厨房里干,不用惊动物业。你想发明一种新的点菜方式,压根不需要向谁报批——写个程序调几个接口就完事了。而下面三层是小区的水电管网,动一下要全体开会。这就是应用层协议为什么能长到几百种,而传输层四十年只多了两个。

第二个后果是应用层协议的质量参差不齐,且必须自己处理边界问题。因为 TCP 交给你的是一条无边界的字节流,"一条消息从哪到哪"必须由应用协议自己规定。历史上流行过三种做法:

# 做法一:用分隔符(HTTP/1.1、SMTP、Redis 老协议)
GET / HTTP/1.1\r\nHost: a.com\r\n\r\n
                              ↑ 连续两个 \r\n 表示头部结束

# 做法二:先告诉长度(HTTP 的 Content-Length、绝大多数二进制协议)
[4 字节长度=1024][1024 字节的实际内容][4 字节长度=88][88 字节内容]
   ↑ 这叫 TLV(Type-Length-Value)风格,最常用也最稳妥

# 做法三:固定长度(老式电信协议、某些金融报文)
每条消息恰好 128 字节,读满就是一条,简单但浪费

# HTTP/1.1 分块传输把两种混用:
Transfer-Encoding: chunked
1a\r\n              ← 十六进制的块长度(26 字节)
这是二十六个字节的内容\r\n
0\r\n\r\n           ← 长度 0 表示结束

这三种划边界的做法,用生活话讲就是:做法一——每段话说完就喊一声"完毕"(分隔符);做法二——先说"接下来我要说 1024 个字",你数够就知道结束了(长度前缀,最稳妥);做法三——规定每句话必须凑够 128 个字,不够就用空格填满(固定长度,简单但浪费)。为什么非要划边界?因为 TCP 递给你的是一整车沙子,它压根不记得你原本是分几袋装的。

协议栈在操作系统里的哪个位置

前面讲的都是"模型",现在看"实物"。四层模型在一台真实机器上是怎么落地的?答案是被切成了三块,分别住在三个地方

这个"切成三块"打个比方就是一栋楼里的三个区域用户空间是你自己家的厨房(想炒什么炒什么,全凭你);内核空间是这栋楼的水电管网(物业统一管,你只能提申请);硬件是市政的自来水厂和管道。而 socket API 就是你家墙上那个水龙头——它是"你能自己控制的"和"物业负责的"之间那条分界线。

┌─────────────────────────────────────────────┐
│  用户空间 User Space                        │
│  ┌───────────────────────────────────────┐  │
│  │ 你的程序:浏览器 / 微信 / Nginx        │  │ ← 应用层
│  │ 库:OpenSSL、curl、gRPC、JSON 解析器   │  │   (含 TLS/编解码)
│  └───────────────────────────────────────┘  │
│                    ↕ 系统调用 syscall        │  ← socket API 是分界线
│              socket() send() recv()          │
├─────────────────────────────────────────────┤
│  内核空间 Kernel Space                      │
│  ┌───────────────────────────────────────┐  │
│  │ socket 层(struct sock、缓冲区管理)   │  │
│  │ TCP / UDP 实现  net/ipv4/tcp_*.c      │  │ ← 传输层
│  │ IP 路由与转发   net/ipv4/ip_*.c       │  │ ← 网络层
│  │ netfilter 钩子(iptables/防火墙)      │  │
│  │ 邻居子系统 ARP  net/core/neighbour.c  │  │
│  │ 网络设备层      net/core/dev.c        │  │ ← 链路层软件部分
│  │ 网卡驱动        drivers/net/...       │  │
│  └───────────────────────────────────────┘  │
├─────────────────────────────────────────────┤
│  硬件 Hardware                              │
│  网卡(MAC 芯片 + PHY 芯片)+ DMA 环形缓冲   │ ← 链路层硬件 + 物理层
└─────────────────────────────────────────────┘

请特别注意那条 socket API 分界线——它就是"应用层"和"传输层"之间的真实边界,也是 OSI 所谓"服务访问点 SAP"在现实中最著名的实例。你的程序调 send() 那一刻,数据从用户空间被拷贝进内核的发送缓冲区,从此就不再由你控制了:什么时候真正发出去、发多大、要不要合并、丢了怎么重传,全由内核的 TCP 实现决定。

这句话值得体会一下:你调 send() 那一刻,数据就像你把衣服扔进了洗衣机——什么时候开始转、转多久、要不要多加一遍水,全是机器说了算,你只能在面板上按几个键提要求。"我明明发出去了"和"它真的上路了"是两件事,中间隔着内核那道墙。

这个划分也解释了几个日常现象。为什么调 TCP 参数要改内核配置?因为 TCP 实现在内核里,你的应用改不了它,只能通过 sysctlsetsockopt() 请求内核改行为:

# 常用的 TCP 内核参数(Linux)
$ sysctl net.ipv4.tcp_congestion_control
net.ipv4.tcp_congestion_control = cubic     # 拥塞控制算法,可换成 bbr

$ sysctl net.ipv4.tcp_fin_timeout           # FIN_WAIT2 超时
$ sysctl net.core.somaxconn                 # accept 队列上限
$ sysctl net.ipv4.tcp_max_syn_backlog       # 半连接队列上限
$ sysctl net.ipv4.ip_local_port_range       # 客户端可用端口范围

# 单条连接级别的调整用 setsockopt()
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));  # 关 Nagle
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); # 接收缓冲区

为什么 QUIC 跑在用户空间反而是个优势?因为 TCP 在内核里,你想换一个新算法就得等操作系统升级——而全球有几十亿台设备的内核版本你控制不了。QUIC 把整个传输层实现搬到用户空间(就是一个普通的库),Chrome 更新一次版本就等于全球几亿用户的传输层升级了一次。"迭代速度"这个软件工程指标,最终成了协议设计的决定性因素。

为什么 QUIC 跑在用户空间反而是优势?打个比方就懂了:TCP 住在"物业管的水电管网"里,你想换个新水管,得等物业排期,而且全楼几十亿户的物业你压根管不着;QUIC 干脆把水管搬进了自己家厨房——浏览器更新一次版本,等于全球几亿用户的水管一夜之间换了新的。这就是为什么"能多快改"最后成了协议设计的胜负手。

还有一个值得知道的极端做法:内核旁路(Kernel Bypass)。高频交易、DPDK、AF_XDP、RDMA 这类技术直接让应用程序绕过内核网络栈,自己从网卡的 DMA 缓冲区读原始帧,然后在用户空间实现一个精简的 TCP/IP。代价是失去内核提供的一切通用能力(防火墙、路由、多进程共享),收益是延迟能从几十微秒压到几微秒。这是分层被主动打破的典型场景——当某一层的通用性成为负担时,专业场景会选择跳过它。

内核旁路这件事,说白了就是高频交易的人嫌走小区大门要过安检太慢,干脆自己从围墙上开了个门直通马路。好处是快得离谱(几十微秒压到几微秒),代价是安检、门禁、监控这些公共服务全都用不上了,出了事全得自己扛。所以这条路只有极少数场景走得通。

TCP/IP 协议栈 · 一张全家福

把前面学的协议"对号入座":

这张表可以当"厨房归位图"来看:同一间厨房里,锅在灶上、刀在案板上、菜在冰箱里——每样东西都有它固定的位置。背协议名没多大意义,但知道"这个协议住在哪一层"极其有用——因为排障时你第一件要做的事,就是判断问题出在哪一层。

TCP/IP 层协议你什么时候用到
应用层HTTP / HTTPS打开网页
DNS域名解析
SMTP / POP3 / IMAP发邮件 / 收邮件
FTP / SSH传文件 / 远程登录
传输层TCP网页、邮件、文件下载
UDP视频、语音、游戏、DNS 查询
网络层IP所有网络通信
ICMPping、traceroute
网络接口层Ethernet插网线上网
WiFi无线上网

用命令行验证四层的存在

模型讲完了,最后做一件事:用几条命令亲眼看到这四层。每一层都有对应的观察工具,这也是排查问题的基本功。

接下来这几条命令,说白了就是给网络做一次逐层体检:先看水表进不进水(网卡 UP 没)、再看管道通不通(有没有 IP、ping 得通不通)、再看水龙头开不开(端口通不通)、最后才看水质(应用层返回什么)。跟修水管一样,永远从进水那头往里查。

# 【网络接口层】看网卡状态、MAC 地址、链路是否 UP
$ ip link show          # Linux
$ ipconfig /all         # Windows —— 看"物理地址"就是 MAC
$ ethtool eth0          # 看协商速率、双工模式、是否插线

# 【网络层】看 IP 地址、路由表、能否到达
$ ip addr show          # 本机 IP
$ ip route show         # 路由表:去哪个网段走哪个网关
   default via 192.168.1.1 dev eth0     ← 默认路由(网关)
   192.168.1.0/24 dev eth0 scope link   ← 本地直连网段
$ ping 8.8.8.8          # 三层可达性(用 ICMP)
$ traceroute 8.8.8.8    # 看中间经过哪些路由器

# 【传输层】看连接状态、端口占用、监听情况
$ ss -tan               # Linux 现代做法,看所有 TCP 连接及状态
   State      Recv-Q Send-Q  Local Address:Port  Peer Address:Port
   LISTEN     0      128           0.0.0.0:22         0.0.0.0:*
   ESTAB      0      0       192.168.1.20:54321  93.184.216.34:443
$ netstat -ano          # Windows
$ nc -zv example.com 443    # 只测某个端口通不通

# 【应用层】看协议内容本身
$ curl -v https://example.com     # -v 显示完整请求和响应头
$ dig example.com                 # 看 DNS 解析过程
$ openssl s_client -connect example.com:443 -servername example.com
                                  # 看 TLS 握手和证书链

这套命令的排列顺序不是随意的——它就是从下往上的排查顺序。链路不 UP,后面全都白搭;路由表没有默认网关,ping 外网必然失败;端口不通,curl 一定超时。养成"从下往上"的习惯,能让你在几分钟内定位到多数网络故障,而不是无头绪地反复重启。

为什么必须从下往上?说白了就是水管没水的时候,你研究水龙头的款式毫无意义。网线没插好,后面全都白搭;没有默认网关,ping 外网必然失败;端口不通,curl 一定超时。下层不通,上层必然不通——所以从下往上排,能最快把范围缩小。

还有一个能同时看到四层的终极工具:抓包。tcpdump 和 Wireshark 会把每一层的头部逐个解析出来给你看,这是理解分层最直观的方式——下一节我们会专门用抓包走一遍完整流程。

抓包为什么值得单独讲一节?因为它是唯一能同时看到四层的工具——相当于把一件快递从最外层大箱到最里面那件礼物,逐层拆开摆在桌上,每一层的面单上写了什么都看得一清二楚。前面讲了半天"应该有哪些栏目",抓一次包你就真看见了那些栏目里填的字。

面试常问的四层模型问题

下面这几条不是让你背答案,而是给你几个"往深答一层"的抓手。面试考的从来不是你会不会背四层的名字,而是你知不知道"为什么要这么分"。能把合并的理由、细腰的代价、SCTP 失败的教训讲出来,比复述一遍对应表管用得多。

Recap · 收束

TCP/IP 四层 = "互联网的实际分工"。它比 OSI 更简洁,是真实世界的标准。
记住四层:应用层(服务)、传输层(可靠/快速)、网络层(地址)、接口层(物理)——你就掌握了互联网 90% 的底层逻辑。

☰ 主页
Xue Hai Wu Ya · Network · § 2.2 · TCP/IP Model