免责声明:本人所整理的本阅读笔记,旨在帮助理解和总结[网络是怎样连接的](作者:[户根勤])中的主要内容与观点,供个人学习和交流使用。笔记内容基于本人对书籍的理解和归纳,难免存在主观判断和理解偏差,可能与原著意图有所不同。
本笔记不构成对原书内容的完整呈现,也不代表对书中观点的认可或反对。使用本笔记者应结合原著及相关资料进行综合分析,谨慎采纳其中信息。
未经授权,严禁将本笔记用于任何商业用途,或大规模转载、复制、分发。如需引用或使用,请注明出处并尊重原著版权。
本人不对因使用本笔记所产生的任何直接或间接损失承担责任。
第1章 浏览器生成消息——探索浏览器内部
1×01 HTTP状态码概要
| 状态码 | 含义 |
| 1xx | 告知请求的处理进度和情况 |
| 2xx | 成功 |
| 3xx | 表示需要进一步操作 |
| 4xx | 客户端错误 |
| 5xx | 服务端错误 |
1×02 IP地址的解析
10.11.12.13
255.255.255.0
10.11.12.13/24
10.11.12.0/24
10.11.12.1/24
IP地址主体
子网掩码
子网掩码(采用网络号比特数)
0的时候表示整个子网
1表示对整个子网广播
IP地址的主机号
●全0:表示整个子网
●全1:表示广播
1×03 Socket库提供查询IP地址的功能
Socket库是网络开发中的一种标准库
1×04 解析器工作步骤
①应用程序停止工作
②解析器开始运行并生成查询IP地址要求
③Socket库根据要求生成DNS可读的查询命令
④协议栈中生成并发送UDP信息
⑤利用网卡将③④送往DNS
⑥DNS服务器返回IP信息
⑦协议栈接收UDP信息
⑧Socket库接收DNS服务器返回的IP地址响应信息
⑨解析器读取IP地址写入应用程序
⑩应用程序继续工作
graph TD A[应用程序停止工作] B[解析器开始运行,生成请求] C[Socket库根据请求生成命令] D[协议栈发送UDP请求] E[网卡发送] F[DNS返回IP] G[协议栈接收UPD请求] H[Socket库返回IP] I[解析器读取IP,返回应用程序] J[应用程序继续工作] A-->B-->C-->D-->E-->F-->G-->H-->I-->J
1×05 DNS的基本工作原理
| www.lab.yxwa.com | student@yxwa.com |
| (a)域名=www.lab.xywa.com (b)Class=IN (c)记录类型=A(Adress) | (a)域名=yxwa.com (b)Class=IN (c)记录类型=MX(Mail eXchange) |
| 记录类型 | 作用 |
| A | 根据域名查询IP |
| MX | 邮箱服务器 |
| CNAME | 查询域名相关别名 |
| NS | 查询DNS服务器IP地址 |
| SOA | 查询域名属性信息 |
| PTR | 根据IP地址反查域名 |
| ……. | …… |
1×06 域名的层次结构
假设公司的域为
yxwa.co.cn
可以创建两个子域
lab1.yxwa.co.cn
lab2.yxwa.co.cn
1×07 DNS通过层次来寻找IP地址
假设我要寻找www.lab.yxwa.com则会分成以下步骤:
寻找com域
↓
寻找yxwa.com域
↓
寻找lab.yxwa.com域
↓
寻找www.lab.yxwa.com域
↓
找到IP地址
1×08 数据收发操作——套接字、协议栈和Socket库
客户端向服务端发送信息的时候,需要根据流程来创建一个能够进行数据传输的“管道”。就像移动硬盘和电脑之间,需要一条数据传输线来连接,两头的USB接口或者Type-C接口就是“套接字”
收发数据的操作可以概括成如下:
(1)创建套接字
(2)将管道连接到服务端的套接字上
(3)收发数据
(4)断开管道并删除套接字
(1)创建套接字
客户端只需要调用Socket库当中的socket程序组件即可。在完成套接字创建之后,协议栈(操作系统)会返回一个描述符,应用程序会存放在cache(缓存区)中。
在多个网页应用程序同时需要传输的时候,他们同时生成的套接字都不相同,描述符就是辨别这些套接字的方法,协议栈也是通过描述符来判断套接字来连接或者收发数据。
graph TD
A[客户端调用Socket库的socket组件]
B[socket内部创建套接字]
C[协议栈返回描述符]
D[应用程序存储描述符]
A --> B --> C --> D(2)连接
应用程序调用Socket库当中的connect程序组件来完成这一操作,需要三个参数:
- 描述符
- 服务器IP地址
- 端口号
当连接成功后,协议栈会将对方的IP地址和端口号等信息保存在套接字中,这样就可以开始收发数据
(3)传递信息
应用程序通过Socket库调用write程序组件来实现。协议栈在中间扮演着重要的角色,它会把数据发送到服务器才能让后者执行接收操作,同时后续需要协议栈接受信息后再发回给Socket库,这样Socket库才能调用read程序组件来把响应信息放到cache
graph TD A[应用程序准备好数据] B[Socket库调用write组件] C[确定描述符和发送数据] D[协议栈发送到服务器] E[服务器执行接收操作] F[服务器向客户端返回响应消息] G[应用程序通过Socket库调用read组件] H[在cache存放接收的响应信息] A --> B --> C -->D-->E-->F-->G-->H
(4)断开
应用程序通过Socket库调用close程序组件来实现。最终,这条“管道”会被删除的同时,套接字本身也会被删除。
在HTTP协议中,Web服务器发送完响应信息后,应该主动断开操作,因此Web服务器会首先调用close来断开连接。同时规定了,HTML文档和图片都作为单独的对象来处理,所以如果一个网页中包含多个图片,就要重复很多次。但是后续HTTP-1.1版本解决了这个问题
所以在Web服务器调用close来断开连接,把这个操作传达给客户端,客户端的套接字就会进入断开阶段。浏览器调用read之后,也会调用close进入断开阶段
graph TD A[Web服务器调用close执行断开操作] B[read组件告知浏览器收发数据操作已结束] C[浏览器调用close进入断开工作] A-->B-->C
第2章 协议栈和网卡
2×01 创建套接字
协议栈,这一部分里面包含了三个部分:TCP UDP IP
重点记住:
TCP用在发送浏览器、邮件等一般应用程序
UDP用在DNS查询
在协议栈内部,有一块内存空间用来存放控制信息,可以说这块空间就是套接字的实体。而协议栈会根据套接字中记录的控制信息来工作
Socket库委托协议栈的时候,协议栈会分配一个内存空间来记录套接字控制信息,然后写入初始状态,再把套接字的描述符告诉应用程序,方便后续应用程序收发数据的时候识别
2×02 连接服务器
创建了套接字之后,应用程序就会调用connect来连接。
为什么需要“连接“”?
因为初始状态的套接字里面没有任何东西,所以要给协议栈传输对象的IP和端口号,不然协议栈根本不知道该传给谁。服务器那边也需要客户端来告诉它:我的地址是xxx.xxx.xxx.xxx,端口号是yyyy才能让双方进行通信
在连接阶段,也就是数据收发开始之前,还存在控制信息。这些控制信息位于网络包的开头,因此也称为头部。为了区分,一般就会叫更熟悉的名字:TCP头部、MAC头部、IP头部
总之一句话:头部就是用来记录和交换控制信息
套接字中也有控制信息,用来控制协议栈操作的信息。但套接字中的控制信息和协议栈的程序本身其实是一体的,所以无论通信双方是什么系统,都可以互相通信。好比你的电脑和手机,这俩内部结构、协议栈、控制信息都不同,但是依然可以办到数据传输
所以,控制信息分为两类:
1、头部中记录的信息
2、套接字中记录的信息
connect格式:
connect(<描述符>,<服务器IP地址和端口号>,……)
上面的connect调用会把服务器的信息传递给协议栈中的TCP模块。TCP模块再和IP地址对应的服务器的TCP模块交换控制信息,这一交互包含这些步骤:
1、客户端创建包含表示开始数据收发操作的控制信息的头部并且把头部的控制位的SYN比特设置为1
2、TCP模块传递信息给IP模块并委托它发送发送网络包
3、网络包通过网络到达服务器
4、服务器IP模块接收到网络包并传里面的数据给TCP模块
5、TCP模套接字写入相应的信息,并将状态改为正在连接块根据TCP头部找到对应的套接字
6、TCP模块返回响应,在其头部设置发送方和接收方端口号以及SYN比特
7、将ACK控制位设为1,表示已经接受到相应的网络包
8、TCP模块传递信息给IP模块并委托它发送发送网络包
9、网络包通过网络到达客户端
10、客户端IP模块把网络包里的内容传给TCP模块
11、TCP头部信息确认连接是否成功,SYN为1则代表成功
12、客户端设置ACK比特为1并发送服务器
13、服务器收到返回包
Looks familiar?想想三次握手是怎么做的,这就是三次握手的细节
⬇或者换个直观对应的方式⬇
- 第一次握手(SYN):客户端发送一个SYN(同步序列编号)包给服务器,表示希望建立连接,并告诉服务器客户端的初始序列号。
- 第二次握手(SYN-ACK):服务器收到SYN包后,回复一个SYN-ACK包,确认收到客户端的请求,同时也发送自己的SYN请求,告诉客户端服务器的初始序列号。
- 第三次握手(ACK):客户端收到服务器的SYN-ACK包后,回复一个ACK包,确认收到服务器的响应。此时连接建立完成,双方可以开始数据传输。
1、客户端创建包含表示开始数据收发操作的控制信息的头部并且把头部的控制位的SYN比特设置为1
2、TCP模块传递信息给IP模块并委托它发送发送网络包
3、网络包通过网络到达服务器
4、服务器IP模块接收到网络包并传里面的数据给TCP模块
5、TCP模套接字写入相应的信息,并将状态改为正在连接块根据TCP头部找到对应的套接字
6、TCP模块返回响应,在其头部设置发送方和接收方端口号以及SYN比特
7、将ACK控制位设为1,表示已经接受到相应的网络包
8、TCP模块传递信息给IP模块并委托它发送发送网络包
9、网络包通过网络到达客户端
10、客户端IP模块把网络包里的内容传给TCP模块
11、TCP头部信息确认连接是否成功,SYN为1则代表成功
12、客户端设置ACK比特为1并发送服务器
13、服务器收到返回包
ACK号:后续收发数据要用到的关键状态码,它不只有1,而是会随着每个数据排头开始的序号而发生变化。举个例子:
30个字节的A数据包被分成3分
a数据块
b数据块
c数据块对应的序号
1
11
21会返回的ACK号
1
11
21
如果数据传输过程中被攻击了导致序号错误该咋搞?
那么就需要SYN控制位来解决这个问题了
SYN号:全名Synchronize(同步),说白了就是保持传输双方步调保持一致,以便后续的数据收发检查。这就是为啥三次握手需要ACK、SYN二者同时来运作,同时他俩也确保了三次握手的稳定
2×03 收发数据
发送数据的操作是应用程序调用write把要发送的数据交给协议栈开始的,协议栈里面有这些要点:
● 要存放:先把数据放在发送cache中,并等待应用程序的下一段数据
● 不关心:协议栈只知道要发送的是一定长度的二进制字节序列
为什么要存放
数据包有大有小,如果一次性给了多了,传输效率就下来了;所以需要放到 cache 后再判断积累多少数据才能发送
至于“要存放”,会涉及到两个判断点:
- MTU:一个网络包的最大长度,在MAC中一般是1500字节。MSS是MTU减去头部长度的最大数据长度。
- 时间:如果等的时间长了也不合适,因此就算数据长度没有达到MSS,也会直接发送出去。
但如果全部交给协议栈来判断的话,不免容易出岔子,所以应用程序还是会留后手,自己来指定一些选项,比如指定“不等待填满缓冲区直接发送”,协议栈就会直接发送数据。
对于较大的数据,在传给发送cache之前会被TCP拆分成一个个以MSS长度为单位的数据块,并且还会给每个数据块的前面加上TCP头部,再根据控制信息标记收发双方的端口号,然后交给IP模块。

