《深入理解计算机系统(原书第3版)》第11章:网络编程 🌐
!note 阅读范围与边界
本笔记依据本地附件《深入理解计算机系统(原书第3版)》第 11 章整理,用自己的话讲解。原书的代码和讨论以 Linux、C、Unix I/O、TCP/IP 套接字接口及 IPv4 示例为背景;TINY 是教学用的小型服务器,不是可直接上线的生产服务器。遇到现代网络与书中示例的差异,会明确标为"补充解释"。
!tip 一句话抓住本章网络编程不是"把数据丢到网线上"那么简单:客户端和服务器先靠地址与端口找到彼此,再用套接字建立一条可读可写的通信通道,最后按照 HTTP 这类应用协议把请求和响应讲清楚。
!warning 先建立正确期待本章的重点是理解通信模型、套接字接口和一个最小 Web 服务器的控制流,不是背所有网络协议细节。读代码时始终问四件事:谁在找谁?地址怎么表示?哪个描述符负责监听、哪个负责通信?这次发送的字节按什么协议解释?
1 本章学习导航 🧭
| 阶段 | 建议用时(估计) | 学什么 | 通过标准 |
|---|---|---|---|
| 0. 预热 | 25 分钟 | 进程、文件描述符、读写与字节序 | 能把"套接字也是一种文件描述符"先记住 |
| 1. 建立地图 | 45 分钟 | 客户端---服务器模型、网络、TCP/IP | 能画出一次请求与响应 |
| 2. 学会寻址 | 70 分钟 | IP、域名、端口、套接字地址、连接对 | 能解释"域名 + 端口"在找什么 |
| 3. 套接字流程 | 120 分钟 | socket、connect、bind、listen、accept | 能分清监听描述符和已连接描述符 |
| 4. 读 HTTP | 90 分钟 | URL、MIME、请求、响应、状态码 | 能手写一条最小 GET 请求并读懂响应 |
| 5. 读 TINY | 120 分钟 | 静态内容、CGI 动态内容、错误处理 | 能沿着一次 GET 请求讲完 TINY 的主要分支 |
| 6. 复习与实践 | 60 分钟 | 本机 HTTP 观察、自测、流程复述 | 能不看书画出端到端路线 |
建议总投入:约 8---10 小时(估计值)。第一次学习不要急着从第 11.6 节的 TINY 代码开始;先理解"地址 → 连接 → 字节流 → HTTP"的顺序,会轻松很多。
- 客户端---服务器事务
- 网络、IP、域名、端口与连接
- 套接字接口与 echo 示例
- Web、HTTP、静态/动态内容
- TINY Web 服务器完整路线
2 本章学习目标 🎯
学完后,你应该能做到:
- 用大白话说明客户端、服务器、资源和事务分别是什么,并知道它们是进程角色,不是固定的一台机器。
- 区分局域网帧、互联网包、IP 地址、域名、端口、套接字地址和套接字对。
- 解释 TCP/IP 在本章中的分工:IP 负责尽力递送数据报,TCP 在其上提供进程间可靠、全双工的连接。
- 说清客户端和服务器使用套接字时各自的调用顺序。
- 分清监听描述符与已连接描述符,以及为什么服务器不能只用一个描述符服务所有客户端。
- 读懂一笔 HTTP 事务的请求行、请求报头、空行、响应行、响应报头和响应主体。
- 区分静态内容与动态内容,知道 CGI 怎样把程序输出送回浏览器。
- 沿着一次请求复述 TINY 如何接收连接、解析 URI、选择静态/动态分支、发送结果并关闭连接。
3 整章知识地图 🗺️
#mermaid-svg-gCg1OapIPc3rMHVT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gCg1OapIPc3rMHVT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gCg1OapIPc3rMHVT .error-icon{fill:#552222;}#mermaid-svg-gCg1OapIPc3rMHVT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gCg1OapIPc3rMHVT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gCg1OapIPc3rMHVT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gCg1OapIPc3rMHVT .marker.cross{stroke:#333333;}#mermaid-svg-gCg1OapIPc3rMHVT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gCg1OapIPc3rMHVT p{margin:0;}#mermaid-svg-gCg1OapIPc3rMHVT .edge{stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .section--1 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section--1 path,#mermaid-svg-gCg1OapIPc3rMHVT .section--1 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section--1 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section--1 text{fill:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth--1{stroke-width:17;}#mermaid-svg-gCg1OapIPc3rMHVT .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-0 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-0 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-0 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-0 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-0 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-0{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-0{stroke-width:14;}#mermaid-svg-gCg1OapIPc3rMHVT .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-1 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-1 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-1 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-1 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-1 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-1{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-1{stroke-width:11;}#mermaid-svg-gCg1OapIPc3rMHVT .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-2 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-2 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-2 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-2 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-2 text{fill:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-2{stroke-width:8;}#mermaid-svg-gCg1OapIPc3rMHVT .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-3 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-3 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-3 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-3 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-3 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-3{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-3{stroke-width:5;}#mermaid-svg-gCg1OapIPc3rMHVT .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-4 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-4 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-4 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-4 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-4 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-4{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-4{stroke-width:2;}#mermaid-svg-gCg1OapIPc3rMHVT .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-5 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-5 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-5 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-5 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-5 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-5{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-5{stroke-width:-1;}#mermaid-svg-gCg1OapIPc3rMHVT .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-6 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-6 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-6 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-6 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-6 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-6{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-6{stroke-width:-4;}#mermaid-svg-gCg1OapIPc3rMHVT .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-7 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-7 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-7 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-7 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-7 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-7{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-7{stroke-width:-7;}#mermaid-svg-gCg1OapIPc3rMHVT .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-8 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-8 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-8 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-8 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-8 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-8{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-8{stroke-width:-10;}#mermaid-svg-gCg1OapIPc3rMHVT .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-9 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-9 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-9 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-9 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-9 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-9{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-9{stroke-width:-13;}#mermaid-svg-gCg1OapIPc3rMHVT .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-10 rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-10 path,#mermaid-svg-gCg1OapIPc3rMHVT .section-10 circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-10 polygon,#mermaid-svg-gCg1OapIPc3rMHVT .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-10 text{fill:black;}#mermaid-svg-gCg1OapIPc3rMHVT .node-icon-10{font-size:40px;color:black;}#mermaid-svg-gCg1OapIPc3rMHVT .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .edge-depth-10{stroke-width:-16;}#mermaid-svg-gCg1OapIPc3rMHVT .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled,#mermaid-svg-gCg1OapIPc3rMHVT .disabled circle,#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:lightgray;}#mermaid-svg-gCg1OapIPc3rMHVT .disabled text{fill:#efefef;}#mermaid-svg-gCg1OapIPc3rMHVT .section-root rect,#mermaid-svg-gCg1OapIPc3rMHVT .section-root path,#mermaid-svg-gCg1OapIPc3rMHVT .section-root circle,#mermaid-svg-gCg1OapIPc3rMHVT .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-gCg1OapIPc3rMHVT .section-root text{fill:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .section-root span{color:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .section-2 span{color:#ffffff;}#mermaid-svg-gCg1OapIPc3rMHVT .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-gCg1OapIPc3rMHVT .edge{fill:none;}#mermaid-svg-gCg1OapIPc3rMHVT .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-gCg1OapIPc3rMHVT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第11章 网络编程
客户端服务器
请求
资源
响应
事务
网络与寻址
网络适配器
IP地址
域名与DNS
端口
连接
套接字接口
socket
connect
bind
listen
accept
getaddrinfo
Web与HTTP
URL
MIME
请求报文
响应报文
CGI
TINY服务器
接收连接
解析URI
静态内容
动态内容
错误处理
读这张图要从左到右走:
- 客户端---服务器先规定角色与一次事务的骨架。
- 网络与寻址回答"到底要找谁、怎样让字节走过去"。
- 套接字接口把网络能力包装成进程可调用的函数和文件描述符。
- HTTP规定双方拿到字节后该怎样组织和理解它们。
- TINY把前四块拼成一个能提供静态、动态内容的教学型服务器。
4 前置知识、后续联系与词汇表 🔗
#mermaid-svg-jnJDYtNfy04rbM8s{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jnJDYtNfy04rbM8s .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jnJDYtNfy04rbM8s .error-icon{fill:#552222;}#mermaid-svg-jnJDYtNfy04rbM8s .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jnJDYtNfy04rbM8s .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jnJDYtNfy04rbM8s .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jnJDYtNfy04rbM8s .marker.cross{stroke:#333333;}#mermaid-svg-jnJDYtNfy04rbM8s svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jnJDYtNfy04rbM8s p{margin:0;}#mermaid-svg-jnJDYtNfy04rbM8s .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jnJDYtNfy04rbM8s .cluster-label text{fill:#333;}#mermaid-svg-jnJDYtNfy04rbM8s .cluster-label span{color:#333;}#mermaid-svg-jnJDYtNfy04rbM8s .cluster-label span p{background-color:transparent;}#mermaid-svg-jnJDYtNfy04rbM8s .label text,#mermaid-svg-jnJDYtNfy04rbM8s span{fill:#333;color:#333;}#mermaid-svg-jnJDYtNfy04rbM8s .node rect,#mermaid-svg-jnJDYtNfy04rbM8s .node circle,#mermaid-svg-jnJDYtNfy04rbM8s .node ellipse,#mermaid-svg-jnJDYtNfy04rbM8s .node polygon,#mermaid-svg-jnJDYtNfy04rbM8s .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jnJDYtNfy04rbM8s .rough-node .label text,#mermaid-svg-jnJDYtNfy04rbM8s .node .label text,#mermaid-svg-jnJDYtNfy04rbM8s .image-shape .label,#mermaid-svg-jnJDYtNfy04rbM8s .icon-shape .label{text-anchor:middle;}#mermaid-svg-jnJDYtNfy04rbM8s .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jnJDYtNfy04rbM8s .rough-node .label,#mermaid-svg-jnJDYtNfy04rbM8s .node .label,#mermaid-svg-jnJDYtNfy04rbM8s .image-shape .label,#mermaid-svg-jnJDYtNfy04rbM8s .icon-shape .label{text-align:center;}#mermaid-svg-jnJDYtNfy04rbM8s .node.clickable{cursor:pointer;}#mermaid-svg-jnJDYtNfy04rbM8s .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jnJDYtNfy04rbM8s .arrowheadPath{fill:#333333;}#mermaid-svg-jnJDYtNfy04rbM8s .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jnJDYtNfy04rbM8s .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jnJDYtNfy04rbM8s .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jnJDYtNfy04rbM8s .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jnJDYtNfy04rbM8s .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jnJDYtNfy04rbM8s .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jnJDYtNfy04rbM8s .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jnJDYtNfy04rbM8s .cluster text{fill:#333;}#mermaid-svg-jnJDYtNfy04rbM8s .cluster span{color:#333;}#mermaid-svg-jnJDYtNfy04rbM8s div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jnJDYtNfy04rbM8s .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jnJDYtNfy04rbM8s rect.text{fill:none;stroke-width:0;}#mermaid-svg-jnJDYtNfy04rbM8s .icon-shape,#mermaid-svg-jnJDYtNfy04rbM8s .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jnJDYtNfy04rbM8s .icon-shape p,#mermaid-svg-jnJDYtNfy04rbM8s .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jnJDYtNfy04rbM8s .icon-shape .label rect,#mermaid-svg-jnJDYtNfy04rbM8s .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jnJDYtNfy04rbM8s .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jnJDYtNfy04rbM8s .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jnJDYtNfy04rbM8s :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} \[第1章_计算机系统漫游]
系统全景与网络
\[第2章_信息的表示和处理\]
字节、编码与字节序
第11章
网络编程
候选:第10章
系统级 I/O
候选:第12章
并发编程
实际应用
Web 服务、RPC、设备通信
怎么看这张图: 第 2 章里的字节、字节顺序和字符串会直接进入 IP 地址、端口和 HTTP 报文;第 10 章的 Unix I/O 是本章读写套接字的基础;第 12 章会解决"怎样同时服务多个客户端"的问题。当前知识库中仅第 1、2 章链接已确认;其余为候选关联,不假定对应笔记已经存在。
候选关联:\[待创建:第10章_系统级I/O]、\[待创建:第12章_并发编程]。
| 词 | 先用一句话记住 | 不要误解成 |
|---|---|---|
| 客户端 | 主动发起服务请求的进程 | 永远是"用户电脑" |
| 服务器 | 管理资源并响应请求的进程 | 必须独占一台物理主机 |
| 事务 | 一次请求到响应的完整交互 | 数据库事务,或一定具备原子性 |
| IP 地址 | 在互联网模型中标识主机的地址 | 进程或某个具体服务本身 |
| 端口 | 同一主机上进一步定位服务/进程通信端点的编号 | 交换机上的实体网线插口 |
| 套接字 | 进程通信的一端;在 Linux 程序中可通过描述符使用 | 一根真实网线 |
| 监听描述符 | 服务器专门等候连接请求的描述符 | 已能和某个客户端收发业务数据的描述符 |
| 已连接描述符 | 一次已建立连接的通信端点 | 永远只存在一个的服务器总开关 |
| HTTP | Web 客户端和服务器解释文本消息的应用层协议 | 网络本身或浏览器本身 |
| MIME 类型 | 告诉接收方内容是什么格式的标签 | 文件扩展名的简单别名 |
5 核心知识点逐个讲 👩🏫
5.1 知识点 1:客户端---服务器模型(⭐⭐⭐⭐⭐)
为什么重要: 它是本章所有 API、HTTP 和 TINY 代码的共同骨架。先分清角色,后面的"谁调用 connect、谁调用 accept"就不容易背反。
5.1.1 ① 一句话
一个网络应用通常由一个服务器进程和一个或多个客户端进程组成:客户端向服务器请求某项资源或服务,服务器处理后返回响应。
5.1.2 ② 大白话解释
把服务器想成"资源管理员",而客户端是"来办事的人"。浏览器要网页、邮件程序要邮件、FTP 工具要文件时,它们都不是直接翻磁盘,而是先向负责资源的服务器进程发请求。
最容易被忽略的一点是:客户端和服务器是进程角色,不是机器角色。 同一台主机可以同时运行浏览器、Web 服务器和数据库;一个客户端与服务器也可以恰好在同一台主机上通信。
5.1.3 ③ 为什么会有它
- 资源往往需要集中管理,例如网页文件、用户数据、计算任务或设备状态。
- 客户端不必知道资源怎样保存,只需要遵守请求/响应规则。
- 多个客户端可以复用同一项服务;服务器可以统一做权限、日志和错误处理。
5.1.4 ④ 一次事务怎样走
服务器管理的资源 服务器进程 客户端进程 服务器管理的资源 服务器进程 客户端进程 #mermaid-svg-ZMne2qehsGDMRqkb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZMne2qehsGDMRqkb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZMne2qehsGDMRqkb .error-icon{fill:#552222;}#mermaid-svg-ZMne2qehsGDMRqkb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZMne2qehsGDMRqkb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZMne2qehsGDMRqkb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZMne2qehsGDMRqkb .marker.cross{stroke:#333333;}#mermaid-svg-ZMne2qehsGDMRqkb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZMne2qehsGDMRqkb p{margin:0;}#mermaid-svg-ZMne2qehsGDMRqkb .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZMne2qehsGDMRqkb text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ZMne2qehsGDMRqkb .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZMne2qehsGDMRqkb .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ZMne2qehsGDMRqkb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZMne2qehsGDMRqkb .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZMne2qehsGDMRqkb #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZMne2qehsGDMRqkb .sequenceNumber{fill:white;}#mermaid-svg-ZMne2qehsGDMRqkb #sequencenumber{fill:#333;}#mermaid-svg-ZMne2qehsGDMRqkb #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZMne2qehsGDMRqkb .messageText{fill:#333;stroke:none;}#mermaid-svg-ZMne2qehsGDMRqkb .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZMne2qehsGDMRqkb .labelText,#mermaid-svg-ZMne2qehsGDMRqkb .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ZMne2qehsGDMRqkb .loopText,#mermaid-svg-ZMne2qehsGDMRqkb .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ZMne2qehsGDMRqkb .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZMne2qehsGDMRqkb .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZMne2qehsGDMRqkb .noteText,#mermaid-svg-ZMne2qehsGDMRqkb .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ZMne2qehsGDMRqkb .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZMne2qehsGDMRqkb .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZMne2qehsGDMRqkb .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZMne2qehsGDMRqkb .actorPopupMenu{position:absolute;}#mermaid-svg-ZMne2qehsGDMRqkb .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZMne2qehsGDMRqkb .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZMne2qehsGDMRqkb .actor-man circle,#mermaid-svg-ZMne2qehsGDMRqkb line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZMne2qehsGDMRqkb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1. 发送请求2. 解释请求并操作资源得到结果3. 发送响应4. 处理响应
怎么看这张图: 先看客户端与服务器之间的两条横线,它们是一次事务的"请求/响应"骨架;再看服务器为什么还要接触资源。服务器不是为了"回一句话"而存在,而是在替客户端操作它管理的东西。
!example 🎬 动画脑补
浏览器先说:"请给我首页。"服务器接到请求后去读取目标文件,拿到内容后把 HTML 字节发回来。浏览器收到后按 HTML 规则显示页面。一来一回就是一笔事务。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 图书馆借书台 | 读者 → 客户端;馆员 → 服务器;图书库 → 资源 | 客户端通常不直接碰资源 | 真实网络没有人类语言,双方只能按协议传字节 |
| 餐厅点单 | 点菜 → 请求;后厨取材加工 → 服务器处理;上菜 → 响应 | 请求与响应的先后关系 | 餐厅可同时处理很多桌,教学中的迭代服务器未必能做到 |
| 政务窗口 | 办事人 → 客户端;窗口系统 → 服务器;档案 → 资源 | 服务角色可与物理地点分离 | 网络请求不会自动具备人工判断或授权 |
5.1.5 ⑤ 容易混淆
| 容易混淆 | 正确理解 | 为什么容易错 | 怎么判断 |
|---|---|---|---|
| 客户端 = 用户电脑 | 客户端是主动发请求的进程 | 浏览器常运行在用户电脑上 | 看谁发起请求,而不是看机器名称 |
| 服务器 = 一台远程主机 | 服务器是提供服务的进程 | 服务器常部署在远程机器上 | 同一主机也可同时跑客户端与服务器 |
| 网络事务 = 数据库事务 | 本章仅指请求、处理、响应的一串步骤 | 都叫 transaction | 本章不承诺原子性、回滚或隔离性 |
5.1.6 ⑥ 面试/表达题
问题: 为什么说客户端和服务器是进程,而不是主机?
回答: 因为角色由通信行为决定:主动请求服务的是客户端,管理资源并响应的是服务器。一个主机能同时运行多个进程,所以同一主机可以既有客户端又有服务器,甚至让两者直接通过本地回环地址通信。
!abstract 📌 一句话卡片|客户端---服务器模型
先找"谁发起请求、谁管理资源、谁返回响应",再谈主机、端口和代码;角色属于进程,不属于机器外壳。
5.2 知识点 2:网络把字节从一台主机送到另一台主机(⭐⭐⭐⭐)
为什么重要: 套接字函数看似只接收地址和描述符,但它背后依赖网络适配器、局域网、路由器、包头与有效载荷的接力。理解这条路,才能知道"地址"和"协议"到底在解决什么。
5.2.1 ① 一句话
从主机角度看,网络适配器是一种 I/O 设备;应用产生的字节经过协议软件和网络硬件封装、转发,最终到达另一台主机的协议软件与目标进程。
5.2.2 ② 大白话解释
你可以把网络看成许多不同道路连成的大交通网。应用真正想交付的是"货物"(数据),但货物每经过一段道路都要装进适合该路段的运输箱。书中把本地网络上的箱子称为帧 ,把能在互联网中被路由的内容称为包;它们都有说明路线等信息的头部,也有真正业务数据的有效载荷。
书中还提醒:网卡不是神秘的"网络专用魔法",它和磁盘控制器一样属于 I/O 设备。收到的数据通常会借助 DMA(直接内存访问)进入内存,CPU 不需要逐字节亲自搬运。
5.2.3 ③ 为什么会有它
- 不同地点的主机需要通信,单一局域网覆盖范围有限。
- 不同局域网技术可能不兼容,路由器和统一协议要把它们连接起来。
- 头部携带目的地、长度等控制信息;有效载荷才是应用真正关心的数据。
5.2.4 ④ 从客户端到服务器的一次转运
#mermaid-svg-UjB7AHAhbeeJniZ5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-UjB7AHAhbeeJniZ5 .error-icon{fill:#552222;}#mermaid-svg-UjB7AHAhbeeJniZ5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-UjB7AHAhbeeJniZ5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .marker.cross{stroke:#333333;}#mermaid-svg-UjB7AHAhbeeJniZ5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-UjB7AHAhbeeJniZ5 p{margin:0;}#mermaid-svg-UjB7AHAhbeeJniZ5 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster-label text{fill:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster-label span{color:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster-label span p{background-color:transparent;}#mermaid-svg-UjB7AHAhbeeJniZ5 .label text,#mermaid-svg-UjB7AHAhbeeJniZ5 span{fill:#333;color:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .node rect,#mermaid-svg-UjB7AHAhbeeJniZ5 .node circle,#mermaid-svg-UjB7AHAhbeeJniZ5 .node ellipse,#mermaid-svg-UjB7AHAhbeeJniZ5 .node polygon,#mermaid-svg-UjB7AHAhbeeJniZ5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .rough-node .label text,#mermaid-svg-UjB7AHAhbeeJniZ5 .node .label text,#mermaid-svg-UjB7AHAhbeeJniZ5 .image-shape .label,#mermaid-svg-UjB7AHAhbeeJniZ5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-UjB7AHAhbeeJniZ5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .rough-node .label,#mermaid-svg-UjB7AHAhbeeJniZ5 .node .label,#mermaid-svg-UjB7AHAhbeeJniZ5 .image-shape .label,#mermaid-svg-UjB7AHAhbeeJniZ5 .icon-shape .label{text-align:center;}#mermaid-svg-UjB7AHAhbeeJniZ5 .node.clickable{cursor:pointer;}#mermaid-svg-UjB7AHAhbeeJniZ5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .arrowheadPath{fill:#333333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UjB7AHAhbeeJniZ5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-UjB7AHAhbeeJniZ5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UjB7AHAhbeeJniZ5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster text{fill:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 .cluster span{color:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-UjB7AHAhbeeJniZ5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-UjB7AHAhbeeJniZ5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-UjB7AHAhbeeJniZ5 .icon-shape,#mermaid-svg-UjB7AHAhbeeJniZ5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-UjB7AHAhbeeJniZ5 .icon-shape p,#mermaid-svg-UjB7AHAhbeeJniZ5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-UjB7AHAhbeeJniZ5 .icon-shape .label rect,#mermaid-svg-UjB7AHAhbeeJniZ5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-UjB7AHAhbeeJniZ5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-UjB7AHAhbeeJniZ5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-UjB7AHAhbeeJniZ5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端进程
准备业务数据
TCP/IP 协议软件
添加互联网包头
LAN 1 帧
本地网卡发送
路由器
读取包的目的地址
LAN 2 帧
换一套本地帧头
服务器网卡
接收
TCP/IP 协议软件
去掉协议头
服务器进程
读取数据
怎么看这张图: 业务数据从左到右一直是同一份"要交付的内容";中间的 LAN 帧会在经过路由器时被替换。不要把"本地链路用的帧"误认为"从源到终点完全不变的包"。
!note 原书模型的重点
原书故意忽略了最大传输单元、路由表如何维护、丢包后如何恢复等复杂细节,只保留"封装、转发、拆封"的主线。初学时先抓主线是正确的。
5.2.5 ⑤ 三个类比
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 快递的外箱与内件 | 内件 → 有效载荷;运单 → 头部 | 业务数据与控制信息分层 | 网络头部由协议严格定义,不是快递员手写 |
| 跨城换乘 | 不同车段 → 不同局域网;中转站 → 路由器 | 途中可以换本地运输方式 | 网络不一定只有一条路线,也不保证每段都成功 |
| 大楼收发室 | 网卡 → 收发室;内存 → 楼内暂存区;DMA → 自动传送带 | 网卡把外部数据交给内存 | DMA 仍受硬件和操作系统规则控制,不是"绕开 CPU 的无限速通道" |
5.2.6 ⑥ 容易混淆
| 概念 | 是什么 | 与另一个概念的关系 |
|---|---|---|
| 帧 frame | 某一段局域网链路上的传输单位 | 到路由器后可被拆掉并换成新帧 |
| 包 packet | 互联网协议用于跨网络递送的单位 | 通常被作为本地帧的有效载荷携带 |
| 头部 header | 地址、长度等控制信息 | 帮助网络与接收方处理数据 |
| 有效载荷 payload | 真正要递交的内容 | 对上层来说是数据,对下层来说可能是载荷 |
!info 补充解释
书中用集线器、桥等设备帮助建立直觉。现代以太网中更常见的是交换机;无论设备名称如何变化,本章对程序员真正重要的抽象仍是:不同网络能被路由器连接,协议头让数据能被正确转交。
5.2.7 ⑦ 面试/表达题
问题: 为什么网络包需要头部,不能只发送纯业务数据?
回答: 纯业务数据没有说明"要去哪里、长度是多少、该怎样处理"。头部携带这些控制信息,使网卡、路由器和协议软件能转发、拆分和交付它;业务数据则作为有效载荷被保留给更上层的程序。
!abstract 📌 一句话卡片|封装
网络不是把应用数据原样"瞬移"过去,而是让不同层为它依次加上可处理的外层信息,到达后再按相反方向拆开。
5.3 知识点 3:IP、域名、端口和连接------先把"找谁"讲清楚(⭐⭐⭐⭐⭐)
为什么重要: 套接字编程中最容易混的是"地址"。只背函数名而不懂 IP、域名、端口和连接对,就会把主机、服务、文件描述符混成一团。
5.3.1 ① 一句话
在本章的互联网模型中,IP 地址用于标识主机,域名是便于人记忆的名字,端口把通信进一步交给主机上的服务端点;一个 TCP 连接由客户端和服务器两端的套接字地址共同唯一确定。
5.3.2 ② TCP/IP 的分工:先别把所有"网络"混为一个词
原书把 TCP/IP 看作一组协作协议,并把讨论重点放在 TCP/IP 为应用程序提供的能力上:
| 名称 | 在本章中先这样理解 | 不负责什么 |
|---|---|---|
| IP | 给主机提供统一的地址表示,并尽力把数据报送往目标主机 | 不承诺每个数据报一定到达、按序到达或不重复 |
| UDP | 在 IP 之上让数据报能在进程之间传递 | 本章不展开讨论它 |
| TCP | 在 IP 之上提供进程之间可靠、全双工的连接 | 不替应用定义 HTTP 这类业务消息格式 |
| TCP/IP | 为本章套接字程序提供的整体通信基础 | 不是"浏览器专用协议" |
#mermaid-svg-K1D24NL76W4F2jjL{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-K1D24NL76W4F2jjL .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-K1D24NL76W4F2jjL .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-K1D24NL76W4F2jjL .error-icon{fill:#552222;}#mermaid-svg-K1D24NL76W4F2jjL .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-K1D24NL76W4F2jjL .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-K1D24NL76W4F2jjL .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-K1D24NL76W4F2jjL .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-K1D24NL76W4F2jjL .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-K1D24NL76W4F2jjL .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-K1D24NL76W4F2jjL .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-K1D24NL76W4F2jjL .marker{fill:#333333;stroke:#333333;}#mermaid-svg-K1D24NL76W4F2jjL .marker.cross{stroke:#333333;}#mermaid-svg-K1D24NL76W4F2jjL svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-K1D24NL76W4F2jjL p{margin:0;}#mermaid-svg-K1D24NL76W4F2jjL .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-K1D24NL76W4F2jjL .cluster-label text{fill:#333;}#mermaid-svg-K1D24NL76W4F2jjL .cluster-label span{color:#333;}#mermaid-svg-K1D24NL76W4F2jjL .cluster-label span p{background-color:transparent;}#mermaid-svg-K1D24NL76W4F2jjL .label text,#mermaid-svg-K1D24NL76W4F2jjL span{fill:#333;color:#333;}#mermaid-svg-K1D24NL76W4F2jjL .node rect,#mermaid-svg-K1D24NL76W4F2jjL .node circle,#mermaid-svg-K1D24NL76W4F2jjL .node ellipse,#mermaid-svg-K1D24NL76W4F2jjL .node polygon,#mermaid-svg-K1D24NL76W4F2jjL .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-K1D24NL76W4F2jjL .rough-node .label text,#mermaid-svg-K1D24NL76W4F2jjL .node .label text,#mermaid-svg-K1D24NL76W4F2jjL .image-shape .label,#mermaid-svg-K1D24NL76W4F2jjL .icon-shape .label{text-anchor:middle;}#mermaid-svg-K1D24NL76W4F2jjL .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-K1D24NL76W4F2jjL .rough-node .label,#mermaid-svg-K1D24NL76W4F2jjL .node .label,#mermaid-svg-K1D24NL76W4F2jjL .image-shape .label,#mermaid-svg-K1D24NL76W4F2jjL .icon-shape .label{text-align:center;}#mermaid-svg-K1D24NL76W4F2jjL .node.clickable{cursor:pointer;}#mermaid-svg-K1D24NL76W4F2jjL .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-K1D24NL76W4F2jjL .arrowheadPath{fill:#333333;}#mermaid-svg-K1D24NL76W4F2jjL .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-K1D24NL76W4F2jjL .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-K1D24NL76W4F2jjL .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-K1D24NL76W4F2jjL .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-K1D24NL76W4F2jjL .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-K1D24NL76W4F2jjL .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-K1D24NL76W4F2jjL .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-K1D24NL76W4F2jjL .cluster text{fill:#333;}#mermaid-svg-K1D24NL76W4F2jjL .cluster span{color:#333;}#mermaid-svg-K1D24NL76W4F2jjL div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-K1D24NL76W4F2jjL .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-K1D24NL76W4F2jjL rect.text{fill:none;stroke-width:0;}#mermaid-svg-K1D24NL76W4F2jjL .icon-shape,#mermaid-svg-K1D24NL76W4F2jjL .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-K1D24NL76W4F2jjL .icon-shape p,#mermaid-svg-K1D24NL76W4F2jjL .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-K1D24NL76W4F2jjL .icon-shape .label rect,#mermaid-svg-K1D24NL76W4F2jjL .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-K1D24NL76W4F2jjL .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-K1D24NL76W4F2jjL .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-K1D24NL76W4F2jjL :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用程序
HTTP 请求或其他业务字节
TCP
把两端进程连成可靠全双工字节流
IP
按主机地址尽力递送数据报
底层网络
把数据送过各段链路
目标主机上的 IP、TCP
目标进程
读取同一条字节流
怎么看这张图: 从上往下看是发送时的封装路径;接收端大致反向拆开。HTTP 不需要自己解决"跨网找主机"和"字节重传",它建立在 TCP 提供的连接之上;TCP 又建立在 IP 的递送能力之上。
!info 原书范围
原书以 IPv4 为主要背景,说明 IPv6 已存在但本章不展开;这里不要把 IPv4 的 32 位地址规则误当成所有网络地址的永久规则。
5.3.3 ③ IPv4 地址与字节顺序
IPv4 地址可以先看成一个 32 位无符号整数。人通常更愿意写成点分十进制,例如"每个字节写一个十进制数,中间用点隔开";程序和网络协议则需要明确字节在内存与报文里的顺序。
- 主机字节顺序:本机 CPU 习惯的存储顺序,可能因机器而异。
- 网络字节顺序:网络协议约定使用大端顺序。
- 转换函数:主机到网络可用 htonl、htons;网络到主机可用 ntohl、ntohs。l 表示 32 位 long,s 表示 16 位 short。
- 文本与二进制形式转换:inet_pton 将文字形式地址转换为二进制网络地址;inet_ntop 做反向转换。原书指出它们可处理 IPv4,也可处理 IPv6。
!warning 别把"字节序"当成显示问题
你在屏幕上看到的地址字符串不等于它在内存、寄存器或网络报文中的字节排列。数据在两端解释时必须遵守同一套网络字节顺序,否则端口号、长度等数值会被读错。
5.3.4 ④ 域名与 DNS:人记名字,网络用地址
域名是由点分隔的名字,例如"主机名.子域.顶级域"。DNS(域名系统)维护域名与 IP 地址集合之间的映射,让程序可以从人容易输入的名字找到可用于连接的地址。
#mermaid-svg-TrdyQrhPMUhbjhOO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TrdyQrhPMUhbjhOO .error-icon{fill:#552222;}#mermaid-svg-TrdyQrhPMUhbjhOO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TrdyQrhPMUhbjhOO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TrdyQrhPMUhbjhOO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TrdyQrhPMUhbjhOO .marker.cross{stroke:#333333;}#mermaid-svg-TrdyQrhPMUhbjhOO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TrdyQrhPMUhbjhOO p{margin:0;}#mermaid-svg-TrdyQrhPMUhbjhOO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster-label text{fill:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster-label span{color:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster-label span p{background-color:transparent;}#mermaid-svg-TrdyQrhPMUhbjhOO .label text,#mermaid-svg-TrdyQrhPMUhbjhOO span{fill:#333;color:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO .node rect,#mermaid-svg-TrdyQrhPMUhbjhOO .node circle,#mermaid-svg-TrdyQrhPMUhbjhOO .node ellipse,#mermaid-svg-TrdyQrhPMUhbjhOO .node polygon,#mermaid-svg-TrdyQrhPMUhbjhOO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TrdyQrhPMUhbjhOO .rough-node .label text,#mermaid-svg-TrdyQrhPMUhbjhOO .node .label text,#mermaid-svg-TrdyQrhPMUhbjhOO .image-shape .label,#mermaid-svg-TrdyQrhPMUhbjhOO .icon-shape .label{text-anchor:middle;}#mermaid-svg-TrdyQrhPMUhbjhOO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TrdyQrhPMUhbjhOO .rough-node .label,#mermaid-svg-TrdyQrhPMUhbjhOO .node .label,#mermaid-svg-TrdyQrhPMUhbjhOO .image-shape .label,#mermaid-svg-TrdyQrhPMUhbjhOO .icon-shape .label{text-align:center;}#mermaid-svg-TrdyQrhPMUhbjhOO .node.clickable{cursor:pointer;}#mermaid-svg-TrdyQrhPMUhbjhOO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TrdyQrhPMUhbjhOO .arrowheadPath{fill:#333333;}#mermaid-svg-TrdyQrhPMUhbjhOO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TrdyQrhPMUhbjhOO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TrdyQrhPMUhbjhOO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TrdyQrhPMUhbjhOO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TrdyQrhPMUhbjhOO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TrdyQrhPMUhbjhOO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster text{fill:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO .cluster span{color:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TrdyQrhPMUhbjhOO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TrdyQrhPMUhbjhOO rect.text{fill:none;stroke-width:0;}#mermaid-svg-TrdyQrhPMUhbjhOO .icon-shape,#mermaid-svg-TrdyQrhPMUhbjhOO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TrdyQrhPMUhbjhOO .icon-shape p,#mermaid-svg-TrdyQrhPMUhbjhOO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TrdyQrhPMUhbjhOO .icon-shape .label rect,#mermaid-svg-TrdyQrhPMUhbjhOO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TrdyQrhPMUhbjhOO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TrdyQrhPMUhbjhOO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TrdyQrhPMUhbjhOO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户输入域名
DNS / 名称解析
一个或多个 IP 地址
再结合端口
目标服务的套接字地址
怎么看这张图: 域名解析只解决"主机地址候选是什么",还没有完成连接;还必须加上端口,并由套接字接口发起连接。一个域名可能对应多个 IP,一个 IP 也可能服务多个域名,所以不能把它们背成永远一一对应。
原书给出的常见关系包括:
- 一个域名映射到一个 IP 地址;
- 多个域名映射到同一个 IP 地址;
- 一个域名映射到一组 IP 地址;
- 合法的域名节点未必直接映射到 IP 地址;
- localhost 是本机专用名称,通常映射到回环地址 127.0.0.1,用于本机客户端和本机服务器通信。
5.3.5 ⑤ 端口、套接字地址与连接对
IP 地址大致找到一台主机,但一台主机上可能同时有 Web 服务、数据库、开发服务器等许多进程,所以还需要 16 位端口号。
- 套接字地址:IP 地址 + 端口号,常写成"地址:端口"。
- 服务器端口:通常使用约定好的知名端口或应用配置的端口,等待客户端连接。
- 客户端端口:客户端发起连接时,内核通常分配一个临时端口。
- 套接字对 :一条 TCP 连接由两端套接字地址共同确定,可抽象写为
(客户端地址:客户端端口,服务器地址:服务器端口)。
#mermaid-svg-TsuaRzNWJscpbNCK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TsuaRzNWJscpbNCK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TsuaRzNWJscpbNCK .error-icon{fill:#552222;}#mermaid-svg-TsuaRzNWJscpbNCK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TsuaRzNWJscpbNCK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TsuaRzNWJscpbNCK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TsuaRzNWJscpbNCK .marker.cross{stroke:#333333;}#mermaid-svg-TsuaRzNWJscpbNCK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TsuaRzNWJscpbNCK p{margin:0;}#mermaid-svg-TsuaRzNWJscpbNCK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TsuaRzNWJscpbNCK .cluster-label text{fill:#333;}#mermaid-svg-TsuaRzNWJscpbNCK .cluster-label span{color:#333;}#mermaid-svg-TsuaRzNWJscpbNCK .cluster-label span p{background-color:transparent;}#mermaid-svg-TsuaRzNWJscpbNCK .label text,#mermaid-svg-TsuaRzNWJscpbNCK span{fill:#333;color:#333;}#mermaid-svg-TsuaRzNWJscpbNCK .node rect,#mermaid-svg-TsuaRzNWJscpbNCK .node circle,#mermaid-svg-TsuaRzNWJscpbNCK .node ellipse,#mermaid-svg-TsuaRzNWJscpbNCK .node polygon,#mermaid-svg-TsuaRzNWJscpbNCK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TsuaRzNWJscpbNCK .rough-node .label text,#mermaid-svg-TsuaRzNWJscpbNCK .node .label text,#mermaid-svg-TsuaRzNWJscpbNCK .image-shape .label,#mermaid-svg-TsuaRzNWJscpbNCK .icon-shape .label{text-anchor:middle;}#mermaid-svg-TsuaRzNWJscpbNCK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TsuaRzNWJscpbNCK .rough-node .label,#mermaid-svg-TsuaRzNWJscpbNCK .node .label,#mermaid-svg-TsuaRzNWJscpbNCK .image-shape .label,#mermaid-svg-TsuaRzNWJscpbNCK .icon-shape .label{text-align:center;}#mermaid-svg-TsuaRzNWJscpbNCK .node.clickable{cursor:pointer;}#mermaid-svg-TsuaRzNWJscpbNCK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TsuaRzNWJscpbNCK .arrowheadPath{fill:#333333;}#mermaid-svg-TsuaRzNWJscpbNCK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TsuaRzNWJscpbNCK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TsuaRzNWJscpbNCK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TsuaRzNWJscpbNCK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TsuaRzNWJscpbNCK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TsuaRzNWJscpbNCK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TsuaRzNWJscpbNCK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TsuaRzNWJscpbNCK .cluster text{fill:#333;}#mermaid-svg-TsuaRzNWJscpbNCK .cluster span{color:#333;}#mermaid-svg-TsuaRzNWJscpbNCK div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TsuaRzNWJscpbNCK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TsuaRzNWJscpbNCK rect.text{fill:none;stroke-width:0;}#mermaid-svg-TsuaRzNWJscpbNCK .icon-shape,#mermaid-svg-TsuaRzNWJscpbNCK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TsuaRzNWJscpbNCK .icon-shape p,#mermaid-svg-TsuaRzNWJscpbNCK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TsuaRzNWJscpbNCK .icon-shape .label rect,#mermaid-svg-TsuaRzNWJscpbNCK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TsuaRzNWJscpbNCK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TsuaRzNWJscpbNCK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TsuaRzNWJscpbNCK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 客户端进程
客户端套接字地址
客户端 IP + 临时端口
TCP 连接
由两端地址共同唯一确定
服务器套接字地址
服务器 IP + 服务端口
服务器进程
怎么看这张图: "服务器在 80 端口监听"只说明服务器这一端的地址;真正区分两位不同客户端的,是两端地址组成的整个连接对。两位客户端即使访问同一个服务器端口,也会拥有不同的客户端地址/临时端口组合。
5.3.6 ⑥ 三个类比
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 城市地址与房间号 | 城市街道门牌 → IP;楼内房间号 → 端口 | 先到主机,再到服务 | IP/端口是协议字段,不是地理位置 |
| 通讯录与电话号码 | 姓名 → 域名;电话号码 → IP 地址;查号台 → DNS | 人可读名字需要解析 | DNS 结果会变化,也可能返回多个候选 |
| 两人通话记录 | 双方号码 → 两端套接字地址 | 连接不是只由"被叫号码"决定 | TCP 还有握手、状态和字节流等细节 |
5.3.7 ⑦ 容易混淆
| 容易混淆 | 正确理解 | 记忆办法 |
|---|---|---|
| 域名 = IP 地址 | 域名是名字,IP 是地址;关系可以多对多或缺失 | 先"查通讯录",再"拨号码" |
| IP 地址 = 服务 | IP 先标识主机,端口继续定位服务端点 | "到楼"后还要"找房间" |
| 端口 = 物理插口 | 本章端口是软件通信端点编号 | 看它是否出现在套接字地址里 |
| 套接字地址 = 文件描述符 | 地址描述通信对象在哪里;描述符是本进程手里的操作把手 | 地址像收件地址,描述符像手中的取件号 |
| TCP = HTTP | TCP 提供字节流连接;HTTP 规定字节表达什么 | 下层运输,上层对话规则 |
5.3.8 ⑧ 面试/表达题
问题: 为什么一条 TCP 连接不能只用"服务器 IP + 端口"来唯一标识?
回答: 因为同一个服务器端口可同时服务多个客户端。每个客户端连接都带有自己的客户端 IP 和临时端口,因此要使用"客户端地址:端口 + 服务器地址:端口"这一对端点才能区分具体连接。
!abstract 📌 一句话卡片|寻址
域名帮人记、IP 帮网络找主机、端口帮主机找服务、两端套接字地址共同标识一条 TCP 连接。
5.4 知识点 4:套接字接口------把网络连接变成可读写的描述符(⭐⭐⭐⭐⭐)
为什么重要: 这是本章最需要掌握的 API 骨架。理解它后,echo、HTTP 客户端和 TINY 都只是"在连接建立后读取和写入不同格式的字节"。
5.4.1 ① 一句话
套接字接口是一组函数;在 Linux 程序看来,套接字是一个打开的文件描述符,客户端用它主动连接服务器,服务器用它绑定地址、监听连接并为每个客户端取得一个已连接描述符。
5.4.2 ② 先建立两个视角
- 内核视角:套接字是通信的一端。
- C 程序视角:套接字对应一个可进行 Unix I/O 的打开文件描述符。
因此,连接建立后,程序可以使用本书前面学过的读写方式与对端交换字节。但"能读写"不意味着"消息自动有边界";TCP 给的是字节流,HTTP 等上层协议要负责规定一条消息从哪里开始、在哪里结束。
5.4.3 ③ 客户端与服务器的调用路线
服务器 客户端 服务器 客户端 #mermaid-svg-32oJKMbsFOay8Jtu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-32oJKMbsFOay8Jtu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-32oJKMbsFOay8Jtu .error-icon{fill:#552222;}#mermaid-svg-32oJKMbsFOay8Jtu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-32oJKMbsFOay8Jtu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-32oJKMbsFOay8Jtu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-32oJKMbsFOay8Jtu .marker.cross{stroke:#333333;}#mermaid-svg-32oJKMbsFOay8Jtu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-32oJKMbsFOay8Jtu p{margin:0;}#mermaid-svg-32oJKMbsFOay8Jtu .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-32oJKMbsFOay8Jtu text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-32oJKMbsFOay8Jtu .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-32oJKMbsFOay8Jtu .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-32oJKMbsFOay8Jtu .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-32oJKMbsFOay8Jtu .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-32oJKMbsFOay8Jtu #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-32oJKMbsFOay8Jtu .sequenceNumber{fill:white;}#mermaid-svg-32oJKMbsFOay8Jtu #sequencenumber{fill:#333;}#mermaid-svg-32oJKMbsFOay8Jtu #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-32oJKMbsFOay8Jtu .messageText{fill:#333;stroke:none;}#mermaid-svg-32oJKMbsFOay8Jtu .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-32oJKMbsFOay8Jtu .labelText,#mermaid-svg-32oJKMbsFOay8Jtu .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-32oJKMbsFOay8Jtu .loopText,#mermaid-svg-32oJKMbsFOay8Jtu .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-32oJKMbsFOay8Jtu .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-32oJKMbsFOay8Jtu .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-32oJKMbsFOay8Jtu .noteText,#mermaid-svg-32oJKMbsFOay8Jtu .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-32oJKMbsFOay8Jtu .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-32oJKMbsFOay8Jtu .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-32oJKMbsFOay8Jtu .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-32oJKMbsFOay8Jtu .actorPopupMenu{position:absolute;}#mermaid-svg-32oJKMbsFOay8Jtu .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-32oJKMbsFOay8Jtu .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-32oJKMbsFOay8Jtu .actor-man circle,#mermaid-svg-32oJKMbsFOay8Jtu line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-32oJKMbsFOay8Jtu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} getaddrinfo、socketgetaddrinfo、socket、bind、listenconnect:发起连接请求accept:返回已连接描述符write:发送应用字节write:发送响应字节close读到 EOF、close 已连接描述符继续 accept 下一个连接
怎么看这张图: 服务器不是先 accept 再 listen;正确顺序是先把一个描述符绑定到地址并转换成监听状态,再由 accept 为每一次到来的连接生成新的已连接描述符。监听描述符保留着继续等下一位客户端。
5.4.4 ④ 函数各自解决什么
| 函数 | 谁常用 | 它解决的问题 | 成功后最重要的结果 |
|---|---|---|---|
| socket | 客户端、服务器 | 创建套接字描述符 | 得到"刚创建、尚未完成角色"的描述符 |
| connect | 客户端 | 主动请求连接到服务器地址 | 客户端描述符可读写;会阻塞到成功或出错 |
| bind | 服务器 | 把描述符与本地服务器套接字地址关联 | 内核知道该服务要使用哪个地址/端口 |
| listen | 服务器 | 将描述符变成监听描述符 | 开始排队接受连接请求 |
| accept | 服务器 | 等待一个连接请求并接收它 | 返回新的已连接描述符 |
| getaddrinfo | 两者 | 将主机名/地址与服务名/端口转换成可用套接字地址候选 | 返回 addrinfo 链表,使用后要释放 |
| getnameinfo | 两者 | 将套接字地址反向转成主机名/服务名字符串 | 用于展示对端信息等场景 |
!tip 用一句顺口溜记客户端
查地址 → 建套接字 → connect → 读写 → close。
!tip 用一句顺口溜记服务器查地址 → 建套接字 → bind → listen → accept → 用新描述符读写 → close 新描述符 → 回到 accept。
5.4.5 ⑤ 监听描述符与已连接描述符:本章最大易错点
| 对比 | 监听描述符 listenfd | 已连接描述符 connfd |
|---|---|---|
| 服务对象 | 所有未来客户端的连接请求 | 某一位已接入的客户端 |
| 生命周期 | 服务器启动后通常长期存在 | 一次客户服务结束后关闭 |
| 能否直接处理该客户端的业务字节 | 不能拿它当作某一个客户端的通信通道 | 可以用它读写该客户端 |
| 由谁产生 | socket → bind → listen | accept 的返回值 |
为什么要分两个? 如果服务器只有一个"总描述符"且在服务客户端时失去监听能力,它就无法继续接受后续连接。分离后,服务器能保留 listenfd 等待新请求,同时用不同 connfd 表示不同已建立连接;第 12 章会在此基础上讨论并发服务。
5.4.6 ⑥ getaddrinfo 为什么值得优先学
早期接口经常要求程序员手工拼 IPv4 地址结构。getaddrinfo 把"主机名、数值地址、服务名、端口、协议偏好"等转换为一组可直接传给 socket、connect 或 bind 的地址记录。原书强调它可重入、与特定 IP 版本无关,因此更适合作为现代接口的入口。
使用它时必须记住两件资源管理事:
- 结果可能是一个候选地址链表,要依次尝试;
- 用完后调用 freeaddrinfo 释放这条链表。
原书提供的 open_clientfd 和 open_listenfd 是教学辅助函数:它们把"查地址 + 尝试 socket/connect 或 socket/bind/listen"的重复流程封装起来。读 TINY 时,把它们当作"打开客户端连接"和"打开监听端口"的语义化入口即可。
5.4.7 ⑦ echo 示例告诉我们什么
echo 客户端把输入的每一行发给服务器,再读回同一行显示;echo 服务器读到一行就原样写回。这个例子证明:套接字一旦连接,读写模式与文件 I/O 很相似。
但它还有三个重要边界:
- 书中的 echo 服务器一次只处理一个客户端,属于迭代服务器;
- 一端关闭连接后,另一端读到最后一个字节之后会观察到 EOF;
- 健壮服务器还必须考虑:客户端提前关闭时继续 write 可能引发 SIGPIPE 或得到 EPIPE,不能把教学代码当成生产级错误处理。
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 电话总机 | listenfd → 总机等待来电;connfd → 一通已接起的电话 | 为什么"等待来电"和"通话"是不同对象 | TCP 连接不是语音通话,也没有天然的句子边界 |
| 门卫与访客证 | listenfd → 大门;accept → 发一张访客证;connfd → 某位访客的通行记录 | 新连接要有独立的处理把手 | 真实套接字不保存人的身份,只保存通信状态 |
| 地址簿 + 尝试拨号 | getaddrinfo → 查到多个号码;遍历候选 → 逐个拨打 | 为什么地址解析可能返回链表 | 网络失败原因比"无人接听"复杂得多 |
5.4.8 ⑧ 常见错误与判断法
| 错误理解 | 正确理解 | 立刻自查 |
|---|---|---|
| socket 一返回就能读写服务器 | 它只创建描述符;客户端还要 connect,服务器还要 bind/listen/accept | "对端是谁"是否已建立? |
| accept 返回的还是监听描述符 | accept 返回新的 connfd | 是否只服务一位客户端? |
| 只要主机名正确就能连接 | 还需要服务/端口、网络可达性和正确协议 | 地址和端口是否都确定? |
| close 只是可选清理 | 描述符和地址解析结果都要在路径上正确释放 | 错误分支是否也 close/free? |
| TCP 的 read 一次对应一条 HTTP 消息 | TCP 是字节流;读到多少由时机和缓冲区决定 | 消息边界由哪层协议定义? |
5.4.9 ⑨ 面试/表达题
问题: 服务器为什么需要监听描述符和已连接描述符两类对象?
回答: 监听描述符持续代表服务器的公开服务入口,用于等待新连接;accept 为每个成功连接返回独立的已连接描述符,业务读写发生在后者上。分离后服务器既不会丢失监听能力,也能把每个客户端连接当作独立会话处理。
!abstract 📌 一句话卡片|套接字生命周期
客户端用 connect 把"刚创建的套接字"接到服务器;服务器用 listen 等待、用 accept 取出"这一位客户端"的新描述符,业务读写永远围绕这对已连接端点进行。
5.5 知识点 5:Web、URL 与 HTTP 事务(⭐⭐⭐⭐⭐)
为什么重要: HTTP 把"套接字里的一串字节"变成浏览器和 Web 服务器都能理解的请求与响应;本章后半段的 TINY 就是在手工实现这套最小规则。
5.5.1 ① 一句话
Web 客户端(通常是浏览器)通过 HTTP 向 Web 服务器请求内容;服务器用 HTTP 响应返回内容及其类型、长度、处理结果等信息。
5.5.2 ② 从套接字到网页:中间还差一份协议
套接字只提供"连接建立后可以读写字节"的能力,它并不知道这些字节是网页、图片、文件还是聊天消息。HTTP 是一份应用层约定:双方约定请求包含什么、响应包含什么、每个字段应该怎样解释。
原书用一个典型模型说明 Web 事务:
- 浏览器与服务器建立互联网连接;
- 浏览器发送 HTTP 请求;
- 服务器读取请求,找到或生成内容;
- 服务器发送 HTTP 响应;
- 浏览器按响应中的类型解释并展示内容;
- 书中的简化示例在一次事务后关闭连接。
!info 补充解释
原书同时提到 HTTP/1.0 与 HTTP/1.1,并用关闭连接的教学路径简化讨论;现实浏览器还会遇到持久连接、HTTPS、HTTP/2、HTTP/3、缓存、重定向、压缩等机制。本笔记先严格抓住原书的请求/响应骨架,不把后续机制混进第一遍学习。
5.5.3 ③ URL:请求目标的"说明书"
URL(统一资源定位符)把"去哪个服务器、找哪项内容、需要带什么参数"放在一串文字里。可以先按下面的抽象格式理解:
text
协议://主机名:端口/资源路径?参数
- 主机名帮助客户端决定要联系哪台主机;
- 端口帮助客户端决定要联系该主机上的哪个服务;省略时会使用协议的默认约定;
- 资源路径帮助服务器在自己的内容规则中定位资源;
- 参数常出现在问号之后,原书的 CGI 示例用与号分隔多个参数。
!warning URL 中的斜杠不是 Linux 文件系统根目录
它首先是 Web 服务器理解的资源路径。服务器可以自己决定它映射到哪个实际目录、哪个程序或哪个业务路由。
5.5.4 ④ 内容、MIME 类型与静态/动态的区别
| 概念 | 它回答的问题 | 原书中的典型例子 |
|---|---|---|
| MIME 类型 | 这串内容应该按什么格式解释? | text/html、text/plain、image/gif、image/png、image/jpeg |
| 静态内容 | 服务器是否只需读取并返回已有文件? | HTML、文本、图片等磁盘文件 |
| 动态内容 | 服务器是否需要运行一个程序,把输出作为响应内容? | CGI 程序根据参数生成 HTML |
| URL | 客户端要什么 | 主机、端口、路径与可选参数 |
!tip 最小记忆
路径告诉服务器"找什么";MIME 告诉客户端"拿到后怎样看";静态/动态告诉服务器"读取文件还是运行程序"。
5.5.5 ⑤ 一笔 HTTP 事务的骨架
Web服务器 浏览器 Web服务器 浏览器 #mermaid-svg-5salJKFg5pr6siNF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5salJKFg5pr6siNF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5salJKFg5pr6siNF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5salJKFg5pr6siNF .error-icon{fill:#552222;}#mermaid-svg-5salJKFg5pr6siNF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5salJKFg5pr6siNF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5salJKFg5pr6siNF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5salJKFg5pr6siNF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5salJKFg5pr6siNF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5salJKFg5pr6siNF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5salJKFg5pr6siNF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5salJKFg5pr6siNF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5salJKFg5pr6siNF .marker.cross{stroke:#333333;}#mermaid-svg-5salJKFg5pr6siNF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5salJKFg5pr6siNF p{margin:0;}#mermaid-svg-5salJKFg5pr6siNF .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5salJKFg5pr6siNF text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-5salJKFg5pr6siNF .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-5salJKFg5pr6siNF .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-5salJKFg5pr6siNF .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-5salJKFg5pr6siNF .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-5salJKFg5pr6siNF #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-5salJKFg5pr6siNF .sequenceNumber{fill:white;}#mermaid-svg-5salJKFg5pr6siNF #sequencenumber{fill:#333;}#mermaid-svg-5salJKFg5pr6siNF #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-5salJKFg5pr6siNF .messageText{fill:#333;stroke:none;}#mermaid-svg-5salJKFg5pr6siNF .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5salJKFg5pr6siNF .labelText,#mermaid-svg-5salJKFg5pr6siNF .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-5salJKFg5pr6siNF .loopText,#mermaid-svg-5salJKFg5pr6siNF .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-5salJKFg5pr6siNF .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-5salJKFg5pr6siNF .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-5salJKFg5pr6siNF .noteText,#mermaid-svg-5salJKFg5pr6siNF .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-5salJKFg5pr6siNF .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5salJKFg5pr6siNF .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5salJKFg5pr6siNF .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-5salJKFg5pr6siNF .actorPopupMenu{position:absolute;}#mermaid-svg-5salJKFg5pr6siNF .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-5salJKFg5pr6siNF .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-5salJKFg5pr6siNF .actor-man circle,#mermaid-svg-5salJKFg5pr6siNF line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-5salJKFg5pr6siNF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} TCP connect请求行一个或多个请求报头空行,报头结束解析目标并准备内容响应行一个或多个响应报头空行,报头结束响应主体关闭连接(本章简化路径)
怎么看这张图: 纵向先看"请求的三部分",再看"响应的四部分"。空行不是装饰;它是报头结束、主体开始的分界标记。
请求的最小形状:
http
GET /资源路径 HTTP/1.1
Host: 主机名
- 第一行是请求行,可抽象为"方法 资源标识 协议版本"。
- 后面的"字段名: 值"是请求报头;书中强调 HTTP/1.1 的 Host 报头尤其重要。
- 空行终止报头列表。对带主体的请求,主体在空行之后。
响应的最小形状:
http
HTTP/1.0 200 OK
Content-Type: text/html
Content-Length: 字节数
响应主体的字节
- 第一行是响应行:协议版本、状态码、状态消息。
- 响应报头说明主体的额外信息;本章最常用的是 Content-Type 与 Content-Length。
- 空行后才是浏览器要显示、保存或解码的主体内容。
5.5.6 ⑥ 书中常见状态码
| 状态码 | 书中含义(用大白话) | 初学者先怎么理解 |
|---|---|---|
| 200 | 成功 | 服务器正常处理了请求 |
| 301 | 永久移动 | 内容位置改变,客户端需按 location 线索处理 |
| 400 | 错误请求 | 服务器无法理解该请求 |
| 403 | 禁止 | 找到资源或路径,但服务器拒绝访问 |
| 404 | 未发现 | 服务器找不到所请求文件/资源 |
| 501 | 未实现 | 服务器不支持该请求方法 |
| 505 | HTTP 版本不支持 | 服务器不支持请求所用版本 |
!warning 404 不等于"网络断了"
404 的前提恰恰通常是:你已经联系上服务器,并且服务器能用 HTTP 告诉你"没有这个资源"。网络、DNS、连接和 HTTP 路径问题要分层定位。
5.5.7 ⑦ GET、POST 与参数
- 对原书讲解的 GET,参数可以在 URI 中传递,问号分隔路径和参数,多个参数可用与号分隔;空格等特殊字符需要编码。
- 对 POST,参数位于请求主体,而不是 URI 中。
这只说明"参数放在哪里",不自动代表权限、安全或数据可靠性。参数仍需要由服务器验证、解析和限制。
5.5.8 ⑧ 三个类比
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 餐厅菜单与出餐单 | URL → 点单目标;请求 → 点单;响应 → 出餐 | 客户端必须说清"想要什么" | HTTP 是严格文本格式,不会自动帮你纠正语病 |
| 包裹标签 | Content-Type → 物品类别;Content-Length → 重量/数量说明 | 为什么接收方需要元信息 | 网络长度按字节计算,不是人眼看到的字符数 |
| 快递状态 | 200、404 等 → 办理结果代码 | 状态码先给结论 | 具体错误原因往往还要看报头、主体和服务器日志 |
5.5.9 ⑨ 容易混淆
| 容易混淆 | 正确理解 | 如何判断 |
|---|---|---|
| HTTP = 网线或 TCP | HTTP 是应用层的消息规则 | 是否在规定请求行、报头和主体? |
| URL 路径 = 本机绝对文件路径 | URL 路径由服务器解释 | 是否经过服务器的路由/映射规则? |
| Content-Type = 文件名后缀 | 类型是响应中给客户端的明确说明 | 浏览器应以响应语义为准 |
| 200 = 页面一定"内容正确" | 只说明 HTTP 层处理成功 | 业务错误也可能被页面正文表达 |
| read 一次 = 一整个 HTTP 请求 | TCP 不保留 HTTP 消息边界 | 是否按协议持续读到报头/主体边界? |
5.5.10 ⑩ 面试/表达题
问题: HTTP 请求和响应为什么都需要"报头 + 空行 + 主体"的结构?
回答: 报头提供协议版本、主机、内容类型、长度等元数据;空行明确告诉接收方报头到此结束;之后的主体才是实际内容。这样接收方才能先知道怎样解析和接收后面的字节。
!abstract 📌 一句话卡片|HTTP
套接字负责把字节送到对端,HTTP 负责让双方一致地把这批字节读成"请求、元信息和内容"。
5.6 知识点 6:CGI 与动态内容------服务器怎样把"运行程序的结果"回给浏览器(⭐⭐⭐⭐)
为什么重要: CGI 把第 8、10、11 章的进程、环境变量、文件描述符重定向、套接字和 HTTP 一次串起来。即使现代 Web 开发很少直接手写 CGI,理解它仍能帮你看懂"服务器如何把请求交给业务程序"。
5.6.1 ① 一句话
当请求的是动态内容时,服务器可派生子进程运行目标程序,把请求参数放进环境变量或标准输入,并把子进程标准输出重定向到已连接套接字;子进程写出的响应便会送到客户端。
5.6.2 ② 大白话解释
静态内容像"从书架拿现成书";动态内容像"把题目交给计算员,等他算完再把新结果交给你"。服务器本身负责接待、解析与转交;具体的动态程序负责根据参数生成内容。
CGI(通用网关接口)规定了这几件事怎样交接:参数怎么给程序、服务器信息怎样提供、程序输出怎样回到客户端。原书用一个"把两个数相加并生成 HTML"的小程序演示了这条路径。
5.6.3 ③ 动态请求的完整接力
#mermaid-svg-XXp7uqEV0k6oekJI{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XXp7uqEV0k6oekJI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XXp7uqEV0k6oekJI .error-icon{fill:#552222;}#mermaid-svg-XXp7uqEV0k6oekJI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XXp7uqEV0k6oekJI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XXp7uqEV0k6oekJI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XXp7uqEV0k6oekJI .marker.cross{stroke:#333333;}#mermaid-svg-XXp7uqEV0k6oekJI svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XXp7uqEV0k6oekJI p{margin:0;}#mermaid-svg-XXp7uqEV0k6oekJI .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XXp7uqEV0k6oekJI .cluster-label text{fill:#333;}#mermaid-svg-XXp7uqEV0k6oekJI .cluster-label span{color:#333;}#mermaid-svg-XXp7uqEV0k6oekJI .cluster-label span p{background-color:transparent;}#mermaid-svg-XXp7uqEV0k6oekJI .label text,#mermaid-svg-XXp7uqEV0k6oekJI span{fill:#333;color:#333;}#mermaid-svg-XXp7uqEV0k6oekJI .node rect,#mermaid-svg-XXp7uqEV0k6oekJI .node circle,#mermaid-svg-XXp7uqEV0k6oekJI .node ellipse,#mermaid-svg-XXp7uqEV0k6oekJI .node polygon,#mermaid-svg-XXp7uqEV0k6oekJI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XXp7uqEV0k6oekJI .rough-node .label text,#mermaid-svg-XXp7uqEV0k6oekJI .node .label text,#mermaid-svg-XXp7uqEV0k6oekJI .image-shape .label,#mermaid-svg-XXp7uqEV0k6oekJI .icon-shape .label{text-anchor:middle;}#mermaid-svg-XXp7uqEV0k6oekJI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XXp7uqEV0k6oekJI .rough-node .label,#mermaid-svg-XXp7uqEV0k6oekJI .node .label,#mermaid-svg-XXp7uqEV0k6oekJI .image-shape .label,#mermaid-svg-XXp7uqEV0k6oekJI .icon-shape .label{text-align:center;}#mermaid-svg-XXp7uqEV0k6oekJI .node.clickable{cursor:pointer;}#mermaid-svg-XXp7uqEV0k6oekJI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XXp7uqEV0k6oekJI .arrowheadPath{fill:#333333;}#mermaid-svg-XXp7uqEV0k6oekJI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XXp7uqEV0k6oekJI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XXp7uqEV0k6oekJI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XXp7uqEV0k6oekJI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XXp7uqEV0k6oekJI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XXp7uqEV0k6oekJI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XXp7uqEV0k6oekJI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XXp7uqEV0k6oekJI .cluster text{fill:#333;}#mermaid-svg-XXp7uqEV0k6oekJI .cluster span{color:#333;}#mermaid-svg-XXp7uqEV0k6oekJI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XXp7uqEV0k6oekJI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XXp7uqEV0k6oekJI rect.text{fill:none;stroke-width:0;}#mermaid-svg-XXp7uqEV0k6oekJI .icon-shape,#mermaid-svg-XXp7uqEV0k6oekJI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XXp7uqEV0k6oekJI .icon-shape p,#mermaid-svg-XXp7uqEV0k6oekJI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XXp7uqEV0k6oekJI .icon-shape .label rect,#mermaid-svg-XXp7uqEV0k6oekJI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XXp7uqEV0k6oekJI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XXp7uqEV0k6oekJI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XXp7uqEV0k6oekJI :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 浏览器请求动态资源
TINY 解析 URI
识别动态内容与参数
fork 创建子进程
设置 CGI 环境
dup2 重定向标准输出
execve 运行 CGI
CGI 写响应报头和主体
浏览器收到动态页面
怎么看这张图: 最关键的箭头是"标准输出 → connfd"。CGI 程序以为自己在向标准输出打印内容,但文件描述符已被重定向,所以字节实际上进入网络连接。
5.6.4 ④ 参数和环境变量怎么交接
- GET 参数:原书示例从 URI 中取出问号后的参数串,并由与号分隔多个参数。
- POST 参数:参数在 HTTP 请求主体中;子进程需要从被重定向的标准输入读取它们。
- QUERY_STRING:原书示例把 GET 参数放入该环境变量,CGI 程序可用 getenv 读取。
- 其他信息:CGI 还可获得请求方法、服务器端口、客户端地址、内容类型和内容长度等环境变量。
5.6.5 ⑤ 为什么 CGI 程序要自己写部分 HTTP 信息
父进程不知道子进程最终会产生什么类型、多少字节的内容。因此,CGI 程序必须对自己的输出负责:写出适当的 Content-Type、Content-Length 和终止报头的空行,然后再写响应主体。不能只输出 HTML 文本就指望浏览器一定能正确理解。
5.6.6 ⑥ 类比与边界
| 生活类比 | 对应关系 | 帮你理解什么 | 类比边界 |
|---|---|---|---|
| 前台与后厨 | TINY → 前台;CGI 子进程 → 后厨;浏览器 → 顾客 | 前台把订单交给专门程序 | CGI 进程间传递的是字节、环境与描述符,不是口头订单 |
| 改接扩音器 | dup2 → 把输出接到客户线路 | 为什么 printf 的结果能到浏览器 | 重定向的是文件描述符,不是复制屏幕文字 |
| 考题答题卡 | URI/POST 参数 → 题目;CGI 输出 → 答案 | 动态内容依赖输入而变化 | 服务器还需要处理认证、校验、超时等大量现实问题 |
!warning 安全边界
TINY 的 CGI 分支是教学示例。现实服务器不能把用户输入不经验证地拼到路径、命令或环境中;还必须控制权限、资源、错误处理与并发。不要把本章代码直接暴露到公网。
5.6.7 ⑦ 面试/表达题
问题: 为什么 CGI 子进程将标准输出重定向到已连接描述符后,浏览器就能收到程序输出?
回答: 因为文件描述符决定了进程写入的目标。重定向后,子进程对标准输出的写入不再去终端,而是写进与浏览器相连的套接字;这些字节经过 TCP 送到浏览器,成为 HTTP 响应的一部分。
!abstract 📌 一句话卡片|CGI
动态服务器的核心交接是:把请求参数交给子进程,再把子进程标准输出接回客户端连接。