2×04 滑动窗口与接收缓冲区
滑动窗口和接收缓冲区结合在一起是有效管理ACK号的一种方式,说白了就是发送方A先发一段数据给接收方B,B收到之后会告诉A缓冲区有多大,然后A就会一遍把分段数据填满B的缓冲区,B也会一边取走一部分数据之后会通过TCP头部告诉A可以接着发。
graph TD A[A发一段数据] B[B收到数据开始处理] C[B告诉A还能发多少] D[A把分段数据填满B的缓冲区] E[B取走一部分] F[B告诉A接着传] G[A接着传] A-->B A-->C B-->D C-->D D-->E D-->F E-->G F-->G
2×05 从服务器断开并删除套接字
在应用程序判断所有数据已经发送完了之后。这时,数据发送完毕的一方会发起断开过程,但不同的应用程序会选择不同的断开时机。
网络是怎样连接的——户根勤
说人话:在HTTP1.0中,一般都是让服务器来发起断开请求,也有让客户端来发起的。但不管咋样,这里面牵扯到的细节就是另一个熟悉的流程:四次挥手。
直接上直白对照流程(可能有点长)
- 第一次挥手:假设服务器主动关闭连接,发送一个 FIN 报文段,FIN 标志位设为 1,序列号为服务器已发送数据的最后一个字节序号 + 1。服务器进入 FIN_WAIT_1 状态,表明不再发送数据,但仍可接收数据
- 第二次挥手:客户端收到 FIN 报文后,返回一个 ACK 报文段,确认号为服务器 FIN 的序列号 + 1,表明已收到关闭请求。客户端进入 CLOSE_WAIT 状态,服务器收到 ACK 后进入 FIN_WAIT_2 状态,等待客户端的关闭通知
- 第三次挥手:客户端完成剩余数据传输后,向服务器发送 FIN 报文段,FIN 标志位设为 1,序列号为客户端已发送数据的最后一个字节序号 + 1。服务器进入 LAST_ACK 状态,等待服务器确认
- 第四次挥手:服务器收到 FIN 报文后,返回 ACK 报文段,确认号为客户端 FIN 的序列号 + 1,并进入 TIME_WAIT 状态。客户端收到 ACK 后进入 CLOSED 状态;服务器等待一段时间(通常为 2MSL)后,也进入 CLOSED 状态,连接彻底释放
- 服务器的协议栈生成将控制位FIN比特设为1的包含断开信息的TCP头部
- 协议栈委托IP模块向客户端发送数据的同时,服务器的套接字也会记录断开操作的相关信息
- 客户端收到TCP头部时,自己的协议栈会把其套接字标记为进入断开操作状态
- 告知服务器已收到FIN为1的包
- 向服务器返回一个ACK号
- 协议栈等待应用程序来取数据
- 应用程序读取数据的时候,会被协议栈告知来自服务器的数据全部收到了
- 应用程序调用close,委托协议栈生成一个FIN比特为1的TCP包,再委托IP模块发送给服务器
- 服务器再次返回ACK号,通信彻底结束
但随着时代发展,现在HTTP/3是主流,这些以前老旧的版本已经淘汰,所以可能现在四次握手更加细节。右边只是以更加直观的“物理层面”来阐述“挥手”的流程,原文还提到一个特别逗比的例子,可以细看:
如果最后客户端返回的 ACK 号丢失了,结果会如何呢?这时,服务
网络是怎样连接的——户根勤
器没有接收到 ACK 号,可能会重发一次 FIN。如果这时客户端的套接字已
经删除了,会发生什么事呢?套接字被删除,那么套接字中保存的控制信
息也就跟着消失了,套接字对应的端口号就会被释放出来。这时,如果别
的应用程序要创建套接字,新套接字碰巧又被分配了同一个端口号 B,而服
务器重发的 FIN 正好到达,会怎么样呢?本来这个 FIN 是要发给刚刚删除的那个套接字的,但新套接字具有相同的端口号,于是这个 FIN 就会错误
地跑到新套接字里面,新套接字就开始执行断开操作了。之所以不马上删
除套接字,就是为了防止这样的误操作

2×06 IP与以太网的包 收发操作
什么是“包”?
直白点:头部+数据两部分=包
包就是数据封装打包的东西,总共由头部和数据两部分组成
头部:包含目的地址等信息
数据:就是数据
- IP包: IP头部 TCP头部 数据块
- 以太网包: MAC头部 IP头部 TCP头部 数据块
路由器用IP协议 集线器用MAC协议
| IP协议 | MAC协议 |
|---|---|
| IP头部 | MAC头部 |
| 路由器 | 集线器 |
传输的过程很简单,IP头部(目标地址)是不变的,改变的只有MAC头部。因为他需要不断地变化来转发到下一个路由器。

网卡是个好东西(我倒是觉得这玩意儿容易削网速),一般情况每一台电脑都会有一台网卡,这也说明也只有一个IP地址,但如果有个多个网卡,也就意味着有多个IP地址。所以也可以理解成:
网卡就是IP的物理载体
在发送数据的时候,发送方IP地址需要确定发送要用的网卡,并填写这个网卡的IP地址。
那该怎么判断把包交给哪个网卡?

这就要用到一个表格——路由表(Routing Table)。
首先看第一栏——Network Destination:
在TCP模块说明目标IP地址的时候,套接字记录的目的IP地址就会和路由表左侧的Network Destination栏比较。
TCP模块告知的目标IP地址为192.168.1.144,只需要对应Network Destination栏中的192.168.1.0
TCP模块告知的目标IP地址为10.10.1.144,只需要对应Network Destination栏中的10.10.1.0
第二栏就是对应子网
第三栏:Gateway(网关)
Gateway(网关)在 TCP/IP 的世界里就是路由器的意思,他的作用就是表示下一个路由器的IP地址,并将包发给这个IP地址。
第四栏:Interface栏——表示网卡等网络接口,通过这些接口可以把包发给通信对象
网络是怎样连接的 户根勤
如果 Gateway 和 Interface 列的 IP 地址相同,就表示不需要路由器进行转 发,可以直接将包发给接收方的 IP 地址
2×07 MAC头部
TCP/IP和MAC这两者之间的协议是无法互用的
IP模块在生成IP头部之后,会在前头再加一个MAC头部。MAC头部的开头就是数据传输双方的MAC地址,功能也和IP头部类似,但是MAC地址的长度是要比IP的要长

IP地址更多类似于现实当中xx市xx区xx大道xx街道xx路这样的层次化结构,而MAC地址当中的48比特是一个整体。
生成MAC头部时,只需要设置好表2.3当中的3个字段、填好以太类型中的协议,填写网卡本身的MAC地址即可。但是在接收方MAC地址就比较复杂。IP头部只需要查找类似图2.18作参考就好了,而MAC头部考虑的就多了(gcd鸣式),因为表格当中根本没有可以用作参考下一个MAC地址的地方。这时候,就需要请另一个老熟人——ARP(Address Resolution Protocol),地址解析协议
2×08 ARP
ARP是怎么找到MAC地址的呢?
首先,ARP会像虎哥那样举个大喇叭:全体目光向我看齐,我宣布个事儿!我是个……(划掉)xxx.xxx.xxx.xxx这个IP地址是谁家的? 只要对方处于同一子网,就可以直接把对方MAC地址写入MAC头部就行。
同样的,ARP也是有缓存的,如果以前双方通过信,那么他们的MAC地址都会存在对方的ARP缓存中,不用再扯个嗓子喊了。
那怎么看到自己的IP地址和对应的MAC地址呢?Windows系统打开cmd输入arp -a就能查看了

2×09 以太网
原文说得相当冗长,推荐直接看图,很容易理解

2×10 网卡
网卡的内部构造现如今都已经大差不差,内置的驱动程序和相关的数据收发操作都和厂商型号的不同而不同。而”全世界唯一的MAC地址“就是存放在网卡当中的ROM中,MAC地址会被网卡驱动程序读取并分配给MAC模块

2×11 集线器
发送网络包
发送网络包的时候会接触到两个发送方式
- 集线器——半双工模式
该模式运作之前需要判断网线是否存在其他信号,如果有,则需要等待该信号传输完毕,不然两个信号之间会发生碰撞。在上一个信号传输结束之后,就可以进行传输了。具体传输的方式会牵扯到另一专业的知识——数字信号传输- 具体传输方式
- MAC模块从报头开始把所有数字信号转化成电信号,然后由PHY或者MAU的信号收发模块发送出去。其中数字信号转化电信号的速率就是俗称的“网速”——xx Mbit/s
- PHY(MAU)不仅仅负责转化信号,还需监控接收线路中有无信号进来。如果信号开始发送到结束发送这段时间内,没有其他信号进来,则判断发送操作成功。 注:以太网不会确认发送的信号对方有没有收到
- 当然即便概率只有几万分之一,说不定还是会有信号碰撞,这时候就需要发送一段时间的阻塞信号来通知其他设备,届时所有的发送操作会全部停止 这里的等待时间是根据MAC地址生成一个随机数计算出来的,毕竟不可能预测到每台设备会在什么时候会发送数据,如果还能碰撞个十回八回,那就报告通信错误
- 关于PHY(MAU)模块:简单的说这就是一个物理层装置,在网卡这一物理层中负责收发信号
- 具体传输方式
- 交换机——全双工模式
具体在第三章解释
网卡的MAC模块生成通用信号,然后由PHY(MAU)模块转换成可在网线中传输的格式,通过网线发送出去
接收返回包
以太网的接收操作和发送大差不差,和设备、TCP以及应用程序的种类都无关。这里只需要关注接收操作会不管是啥信号都会拿过来看两眼判断一下再存起来
- 具体操作
- 接收信号之后,都会先在PHY(MAU)模块转化成通用格式,再用MAC模块转化成数字信号
- FCS也是需要检查的项目,而它被安排在数字信号的结尾处当作参照物。从包开头到末尾的所有比特会套用一个公式换算成FCS,如果和数据包结尾自带的FCS不同的话,就会被当做错误包丢弃
- FCS检验没问题后就会检查MAC地址是否一致,不是传给我的包,直接不要
(哈哈,那厉害了);如果一致的话,就会放进缓存中
关于“中断”操作,这与网卡相挂钩。说白了就是计算机不会一直盯着网卡工作,当网卡结束工作后,会把中断号和对应的网卡驱动绑定。当网卡发起“中断”,计算机就会在CPU把其他任务挂起,从网卡的缓冲区取出收到的数据包,并在MAC头部判断协议类型。
2×12 UDP
相比较TCP而言,UDP显得暴力又直白。TCP需要确保“完整性”,他需要反复确认发送和接收的进度,如果中途漏掉一个东西又要续上确保数据完整,这就导致他十分复杂。而UDP就可以做到:
将很短的数据包直接装成一个包发送,就算中间有遗漏,可以再补发一个完整的,既快又快,只要确认收货即可
那么什么时候可以用到UDP呢?
- 音频
- 视频
第3章——从网线到网络设备
转发设备在传输数据包的时候不会检查数据包其中的内容,只会根据设备内部的转发规则表来判断数据包的目的地进行转发。所以:
HTTP请求的方法,TCP的确认相应和序号,客户端和服务器之间的关系都与包的传输无关,因此所有的包在传输到目的地的过程中都是独立的。
3×01 信号衰减和集线器
前面提到,以太网的信号传输本质是正负变化的电压,所以网卡当中的PHY模块是负责把数据包转换成电信号让传输线明白里面的内容
离路由器越远,WiFi信号越弱。同理,网线越长,信号衰减也就会越严重。
集线器
集线器这个物理设备,只需要记住:
集线器将信号发送给所有连接在它上面的线路
3×02 交换机
交换机其中的PHY模块和集线器是相同的,二者都是MDI-X模式进行连接交换。同时,交换机的每个网络接口后面都是类似网卡的运作模式,网线接口和后面的电路部分加在一起称为一个端口,就可以理解成一块没有MAC地址的网卡
它只会接收所有的数据包并放在缓存区,而且端口的MAC模块不具有MAC地址
MDI-X模式
指的是“Medium Dependent Interface Crossover”模式,是以太网接口中的一种自动交叉功能。MDI-X接口可以自动识别连接的线缆类型,并自动调整发送和接收信号的线对,从而实现直接连接,无需使用交叉线。这样大大简化了网络布线,避免了线缆选错的麻烦
——————————————————————————————————————————————
但现在基本大部分网络连接设备都具备自动选择合适的模式来进行连接,所以对于这一部分理解即可
查询
收到数据包之后,需要对接收方的MAC地址进行查询:该MAC地址是否已经在MAC地址表中已有记录
对于交换机内部工作原理,建议看原作P151的交换机工作流程
特殊操作

其中一种情况——看图说话:说白了就是A给B和集线器都发了东西,但是集线器又将数据包转给了交换机;交换机在转发完包之后,会原路返回给B,导致B收到了两份东西
流程图如下:
graph LR A[计算机A] B[计算机B] C[集线器] D[交换机] A --数据包a、b--> C C --A传出去的b--> D C --A传给B的a--> B D --返回来的b--> C C --返回来的b--> B
注:这里面的数据包a=b,为了方便理解,故作分开
所以这时候交换机发现相同的包还要返回到相同的端口,就会直接丢弃这个包
另一种情况就是地址表找不到指定的MAC地址
这里的解决办法相当暴力:只需要向除了源端口之外的全部端口发一个数据包,只要有了回应,那么他就直接会记录接收者的MAC地址
3×03 全双工模式
全双工,顾名思义就是可以同时发送和接收。在目前市面上基本都是在用交换机的全双工模式,因为它同时兼容了效率、稳定的作用。只需要记住:
全双工可以在无论网络中有没有信号都可以发送信号的工作模式,同时规定停用信号碰撞检测,这样就可以无需等待其他信号结束即可发送信号
3×04 自动协商
实现最大收益和最大效率一直是工作追求的目标,因此在网络传输的过程中研发出了在全半双工中切换的同时,还能探测对方的传输速率并进行自动切换,而这就被称为“自动协商”
在数据线中没有数据包流动的时候,以太网会在网线里面填充一个被称为“连接脉冲”的脉冲信号。这一脉冲信号的主要作用就是检测双方是否在正常连接,这就是为什么能看到不少网络连接设备有绿/红色指示灯,这是为了更直观的显示目前的连接状态。同时现在连接线内部的特定排列的脉冲信号可以让双方得知互相的状态。这样就可以选择最优的工作模式和传输速率。
还有一点:交换机是可以同时执行多个转发操作的,而集线器无法做到这一点;后者只能将输入的信号广播到所有端口,
3×05 路由器
路由器内部有两个重要的模块:转发模块和端口模块
- 转发模块:负责判断包的转发目的地
- 端口模块:负责包的收发操作。具有多种通信技术和通信协议,可以接收包括但不限于ADSL、FTTH,以及各种宽带专线等,只需要硬件支持即可
工作方式也很简单:端口负责收发,转发负责查询

同时路由器的各个端口都具有MAC地址和IP地址
3×06 路由表
这一部分原作讲得及其详细,需要结合自身经验、原作或者大模型辅助来理解
虽然与交换机的大体工作思路是一致的,但是具体细节却有所不同。
交换机通过MAC头部中的接收方MAC地址来判断转发目标
路由器通过IP头部中的IP地址来判断转发目标对于路由器而言,他不需要像交换机精确到每个地址,只需要有相应的记录即可。
因此路由器会忽略主机号的部分,只匹配到网络号部分。
这么讲很抽象,通过大模型举个例子:
假设有三个网络:
- 网络A:192.168.1.0/24
- 网络B:192.168.2.0/24
- 网络C:192.168.3.0/24
有两台路由器:
- Router1 连接网络A和网络B
- Router2 连接网络B和网络C
graph LR A[网络A 192.168.1.0/24] a[Router1] B[网络B 192.168.2.0/24] b[Router2] C[网络C 192.168.3.0/24] A-->a-->B-->b-->C
网络B就起到中间的“跳板”作用,那么两个路由器对于他就拥有两个IP:
- Router1 192.168.2.1
- Router2 192.168.2.2
假设:一台主机A在网络A(IP: 192.168.1.10)要访问网络C内的主机C(IP: 192.168.3.20)。
其中的过程就是
过程
- 主机A发送数据包,目的地址是192.168.3.20。
- 数据包到达Router1。
- Router1检查路由表,发现目标192.168..0/24的下一跳是192.168.2.2(Router2)。
- Router1将数据包转发到接口连接网络B,更新数据链路层的MAC地址为Router2的接口MAC。
- 数据包到达Router2。
- Router2检查路由表,发现192.168.3.0/24是直连网络,直接转发到接口连接网络C。
- 数据包最终到达主机C。
graph LR A[主机A] B[Router1 检查路由表发现目标下一跳是192.168.2.2] C[网络B] D[Router2 发现和192.168.3.0/24是直连] E[网络C] F[主机C] A--数据包目的地:192.168.3.20-->B--传给网络B-->C-->D-->E--确定具体IP地址-->F
从上面的举例可以看出:
路由器的端口都具有MAC地址,同时还只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃
具体查询路由表的流程如下:

- 根据包的接收方IP地址查询路由表中的地址栏,找到相匹配的记录
- 注意:路由表中的子网和实际IP的子网并非完全一致,因此是需要根据子网掩码列中的值判断网络号的比特数,并匹配相应数量的比特
- 最长匹配原则:路由器首先寻找网络号比特数最长的一条记录。
网络号比特数越长→说明主机号比特数越短→意味着该子网内可分配的主机数量越少→子网中可能存在的主机数量越少
- 网络号长度相同的记录过多,会根据跃点计数的值来进行判断
- 跃点计数越小说明该路由越近
- 路由器无法找到匹配的记录,路由器会丢弃这个包
- 处理方式和交换机不同,原因在于网络规模的大小——毕竟路由器面对的是全球互联网,而交换机绝大部分情况只需要面对大中小型公司产业
- 找不到匹配路由时选择默认路由——子网掩码0.0.0.0
- 0.0.0.0子网掩码的意思是网络包接收方IP地址和路由表目标地址的匹配中需要匹配的比特数为0
- 意思就是根本不需要匹配,只要子网掩码设置为0.0.0.0,任何地址都能匹配到这一条记录
- 0.0.0.0子网掩码的意思是网络包接收方IP地址和路由表目标地址的匹配中需要匹配的比特数为0
3×06 包的有效期
在路由器查询路由表找到匹配目标,将包转交给输出端口并最终发送出去之前,路由器还需要做一些工作:
- 更新IP头部的TTL(Time to Live 生存时间)字段。在包经过每一个路由器的时候,TTL字段都会减1,当这个值变成0,就表示超过了有效期,最终会被丢弃
- 这一机制主要是确保防止一个包在陷入死循环导致数据传输造成损失
- 分片功能拆分大网络包
- 当一个数据包长度远大于输入端口的时候,就需要IP协议中定义的分片功能对包进行拆分。
- 注:这里分片机制和TCP对数据的拆分完全不同。
TCP是切了片再送出去 IP是拿回来一整块再切
- 注:这里分片机制和TCP对数据的拆分完全不同。
- 当一个数据包长度远大于输入端口的时候,就需要IP协议中定义的分片功能对包进行拆分。
3×07 路由器和交换机
IP(路由器)负责将包送达通信对象这一整体的过程,而其中将包传输给下一个路由器的过程则是由以太网(交换机)来负责的
举个例子,X在A平台上从Y买了一份商品B,B和快递单C需要从Y寄到X的手里。
从例子出发,A平台就是IP(路由器)需要负责XY二者之间商品包裹传输的过程;中间几经流转的快递中转站就是一个个以太网(交换机)
3×08 路由器的附件功能——地址转换
对于地址转换,上一张图来的最直观:

服务器对望有一个外网IP,对内有一个内网IP。这就是地址转换的一个直观效果。这其中的基本原理就是利用端口号进行转换,再将改写后的端口号和IP地址、改写前的端口号和IP地址一起收进一个表格方便对应。
3×09 改写端口号
端口号是一个16比特的数值,所以总共可以分配65536个端口。因此,公有地址加上端口的组合对应一个私有地址,这样一个公有地址可以对应几万个私有地址,极大地提升公有地址的利用率
3×10 从互联网访问内网
日常生活中,公司和学校都有相应的内网。在家里,就需要挂上VPN(虚拟网络)才能连接学校的教务在线肯定每个人都经历过,这是另外的原理。
而在这里介绍的是“古法访问”。假设路人甲没有能连进教务在线的VPN或者任意代理工具,那这时候路人甲只能让学校信息中心的员工给他在学校外网访问用的服务器中的地址转换设备分配一个公有地址,或者将服务器的私有地址手动添加到地址转换设备中,这样路人甲就能上教务在线抢课了
第4章 接入网和网络运营商
这一篇章原书的内容可能严重过时,内容所涉及的大部分术语和设备都已经被淘汰,所以作为了解网络连接设备变更的历史是可行的
4×01 ADSL(Asymmetric Digital Subscriber Line)接入网
ADSL——不对称数字用户线

目前大部分城市已经换成光纤(FTTH),ADSL基本退网了,但它在历史上非常重要,是让普通家庭第一次用上“宽带”的关键技术,所以还是有必要了解一下这门技术的作用
- 把普通电话线变成宽带线
不需要重新拉光纤或网线,直接用家里已有的电话线就能上网,极大降低了宽带普及成本。 - 实现“边打电话边上网” 传统56K拨号上网时,电话线被完全占用,打电话会断网。
- ADSL 使用频分复用技术:
0~4kHz → 传统电话语音
更高频率(几十kHz~1MHz左右) → 数据上网 所以上网和打电话互不影响。 - 提供比拨号快几十倍的网速(当时革命性提升)
早期常见速率:
下行(下载):512kbps~8Mbps(甚至更高版本到24Mbps)
上行(上传):128kbps~1Mbps 比56K拨号(最高56kbps)快非常多。 - 最主要的用途
家庭宽带上网
小型企业/网吧接入互联网
在光纤普及之前(2000年代~2010年代中期),是中国、日本、欧洲等地最主流的宽带方式
4×02 光纤接入网(FTTH)
光纤相比较ADSL,组成结构更加简单明了:只需要光信号来表示——亮表示1,暗表示0。同时,根据光纤内的光电二极管将光的亮暗转化成电信号。
4×03 光纤分类——单模与多模
牵扯到的大物知识用一段话来概括就是:
多模光纤因为是多条反射角不同的光线同时传到,其中反射角越大,光线反射次数就越多,走过的距离也就越长;
相对地,反射角越小,光线走过的距离越短。但是通过的距离越长就会导致到达接收端的时间越长。
结果就是多条光线到达的时间不同,信号的宽度被拉伸,就会导致失真。
因此,光纤越长,失真越大,当失真程度超过允许范围,通信就会出错。
—————————————————–分隔线——————————————————–
单模光纤因为只有一条光纤,因此不会产生时间差。
所以,从使用场景出发,多模光纤一般用在一座办公楼里面;单模光纤用在距离较远的建筑物之间的连接。
4×04 拨号上网
在二三十年前的电话拨号上网的年代,就需要用户:
- 用户向运营商的接入点拨打电话
- 用户输入自己的用户名与密码
- 用户名和密码通过RADIUS协议从RAS发送的认证服务器
RAS是Remote Access Server,不是(Raise A Suilen) - 认证服务器校验信息
- 认证服务器返回IP地址等配置信息给用户

graph TD A[用户拨打电话] B[用户输入账户密码] C[账户密码认证] D[认证服务器校验并返回配置信息] A-->B-->C-->D
4×05 PPPoE——在以太网上传输PPP消息
PPPoE:Point-to-Point Protocol over Ethernet,以太网的点对点协议
PPP:Point-to-Point Protocol,点对点协议 不是ppp(Poppin’party)
拨号上网的PPP是无法直接用于FTTH,所以才诞生了专门用于现代以太网的PPPoE,在理解PPPoE前,先了解PPP协议的传输方式
传输PPP消息的思路和IP包封装到以太网包中传输是一样的。但PPP协议中没有定义以太网中的报头和FCS认证等元素,也没有定义信号的格式,所以将PPP消息转化成信号来传输很不现实。要传输PPP消息,就需要一个类似报头、FCS认证和信号格式转换等元素的“容器”来收容PPP消息。于是,HDLC(High-level Data Link Control,高级数据联接控制)就派上用场了。HDLC协议就是这个容器,同时HDLC又是专门为了专线中传输数据包而设计的,所以PPP+HDLC的组合,变相地完成了现在的以太网数据包传输。但是,新的问题又来了,FTTH不能兼容HDLC,所以就需要一个新的机制来替代。这时候,PPPoE就诞生了
从有点牵强的角度来说:PPP+HDLC+兼容插件=PPPoE
除此之外,还有一个叫PPPoA的方式。
4×06 隧道传输网络包
BAS:Broadband Access Server,宽带接入服务器。它也是一种路由器。
隧道看起来好像又是一种新的连接方式,其实本质上来说,也是一种TCP连接。隧道也是需要两边都有路由器来充当收发双方的,这个跟TCP连接本质并无区别。所以无论任何机制,只要能把包原封不动地从一边传输到另外一边,从原理上来看都可以用来建立隧道,也可以看成一个隧道。
4×07 接入网
- 在用户接入互联网之前,需要先和BAS进行通信,这中间就会用到PPPoE的发现机制来查询BAS的MAC地址。
- 之后BAS下发的TCP/IP参数会被配置到互联网接入路由器的BAS端的端口上,这样路由器就完成了接入互联网的准备。
- BAS会在收到用户路由器发送的网络包之后,去掉MAC头部和PPPoE头部
- BAS用隧道机制将包发送给网络运营商的路由器
graph TD A[用户和BAS通信] B[BAS下发配置参数提供用户] C[BAS去掉用户网络包中的部分信息] D[隧道机制发送网络包] A-->B-->C-->D
4×08 网络运营商内部——POP和NOC
POP:Point of Presense,中文一般叫作“接入点”。
网络包通过接入网之后,到达运营商POP的路由器。互联网的实体并不是一个组织单独运营管理的单一网络,而是由多个运营商网络相互连接组成的。FTTH、ADSL等接入网是与用户签约的运营商设备相连的,这些设备就被成为POP,互联网的入口就在这里网络包通过接入网之后,到达运营商 POP 的路由器
PPPoE中的身份认证和配置下发操作由接入服务商的BAS来负责,运营商的路由器只负责对包进行转发,因此这里也是使用一般的路由器就可以了。
NOC:Network Operation Center,网络运行中心。
NOC是运营商的核心设备,从POP传来的网络包都会集中在这里然后从这里被转发到离目的地更近的POP,或者转发到其他的运营商,这里也需要配备高性能的路由器。
4×09 IX
IX:Internet eXchange,中文一般叫作“互联网交换中心”。
运营商在IX中就相当于一个具有大量高速以太网端口的二层交换机,用来传输接收过来的网络包。整体的工作原理也和前面提到的交换机没有区别。ARP查询下一个路由器的MAC地址,然后写入MAC地址发出。
第5章 防火墙
防火墙的工作方式大方向来讲很简单——过滤。允许指定的应用程序网络包通过,但是屏蔽其他不允许通过的网络包。屏蔽过滤的方法有很多种,可以大致分为包过滤、应用层网关、电路层网关等几种方式。
5×01 端口号限定
假设一个web服务器的端口号为80(相当常见),那只需要在防火墙的端口号过滤当中添加80端口即可
5×02 控制位判断连接方向
TCP协议是双向收发网络包,因为三次握手和四次挥手导致网络包是需要双向传输的,因此控制位的判断在这一方面显得举足轻重。前面酱过三次握手中,TCP控制位相当于网络包的一个“身份证”,当防火墙在给这个“身份证”上再添加一个护照,这样来回传输的网络包就可以进出防火墙了。
5×03 内网访问公开区域
防止内网被外网渗透的过程中,防火墙就起到一个相当重要的作用。不仅要设置互联网和公开区域之间的
包过滤规则,还需要设置公司内网和互联网之间,或者公司内网与公开区域之间的包过滤规则。这时,需要注意的是不要让这些规则互相干扰。
那么,如何做到外网进不来而内网出得去呢?
这就请到一个老朋友——地址转换
5×04 通过防火墙
在网络包通过防火墙的时候,是会被防火墙内部记录下来的。这也是运维的一个重点:查看防火墙日志,然后取证。
第6章 代理
使用clash verge或者其他同类型的VPN的时候多多少少都见过这样的字眼:系统代理。

在这之前,先明确代理是什么意思。代理(proxy)的本意并不是“转发”,而是先把消息收集后保存,然后“伪装”成原始客户端向Web服务器发出访问请求
6×01 正向代理
正向代理本质是缓存服务器使用的代理机制,目的之一是缓存。而缓存服务器——一台通过代理机制对数据进行缓存的服务器,其实说白了缓存服务器就是个“中间商”,但不赚差价。缓存服务器在Web服务器和客户端之间中转的时候,会把Web服务器的数据暂时先保存在自己的磁盘里,然后再发给客户端。所以,对于客户端来说,代理服务器就是Web服务器。
回到正向代理,正向代理除了可以做到缓存,还有一个重要作用——防火墙。
正向代理应用一套独属于自己的过滤系统,他可以现接收所有要访问的数据包,然后查看,筛查,根据内容判断是否允许访问。
相当于公司内网用了这套正向代理,员工就不好上外网摸鱼了(雾)


正向代理的使用方式是一般需要在浏览器里面执行。在浏览器的设置窗口中的“代理服务器”一栏中填写正向代理的IP地址。浏览器在需要传输数据的时候,正向代理会先忽略网址栏的内容,将数据存储起来之后根据URI直接向目标网址转发
6×02 反向代理——正向代理升级版
正向代理当中,需要在浏览器设置是一大痛点,只要输错一个字符或者浏览器崩溃,都会让正向代理失效。那么,有没有已经将URI中的目录名和Web服务器关联起来,然后还方便部署在服务端的代理方式呢?有的,这就是反向代理
6×03 重定向服务器
重定向——状态码3xx——的意思就是“需要访问的数据在另一台服务器上,请访问另一台服务器”,这个方法可以将访问目标分配到最近的缓存服务器。
当触发重定向的时候,首先需要将重定向服务器注册到Web服务器端的DNS服务器上。这样,客户端会将HTTP请求消息发送到重定向服务器上面。这个方法有利有弊,优点在于重定向是根据客户端发送来的HTTP消息的发送方IP来估算距离,因此精度比一般缓存服务器的DNS服务器精度较高。缺点就很明显了:HTTP消息的交互次数过多,开销也随之上升
6×04 缓存的更新方法
缓存过多会影响服务器的运行效率,但又不能盲目地清理,所以就需要一个合适的更新方法既可以去除没有必要的数据,也可以维持服务器运行的效率

其中一种方法——当Web服务器的原始数据发生更新时,立即通知缓存服务器更新,一直让缓存服务器保持最新状态
第7章 服务器
7×01 客户端和服务器
客户端很简单,笼统的说,所有可以发送请求的,都可以叫做客户端。
服务器就可以分为很多种,根据硬件和操作系统的区别又可以分为不同种类。但是跟网络相关的部分,网卡、协议栈、Socket库和客户端却并无二致,因为无论如何变化,底层的TCP/IP都是一样的。
7×02 服务器的套接字和端口号
服务器在网络传输的定位中处于被动的地方,只有接收到了请求才会向外面发出信息。所以,双方要有一个建立起联系的前后顺序。发起连接的一方是客户端,等待连接的一方是服务器。同时连接是不会存在的
二者在数据收发的顺序区别:
客户端
- 创建套接字(创建套接字阶段)
- 用管道连接服务器端的套接字(连接阶段)
- 收发数据(收发阶段)
- 断开管道并删除套接字(断开阶段)
服务器
- 创建套接字(创建套接字阶段)
- 将套接字设置为等待连接状态(等待连接阶段)
2.1接受连接(接受连接阶段) - 收发数据(收发阶段)
- 断开管道并删除套接字(断开阶段)
7×03 网卡的接收操作
在收到客户端的请求之后,
网卡的 MAC 模块将网络包从信号还原为数字信息,校验 FCS并存入缓冲区
这个过程中,CPU会暂停手中的工作,切换到网卡的任务中,这里也是和客户端网卡的运作方式一模一样。网卡驱动开始运行后会将接收到的包读取出来,之后就会
网卡驱动会根据 MAC 头部判断协议类型,并将包交给相应的协议栈
7×04 IP模块的接收操作
协议栈接收后,IP模块开始率先检查IP头部、接收方IP地址,看包是不是发给自己的。如果服务器启用了类似路由器的包转发功能时,对于不是发给自己的包,会像路由器一样根据路由表对包进行转发。
确认包是发给自己的之后,接下来需要检查IP头部内容看包有没有被分片。如果是分片的包,则将包暂时存放在内存中, 组合成完整的包再还原成原始数据包;如果没有分片,则直接保留接收时的样子。
graph LR A[判断是不是发给自己的] B[包是否经过切片] C[将包转交给TCP或UDP模块] A-->B-->C
7×05 TCP模块处理连接包
跟客户端也大差不差,先连接、后处理。当TCP头部中的控制位SYN为1时,表示这是一个发起连接的包,之后检查包的接收方端口号,确认有无相同并处于等待状态的套接字。没有的话就发送错误包。处理完之后,TCP模块就会执行接受连接的操作
如果存在等待连接的套接字,则为这个套接字生成一个副本,并将发送方IP地址、端口号、序号初始值、窗口大小等必要的参数写入这个套接字中,同时分配会用到的内存空间。然后生成代表接收确认的ACK号,把接收缓冲区剩余容量的窗口大小,并用这些信息生成TCP头部,委托IP模块发送给客户端
graph LR A[确认TCP头部的控制位SYN] B[检查接收方端口号] C[连接套接字复制一个新的副本] D[记录发送方IP地址和端口号等信息] A-->B-->C-->D
7×06 TCP模块处理数据包
graph LR A[根据传输的双方IP地址、端口号找到相对应的套接字] B[将数据块拼合起来并保存在接收缓冲区] C[向客户端返回ACK] A-->B-->C
7×07 TCP模块的断开操作
详见四次挥手……真的懒得写了
7×08 URI转换为实际的文件名
最简单的一个例子就是请求方法为GET,URI为HTML文件名。
这种情况下,只需要从文件当中读取HTML文档,然后作为响应信息返回即可。但是这么做就会直接将Web服务器的磁盘内容全部暴露。只就是文件包含漏洞的一个简单示例。所以需要一些特殊的保护机制:
URI中一般写的是虚拟目录结构下的路径名,所以在读取文件时需要先查询虚拟目录与实际目录的对应关系,并将URI转换成是实际的文件名后,才能读取文件并返回数据
7×09 CGI程序
CGI随着科技发展早就退出了网页渲染的舞台,了解即可
CGI程序(全称 Common Gateway Interface 程序,中文常译为“通用网关接口程序”)是Web发展早期(主要是1993–2005年左右)用来实现动态网页的最经典方式之一。
它本质上是一个运行在服务器端的可执行程序(或脚本),通过一个叫 CGI 的标准接口跟Web服务器(如Apache、Nginx、IIS等)通信,从而让静态的Web服务器能生成动态内容。
结语
呼~总算结束了!从2025.1.6开始写这篇随笔已经有了接近两个月的时间,当初为了读点东西就把这本书翻了两页,结果就发现虽然是个扫盲的简单专业书籍,但里面一些东西的底层逻辑还是可以理解一下,就当复习以前学的东西。中间想着放弃过,跑去写docker。但毕竟已经写一半,半途而废总觉得不太合适,毕竟是自己的博客,里面的东西至少还是要保持完整,就顶着复杂的心情继续往下写。
从第5章开始(还是第4章来着),至少个人能感觉出对于书中的观点我已经不想写太多废话,直接提炼出来,上图以及绘制流程图来得更加直观,方便。
说句不好听的,这本书个人看来还是蛮滞后的,里面一些术语和设备已经早被淘汰,现在只能从网上看到它们。但是不能否定它们在网络连接发展历史上的重要作用,毕竟从二十世纪发展到现在不是一蹴而就,它们的运作方式和底层逻辑还是相当值得学习。
废话也写不出那么多,那就下一篇读书记录再见!
2026-3-2 15:21:59




