计算机网络
Ch.1 互联网络
1.1 因特网
在一开始的时候,计算机是一个独立的机器,人们需要将信息输入到这台独立的机器中,然后机器经过计算,得到一个结果。随着技术的进步,人们开始尝试将多台计算机连接在一起,进而组成了计算机网络。
一开始,计算机只在一个较小的区域相互连接,这便构成了局域网。以太网(Ethernet)是一种实现局域网的技术,其通过网线将不同的计算机相互连接在一起。
随着需求的增加,全世界的计算机最终都相互连接在了一起,这便构成了因特网(Internet),也就是我们常说的互联网。互联网是网络之网络,是一个巨大的全球性网络系统。为了实现高效的互联网络通信,我们需要记住以下概念:
- TCP/IP协议:实现互联网中发送、接收信息的协议,也是计算机网络课程一般主要讨论的内容,我们会在后面详细讲解该内容。
- ISP (Internet Service Provider):互联网服务提供商,提供接入因特网的服务和通道。
我们来看一下TCP/IP和ISP是如何实现互联网的:

在上图中,我们注意到还有更多的术语,比如:
- 数据包(Packet):计算机发送的信息会被分割成小块,这些小块就是Packet,是网络传输的基本单位。
- 通信链路(Communication Link):实现网络传输的媒介,如光纤、无线电等。
- 路由器(Router):用来连接异构网络,也就是不同类型的网络,并实现最佳路径选择,将数据包从一个网络转发到另一个网络。
- 端系统/主机(End System/Host):位于网络边缘,是信息的消费者和生产者。手机、笔记本电脑、网站服务器等都是端系统。
1.2 TCP/IP协议
协议就是通信规则。上面已经提到,packet是计算机网络中传输的最小单位,其本身是由Bit组成的二进制数。在传输中,无论使用什么物理媒介,我们都只能传输二进制数,如果是光纤那么就将二进制数转换为光脉冲,如果是无线传输则转换为无线电频率或相位,如果是铜线则转换为对应形态的电压电流等。
显然,packet不是我们本来要传输的内容。比如说,你要发送消息给朋友,你不可能也不需要直接发送一堆二进制数,这些由计算机来完成。你要发送的信息被称为Message(报文),显然,不同的角色要发送的message本身也千差万别,比如网站要给你发送的是网页,你给朋友发送的是邮件,等等。具体的message内容取决于应用,因此message是应用层的内容。
TCP/IP协议是目前最通用的互联网协议,规定了上述应用层以及其他层的内容,具体内容包括:
- 应用层 (Application Layer): 您创建 报文 (Message) 。
- 传输层 (Transport Layer): 将 报文 切割成 段 (Segment) ,并添加端口号,确保数据能交给正确的应用程序。
- InternetLayer: 将 段 封装成 分组 (Packet) ,并添加 IP 地址,确保数据能到达正确的 主机 。
- Link Layer: 将 分组 封装成 帧 (Frame) ,并添加 MAC 地址,确保数据能到达 下一跳设备 。同时将 帧 转换为 二进制电信号 或光信号进行传输。
这里注意,经典教材自顶向下中将Internet层叫做Network层,将Link层分为Link层和Physical层来讲。由于现实中我们基本都在使用TCP/IP协议,因此在笔记中,我们默认按照TCP/IP的四层模型来讲。

上图形象地展示了TCP/IP协议的分层工作机制。
协议定义了在两个或多个通信实体之间交换的报文格式和次序,以及报文发送和/或接收一条报文或其他时间所采取的动作。
协议三大要素:
- 语法(Syntax):每一段内容符合一定规则的格式,比如一个报文前8位是原地址,后八个是目的地址(只是举例,不要当真)之类。
- 语义(Semantics):每一段内容需要代表某种意义,比如原地址部分的二进制到底是指哪个地址。
- 同步(Timing):通信的过程,即每一段任务的执行顺序。
1.3 局域网
我们已经知道了互联网大概长什么样子,那么接下来我们来了解以下终端是如何接入互联网的。
我们可以看到,网络中主要有两种设备,一种是提供服务和使用服务的终端,另一种是实现互联网传输的互联网基础设施。接入网(Access Network)是连接端系统(您的电脑、手机等)到互联网服务提供商(ISP) 第一台路由器的物理网络。它是用户进入互联网的 最后一段路 。它使得最终用户能够物理上连接到全球性的互联网。
实现接入网这一步骤的设备就是边缘路由器(Edge Router),你可以认为他是互联网的大门,充当着网络的入口点或 网关 ,负责将您的本地网络连接到更广阔的广域网(WAN)或互联网核心。它是接入网的 终点 ,也是 ISP 网络的 起点 。它将局域网 (LAN) 中的用户汇聚到广域网 (WAN) 当中。
现在,基本上所有的入口处都和一个局域网相连。在大部分家庭和学校,如今都使用局域网和WiFi来实现局域网内设备和边缘路由器的连接。
而如今大多数公司,已经习惯于将公司服务托管在云服务商的服务器中,比如AWS、GCP、阿里云等。这些云计算公司提供更好的分布式网络基础设施,让服务提供商无需花费精力在网络优化上。通过云服务商的网络虚拟化技术,每个客户都有一个逻辑上完全隔离的私有网络空间(VPC),这就好比是我们自己家里的局域网(不过实现技术不同),这个VPC会通过云服务商的边缘路由器和网关,来安全地与公共互联网进行数据交换。
网关(Gateway) 是两个不同网络之间的出入口。我们在上面已经提到,互联网和局域网通过边缘路由器连接,边缘路由器就是那个门。而网关正是一个个具体的门。换句话说,一个局域网内部的设备相互通讯时,无需经过网关;然而,当局域网内的设备需要和互联网中的内容交互时,他就必须通过网关。
对于普通用户,我们不需要关注网关如何设置,边缘路由器会自动配置。网关是一个逻辑上的出入口,表示方式是一个IP地址,可以将packet转发到目的地。网关也是一个设备地址,用语确定packet的下一跳转发目标。
综上所述,接入网、边缘路由器、网关是终端和互联网通讯的同一个东西的不同表达方式:
- 局域网:小区,负责本地范围内的本地通信,与互联网相互隔绝
- 接入网:小区内的私人车道,是本地网络通往互联网的物理通道
- 边缘路由器:小区大门,是执行路由和转发任务的硬件
- 网关:小区大门,包含默认网关IP(小区大门的地址牌)和路由功能(大门保安,会告诉你目的地IP的数据下一跳如何前往)
当我们传输一个message的时候,实际上就是经历了下面的过程:
- message会被切分为多个packet,然后分别封装在大包裹(Frame)里
- 终端把packet放到一个大包裹里,上面写的是默认网关的MAC地址,也就是小区大门的物理地址
- 终端把Frame发送给默认网关IP即边缘路由器,相当于开车到小区大门口
- 网关会把小区内部用的Frame扔掉,然后查看Packet的目的地,并创建一个新的Frame,包含下一跳路由器的MAC地址。相当于小区保安看了你的目的地后,告诉你下一站该往哪里走。
- 大包裹进入互联网,进入了公共道路(ISP)
我们可以看到,Frame在传输的时候,不会直接记录最终目的地,而是记录下一跳。每到下一个路由器那里,再改为packet中记录的最终目的地,一跳一跳来到达最终目的地网关。换句话说:
- packet会记录最终目的地IP地址,相当于收件人的家庭地址
- frame只记录下一跳地址
具体的路由算法和相关内容我们会在链路层讲解。
1.4 ISP
当大包裹(Frame)进入到互联网中后,大部分道路如今都是由ISP来提供并维护的。如今一些大型的网络服务提供商,比如Google,自己也在铺设道路如私有海底光缆,从而充当了ISP的角色。不过一般讨论ISP时,我们说的是传统的ISP,包括:
- Tier-1 ISP:处于互联网骨干网络的顶层,拥有全球性的超大型网络,是互联网的核心,主要公司包括AT&T, Verizon, NTT, Telia Carrier等。
- Tier-2 ISP:也叫做区域ISP,负责区域性或国家级的内容,负责将Tier-1的骨干网连接到更广泛的区域,并需要向骨干网络(Tier-1 ISP)付费,但是也可以向地方性ISP出售服务。
- Tier-3 ISP:也叫做接入ISP,主要是接入网提供商,主要直接向终端用户出售互联网接入服务,是最后一公里的建设者和维护者。
随着互联网的发展,如今三层级的ISP分类和公司职责相当模糊,比如中国的三大运营商同时提供Tier-1到Tier-3的ISP服务。
1.5 Delay
现在,我们已经知道一个mesaage在传输过程中需要经过以下步骤:
- 打包为大包裹
- 在互联网中传输
- 接受者收到后拆开大包裹
在这个过程中,每个步骤都需要花费时间,我们可以拆开来看:
- 发送方打包大包裹花费的时间,涉及处理delay
- 发送方把大包裹发送到边缘路由器的时间,涉及传输delay和传播delay
- 大包裹从发送方的边缘路由器到接收方的边缘路由器的时间,涉及处理、排队、传输、传播delay
- 接收方将大包裹从边缘路由器拿到终端设备的时间,涉及传输delay和传播delay
- 接收方打开大包裹的时间,涉及处理delay
其中排队时延(Queuing Delay)是packet在路由器缓存中等待被发送到通信链路的时间,这个是不固定的。其他的都是固定的:
- 传输时延(Transmission Delay):\(T_{\text{trans}} = \frac{L}{R}\),L是分组长度,R是传输速率(带宽)
- 传播时延(Propagation Delay):\(T_{\text{prop}} = \frac{d}{s}\),d是链路长度,s是信号传播速度
- 处理时延(Processing Delay),一般都是毫秒级很短的时间
总时延是指一个数据分组(Packet)从发送方主机被推入通信链路开始,到它所有比特到达接收方主机所经历的总时间:
其中影响最大的是传播时延和排队时延。传播时延是信号在物理介质中传播所需要的时间,排队时延则是网络中每一个经过的路由器中所排队等待的时间。
我们经常测网速所用的ping,其值基本就是:
而传输时延和处理时延通常都非常小。一般来说,稳定的高Ping值主要是由于目标主机很远导致的,即主要由传播时延贡献;而剧烈波动的Ping值,主要是由于网络拥堵,一般由排队时延贡献。
而很多网友在玩网络游戏时遇到的丢包(Packet Loss),基本都是在排队时发生的。路由器的每一个输出端口都有一个有限大小的 缓存或队列 。当到达该端口的分组速率持续超过链路的传输速率时,队列就会被填满。任何新到达的分组将无法进入队列,路由器只能选择 丢弃 (Drop)这个分组。当然,其他环节也有可能发生丢包,但是大多数都发生在排队时。
我们把流量强度记作:
其中L表示packet的长度;a表示packet的平均到达速率,也就是路由器接收packet的速度;R是链路带宽,代表路由器发送packet的能力。
\(L \times a\) 相当于:
所以整体公式就是:
当I接近0时,需求远小于能力,队列几乎总是空的,那么I就总是趋近于0。当I处于0到1之间时,即是正常负载,需求小于能力,但存在竞争,不过整体而言路由器可以处理所有流量。当I大于1时,需求超过能力,此时队列会超过路由器的容量上限,从而新的packet会被丢弃,导致丢包。
从终端A到终端B的传输吞吐量,取决于整个传输链路中的瓶颈链路(bottleneck link):
即整个端到端连接的吞吐量,取决于这条路径上最慢的那条链路的传输速率。这是计算机网络中最基本和最重要的原理之一,它说明了 整个系统的性能总是由其最弱的环节决定 。
Ch.2 应用层
2.1 Web
计算机网络的应用层是TCP/IP协议族中的 最高层 ,它直接面向用户应用,提供网络服务。应用层协议定义了 应用程序之间如何相互通信 ,以及 用户如何与网络交互 。它包含了大量支持各种用户应用程序的协议。
Web相关的服务日常使用最频繁的服务之一,用于浏览器和Web服务器之间的通信,其主要基于HTTP/HTTPS(超文本传输协议):
- HTTP (Hypertext Transfer Protocol) 是 Web 客户端(通常是浏览器)和 Web 服务器之间用于传输超文本文件(HTML 文件)、图片、视频等资源的标准协议。
- HTTPS 是在 HTTP 协议的基础上加入了安全套接字层/传输层安全协议 (SSL/TLS) 的加密功能。它的目的就是保障用户和服务器之间数据传输的 安全 、完整和 隐私 。
HTTP默认使用TCP 80端口,HTTPS默认使用TCP 443端口。
TCP 端口(Port) 是一个用于区分同一台计算机上不同应用程序或服务的 逻辑标识符 。想象一下,你有一栋大楼(这栋大楼就是你的 IP 地址 ),里面有很多不同的办公室(这些办公室就是你的 应用程序或服务 )。
- IP 地址 :用于找到正确的 大楼 (即网络上的某一台主机)。
- 端口号 :用于找到大楼里正确的 办公室 (即主机上的某个应用程序)。
TCP 端口号是一个 16 位的数字 ,范围从 0 到 65535 。当数据包(在传输层称为 报文段 )到达一台主机时,操作系统会查看该报文段的目标端口号,然后将数据准确地交付给正在监听该端口的应用程序。
常见的TCP周知端口(0-1023,由IANA永久分配给特定的网络服务)包括:
| 端口号 | 协议/服务 | 应用 |
|---|---|---|
| 21 | FTP | 文件传输协议(控制连接) |
| 22 | SSH | 安全外壳协议 |
| 23 | Telnet | 远程终端服务(不安全) |
| 25 | SMTP | 简单邮件传输协议 |
| 53 | DNS | 域名系统(TCP/UDP 均使用) |
| 80 | HTTP | 超文本传输协议(明文) |
| 443 | HTTPS | 安全超文本传输协议 |
一个 TCP 连接是由一个四元组唯一确定的:
例如,你的浏览器(客户端)访问一个网站(服务器):
- 服务器 :监听在
1.2.3.4:443 - 客户端 :临时分配一个动态端口,例如
192.168.1.100:50000
当数据到达服务器 1.2.3.4 时,操作系统发现目标端口是 443,就知道要把数据交给 Web 服务器程序处理;同时,服务器也能通过源端口号 50000 区分这是哪个客户端发来的数据。
2.2 Email
电子邮件服务依赖于三个主要的协作协议,以实现邮件的发送、接收和管理:
| 协议 | 端口 (TCP) | 作用 | 服务模式 |
|---|---|---|---|
| SMTP | 25 / 465 / 587 | 发送 :将邮件从发件人推送到收件人的邮件服务器。 | 推送(Push) |
| POP3 | 110 / 995 | 接收 :允许客户端从服务器下载邮件到本地。 | 离线管理 |
| IMAP | 143 / 993 | 接收/管理 :允许客户端在服务器上同步管理邮件。 | 在线同步 |
SMTP(Simple Mail Transfer Protocol)主要负责邮件的 投递 。当用户发送邮件时,邮件客户端将邮件推送给发件方邮件服务器,该服务器再通过 DNS 查找收件方服务器的地址,并使用 SMTP 将邮件转发过去。
用户接收邮件时,需要使用 POP3 或 IMAP 。POP3 倾向于将邮件下载到本地后删除服务器上的副本,实现离线操作;而 IMAP 则允许用户在服务器上保留邮件,方便多设备同步状态(如已读/未读、文件夹结构)。
2.3 FTP
FTP(File Transfer Protocol)是客户端和服务器之间传输文件的标准协议。
FTP 最大的特点是使用 两条独立的 TCP 连接 :
- 控制连接 (Control Connection) :使用 TCP 21 端口 。用于传输控制命令(如登录、改变目录、上传/下载请求)。这条连接是持久的,贯穿整个会话。
- 数据连接 (Data Connection) :用于实际传输文件数据。这条连接在每次文件传输完成后关闭。
数据连接的建立有两种模式:
- 主动模式 (Active FTP) :客户端发送端口号,服务器使用端口 20 主动连接客户端的指定端口。
- 被动模式 (Passive FTP) :客户端发送请求,服务器回复它打开的随机端口号,客户端主动连接该随机端口。被动模式更常见,因为它更容易穿越防火墙。
2.4 SSH
SSH(Secure Shell)是一种提供加密网络服务的协议,用于安全地访问远程计算机。SSH 的设计目标是取代 Telnet 和 FTP 等不安全的协议。它在通信过程中对所有数据进行 加密 (包括登录凭证和命令),确保了数据传输的机密性和完整性。
SSH 最常见的用途是:
- 远程命令行登录 :用户通过终端连接到远程服务器,安全地执行命令。
- 安全文件传输 :如 SFTP (SSH File Transfer Protocol),在 SSH 安全连接上进行文件传输。
- 端口转发/隧道 :将不安全的网络通信封装在 SSH 加密通道中进行传输。
2.5 DNS
DNS(Domain Name System)是互联网的“电话簿”,负责将人类可读的域名解析为 IP 地址。
DNS 不是集中式的,而是分布在全球数百万台服务器上,形成一个层次化的树形结构:
- 根域名服务器 (Root DNS Servers) :位于顶层,存储所有顶级域名的信息。
- 顶级域名服务器 (TLD DNS Servers) :负责
.com,.org,.cn等顶级域。 - 权威域名服务器 (Authoritative DNS Servers) :存储特定域名的 IP 地址记录(例如
google.com的 IP 地址)。
DNS 查询主要通过 UDP 53 端口进行,追求速度。当用户输入一个域名时,客户端会通过 本地 DNS 服务器 (ISP 提供)进行递归查询或 迭代查询 ,最终从权威服务器获取 IP 地址。 缓存机制在解析过程中起到了关键作用,可以大大加快后续查询速度。
2.6 P2P
P2P(Peer-to-Peer)模型与传统的客户端/服务器模型不同,它是一种分布式应用架构。在 P2P 架构中,通信的设备(节点或对等方)同时扮演客户端和服务器的角色。每个对等方都能直接与其他对等方通信,共享资源和服务。网络中的 资源和处理能力是分布式的 ,不依赖于单一的中心服务器。
P2P 模型的应用非常广泛:
- 文件共享 :如 BitTorrent,用户同时从多个来源下载文件,并向其他用户上传已有的部分。
- 分布式计算 :如某些加密货币(比特币),节点共同维护账本。
- VoIP(网络电话) :某些 VoIP 应用利用 P2P 技术建立通话连接。
2.7 Socket API
Socket API是网络通信中通信端点的一个抽象。你可以把它看作是应用程序与网络协议栈进行通信的一扇“门”或一个“句柄”。API (应用程序编程接口) 是一组函数,程序员可以通过调用这些函数来实现特定的功能。Socket API 就是操作系统提供给程序员的一套标准接口(函数库),用于创建、管理和使用套接字,以便进行网络通信。
通过 Socket API,应用程序可以实现以下核心操作:
| 操作 | 函数示例 (C/C++ 语言) | 描述 |
|---|---|---|
| 创建 | socket() |
创建一个新的套接字。 |
| 绑定 | bind() |
将套接字绑定到本地的 IP 地址和端口号。 |
| 监听 | listen() |
服务器端用于监听进入的连接请求。 |
| 连接 | connect() |
客户端用于发起连接请求到服务器。 |
| 发送/接收 | send(),recv() |
用于通过网络发送和接收数据。 |
| 关闭 | close() |
关闭套接字,终止通信。 |
Socket API 本身不是协议 ,它是让应用层协议得以实现的 工具 。我们前面提到的所有应用层协议(如 HTTP、SMTP、FTP 等),它们都是由应用程序来实现的。
- 浏览器实现了 HTTP 客户端的功能。
- Web 服务器实现了 HTTP 服务器的功能。
- 邮件客户端实现了 POP3/IMAP/SMTP 客户端的功能。
这些应用程序在实现各自的协议逻辑时,都需要通过调用 Socket API 来完成实际的网络数据传输。
比如说:
| 协议示例 | 实现方式 |
|---|---|
| HTTP | Web 浏览器调用 socket()创建套接字,然后调用 connect()连接到 Web 服务器的 80 端口 ,再调用 send()发送 HTTP 请求报文。 |
| DNS | DNS 解析器调用 socket()创建套接字,通常使用 UDP ,然后调用 sendto()向 DNS 服务器的53 端口发送查询报文。 |
应用层协议设计者和实现者不需要关心数据是如何被分段、如何路由、如何通过网卡发送的。
- Socket API 向上 :提供了一个简洁的接口,让应用层程序可以轻松地发送和接收数据,无需了解底层复杂的网络细节。
- Socket API 向下 :负责将应用层的数据交给 传输层 (TCP/UDP)进行处理和传输。
Socket编程
Socket 就像是程序之间在网络中进行通信的“ 端口 ”或“ 连接点 ”。它为应用程序提供了一种标准的、可移植的方式来发送和接收数据,无论是同一台机器上的不同进程,还是通过互联网连接的两台不同机器上的进程。它通常基于标准的网络协议,如 TCP (传输控制协议) 或 UDP (用户数据报协议) 。
Socket 编程最常见的应用模式是 客户端 (Client) - 服务器 (Server) 模型 。
在该模型中,服务器端等待并接收客户端的连接请求,并处理数据:
| 步骤 | 操作 | 描述 |
|---|---|---|
| 1. | socket() |
创建一个 Socket :指定使用的协议族(如 IPv4)和类型(如 TCP)。 |
| 2. | bind() |
绑定地址和端口 :将 Socket 绑定到一个本地 IP 地址和端口号上。这是客户端连接的“门牌号”。 |
| 3. | listen() |
监听连接 :服务器 Socket 处于监听状态,准备接收客户端的连接请求。 |
| 4. | accept() |
接受连接 :阻塞等待,直到有客户端尝试连接。一旦接受,会创建一个新的 Socket专用于这个客户端的通信。 |
| 5. | recv()/send() |
通信 :通过新的 Socket 接收和发送数据。 |
| 6. | close() |
关闭 :通信结束后,关闭 Socket。 |
客户端主动发起连接请求,并与服务器进行通信:
| 步骤 | 操作 | 描述 |
|---|---|---|
| 1. | socket() |
创建一个 Socket :与服务器使用相同的协议族和类型。 |
| 2. | connect() |
连接服务器 :使用服务器的 IP 地址和端口号向服务器发起连接请求。 |
| 3. | send()/recv() |
通信 :连接成功后,通过这个 Socket 接收和发送数据。 |
| 4. | close() |
关闭 :通信结束后,关闭 Socket。 |
下面举一个python的TCP Echo服务器示例,展示了如何用 Python 的 socket 模块实现一个服务器,它接收客户端发送的任何消息,然后将消息原样(“Echo”)返回。
服务器端代码 (server.py)
import socket
# 1. 配置
HOST = '127.0.0.1' # 标准回环地址 (本机)
PORT = 65432 # 要监听的端口
# 使用 with 语句,确保 Socket 会被自动关闭
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# socket.AF_INET 表示使用 IPv4
# socket.SOCK_STREAM 表示使用 TCP 协议 (流式套接字)
# 2. 绑定地址
s.bind((HOST, PORT))
print(f"服务器已启动,监听 {HOST}:{PORT}...")
# 3. 监听连接
s.listen()
# 4. 接受连接(阻塞等待客户端连接)
# conn 是新的连接 Socket,addr 是客户端的地址
conn, addr = s.accept()
with conn:
print(f"连接来自 {addr}")
# 5. 通信循环
while True:
# 接收数据,最多接收 1024 字节
data = conn.recv(1024)
if not data:
# 客户端断开连接
break
# 将收到的数据原样发送回去 (Echo)
conn.sendall(data)
print(f"回送数据: {data.decode('utf-8')}")
print("连接关闭。")
客户端代码 (client.py)
import socket
# 1. 配置
HOST = '127.0.0.1' # 服务器的地址
PORT = 65432 # 服务器的端口
MESSAGE = "Hello, Socket Programming!"
# 使用 with 语句,确保 Socket 会被自动关闭
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
# 2. 连接服务器
s.connect((HOST, PORT))
print(f"已连接到服务器 {HOST}:{PORT}")
# 3. 发送数据
s.sendall(MESSAGE.encode('utf-8'))
print(f"发送: {MESSAGE}")
# 4. 接收服务器回送的数据
data = s.recv(1024)
print(f"接收到 Echo 数据: {data.decode('utf-8')}")
print("客户端结束。")
运行方式:
- 在一个终端运行server.py,启动服务器
- 在另一个终端运行client.py,启动客户端
您将看到客户端发送的消息被服务器接收,然后服务器将该消息回送给客户端,从而完成了整个 Socket 通信过程。
Ch.3 Transport层
3.1 TCP连接
TCP是面向连接的可靠传输,保证数据按序到达。具体的三次握手过程如下:
- client发送SYN给server
- server接收到SYN后,发送SYN+ACK给client
- client接收到SYN+ACK后,发送ACK给server
我们用 \(RTT\)(往返时间)来衡量客户端和服务器之间一个数据包来回一趟所需的时间,那么很显然完成三次握手花费的时间是1.5RTT。
三次握手确定连接后,即可传输文件。
传输完成后,TCP会进行四次挥手:
- client发送完所有的内容,发送FIN
- server接收到client的FIN,发送ACK
- server完成所有内容的接收,发送FIN
- client接收到server的FIN,发送ACK
整个过程如下:
TCP报文段头部中的几个关键标志位和字段如下:
| 字段 | 含义 | 作用 |
|---|---|---|
| SYN | Synchronization (同步) | 请求建立连接 。 |
| ACK | Acknowledgment (确认) | 确认收到了对方的报文。 |
| Seq | Sequence Number (序列号) | 发送方当前报文段中数据的第一个字节的序号。 |
| Ack | Acknowledgment Number (确认号) | 接收方期望收到的下一个字节的序号(即已收到序号\(+1\))。 |
| Window Size | 窗口大小 | 接收方告诉发送方自己当前可以接收的数据量 (用于流量控制)。 |
具体的传输见下一节TCP传输。
3.2 TCP传输
TCP完成三次握手后,就在client和server之间建立了连接。建立连接后,就可以开始传输文件。传输的单位是segment,传输基于 ARQ(Automatic Repeat Request,自动重传请求)来完成。
ARQ主要分为两种:GBN和SR。这两种方法都是基于滑动窗口来实现的。
GBN
GBN(Go-Back-N,回退N)协议的名字来源于它的重传机制:当发生丢包时,发送方需要回退到丢失的那个包,并重新发送该包及之后的所有已发送数据包。

简单来说,sender按顺序发送pkt,receiver按顺序接收pkt。如果receiver没有接收到pkt,那么receiver就会直接discard这个跳过了顺序的pkt,然后重新发送ack回去。
SR
SR(Selective Repeat, 选择重传) 是另一种基于滑动窗口的ARQ策略,SR在理论上不会比GBN更差,算是一种GBN的优化版本。
SR的核心思想是:当数据包丢失时,发送方只重传丢失的那一个数据包,而不会像 GBN 那样重传丢失包之后的所有数据。

可以看到,在SR协议中,当receiver没有收到pkt2后,receiver继续接收后面的pkt,但是并没有直接存储,而是先缓存起来。当sender超时后依然没有收到ack2后,会重新发送pkt2,这个时候receiver会在接收到后把pkt2和缓存的一起正式接收,并发送ack2。
为了保证 SR 协议的可靠性,窗口大小 \(N\) 必须满足以下条件:
这个限制也被称为“SR的困境”。其根本原因是,当序列号空间不足的时候,receiver无法区分一个收到的pkt到底是当前轮次丢失后被重传的,还是下一轮次带着相同序列号的新pkt。
3.3 拥塞控制
拥塞控制(Congestion Control) 是TCP协议独有的机制,旨在保护整个互联网(Internet)免于崩溃。 GBN和SR实现了TCP协议的 传输可靠性(Reliability),但是如果没有拥塞控制,每个发送方都会以最快速度发送数据。当网络流量超过路由器处理能力时,所有路由器会开始丢弃数据包,导致拥塞崩溃 (Congestion Collapse)。拥塞控制就是为了防止这种情况发生。
拥塞控制采用以下机制来保证网络不会拥塞崩溃:
- 拥塞窗口(CWND)
- 慢启动(SLow Start)
- 拥塞避免(Congestion Avoidance)
- 快重传(Fast Retransmit)
- 快恢复(Fast Recovery)
拥塞控制就像管理一条繁忙的高速公路(网络),我们的目标是让尽可能多的车(数据包)通过,同时防止交通瘫痪(拥塞崩溃) 。CWND就是sender在等待来自receiver的ACK确认信息之前,可以发送到网络中的最大数据量。
接收窗口(RWND)则是限制接收方的流量控制机制。
发送方实际能够发送的数据量,受以下CWND和RWND的最小值限制:
不过一般来说,CWND是限制发送速率的主要因素。因此拥塞控制主要是针对CWND的,我们主要学习和了解MIMD和AIMD这两种拥塞控制方法。
MIMD
MIMD(Multiplicative Increase/Multiplicative Decrease)机制是让CWND在增加阶段以指数级增长:
然后在减少阶段则乘性减少(通常是减半):
举例来说,假如放行的时候MIMD会不停翻倍地增加可通行的流量;而在检测到拥塞的时候,又不停减半放行流量。
很显然,MIMD会导致剧烈震荡及不稳定。
AIMD
AIMD(Additive Increase/Multiplicative Decrease)牺牲了MIMD的快速,但换取了稳定性。由于网络稳定更加重要,因此TCP/QUIC拥塞控制基本都采用AIMD机制。
在增加阶段,AIMD会让CWND以线性速度缓慢增加:
当检测到拥塞时,减少方式和MIMD一样,一般也是减半,这是为了迅速缓解压力,避免拥塞恶化:
AIMD相对公平,可以让两个流的份额差距不至于迅速拉到太大,从而让所有流都收敛到公平共享带宽的状态。
3.4 UDP
UDP(User Datagram Protocol,用户数据报协议) 的传输方式与 TCP 截然相反,它是一种 面向无连接的、不可靠的 传输协议,因此它的传输流程非常简单、快速:
UDP不需要三次握手来建立连接,不需要四次挥手,没有序列号、确认号、重传等,只管传输,不管是否传输成功。
UDP 头部非常小且固定,通常只有 8 字节 ,这使得它的开销极低。
- 源端口号 :占 2 字节。
- 目的端口号 :占 2 字节。
- UDP 长度 :占 2 字节(UDP 头部和数据的总长度)。
- UDP 校验和 :占 2 字节(用于检测数据报是否损坏)。
举例来说,DNS就是一个UDP的典型应用,当设备向DNS发送一个查询网址对应IP的请求后,DNS服务器会直接返回一个包含IP地址的UDP数据报。client在发送时会启动一个计时器,如果计时器到时后没有接收到server返回的内容,那么client就会再次发送。整个过程中没有任何的连接和沟通,只有client的请求与server的发送。
3.5 QUIC
QUIC(Quick UDP Internet Connections) 是 Google 提出的、后来由 IETF 标准化的新一代 传输层协议 ,用于替代传统的 TCP + TLS + HTTP/2 组合。
它运行在 UDP 之上 ,但提供比 TCP 更强的性能、灵活性和安全性。由于采用UDP,因此QUIC不需要传统TCP传输的三次握手、四次挥手,而采用了全新的握手和重传机制。
HTTP/3就是运行在QUIC协议之上的。这里要注意,HTTP/3在底层强制要求使用TLS加密技术,因此HTTP/3总是安全的。但是在术语上,仍然要区分HTTP和HTTPS,HTTPS是安全传输的总称,而HTTP(无论是HTTP/1.1, HTTP/2还是HTTP/3)则是定义数据传输格式和规则的应用层协议。

如上图所示,QUIC相比于TCP+TLS1.3,节省了1个RTT。
Ch.4 Internet层
TCP/IP一共有四层,其中Internet层在一些教材中相当于网络层的内容。因此为了方便,在本章中Internet层、网络层、网际层都是指的一个东西。
网络层主要负责将数据包从源主机传送到目的主机,因此核心内容是IP、路由、转发等。
4.1 IP
我们先来看IP。在笔记开头曾提到大包裹的例子,当我们从小区内部把大包裹送到小区门口的时候,门口的保安需要根据最终的目的地来规划路线,这里最终的目的地就是根据互联网IP来决定的。
IP协议(Internet Protocol) 是网络层中最重要、最核心的协议,他提供的是不可靠、无连接的数据报服务。这里要注意:
- IP中的Internet指的不是我们常说的互联网,而是网络互联(Inter-networking)的意思,因此在局域网中我们也会用到192.168.0.1这种IP地址。
- 不可靠无连接的数据报服务,指的是IP协议自身不提供错误校正、丢包重传或顺序保证等机制,这些可靠性需求在上层的TCP协议中实现
不过我们一般提到IP,指的都是互联网中的IP。在互联网上,IP地址是唯一的。
与IP相关的还包括将网络层的IP地址解析为数据链路层的MAC地址需要靠ARP协议,报告网络错误和设备状态信息的ICMP协议。
完整的IP协议包括IP地址和IP数据报。IP数据报(Datagram)就是包裹好的信件,也就是我们在一开头例子中的大包裹。大包裹上面写的地址则是IP地址。
IP数据报
IP Datagram是网络层传输的基本单位,由Header和Data Payload部分组成。
IPv4地址
IP 地址是用于唯一标识连接到网络上的每一台主机或网络接口的数字标签。目前,互联网主要使用两种 IP 地址版本:IPv4和IPv6。
我们首先来看最常用的IPv4。
IPv4一般由四个数字组成,比如192.168.1.10,但是这四个数字本身并没有固定的、单独的含义,他们共同组成了一个32位的地址。
IPv4 地址的 \(32\) 位二进制结构是分层寻址的基础:
其中:
- 网络号标识设备所连接的 网络或子网 。路由器在互联网上进行转发时,主要依赖这个网络号来确定数据包的去向。
- 主机号标识该网络中的 特定设备 。当数据包到达正确的网络后,数据链路层和网络层依靠这个主机号(并结合 MAC 地址)将数据包最终交付给正确的设备。
由于网络号和主机号的分界线是可变的,因此我们需要通过子网掩码(Subnet Mask)来判断,在这32位中,有多少是网络号,有多少是主机号:
- 子网掩码中为1的位对应IP地址中的网络号
- 子网掩码中为0的位对应IP地址中的主机号
换句话说,仅仅知道IP地址是不够的,我们必须同时知道子网掩码,才能知道这个IP地址中的网络号和主机号都是多少。
比如说,128.124.2.44可能是:
- 网络 \(128.0.0.0/8\) 中的一个主机。(A类网络)
- 网络 \(128.124.0.0/16\) 中的一个主机。(B类网络)
- 网络 \(128.124.2.0/24\) 中的一个主机。(C类网络)
这就是CIDR写法:IP地址/网络前缀长度,上面标注的A、B、C类网络就是在CIDR发明之前的A/B/C类的网络划分法,浪费了大量的IPv4地址。CIDR通过使用任意长度的网络前缀,暂时地解决了IP地址不够用的问题。
只有当IP地址与子网掩码结合时,IP地址才能变成一个完整的、可用的地址。
子网掩码不是凭空出现的,它在两个关键位置存在:
- 在电脑、手机或服务器上配置IP地址时,操作系统会通过DHCP自动获取子网掩码,或者用户也可以手动配置。终端可以通过该子网掩码来计算本地网络地址,进而知道哪些IP地址是邻居,可以直接通过ARP协议通信;哪些是远程,需要发给默认网关
- 在路由器的路由表中,即互联网上的所有路由器,他们的路由表中存储了包含子网掩码(以CIDR前缀长度表示)的路由条目。路由条目中包含目标网络地址、前缀长度、下一跳地址等,具体内容后面的路由章节会展开讲。
简单来说,路由器判断两个主机是否在同一子网,是分别对它们的 IP 与子网掩码进行 AND 运算,如果两个结果(子网号 network address )相同,则在同一子网。
IPv6地址
上面已经提到,CIDR写法的出现就是为了解决IP地址不够用的问题。然而,那个时候没人想到互联网会发展到今天这样庞大的地步:IPv4也不够用了。
由于IPv4地址上限也不过几十亿个,在今天人手一个智能手机的时代,IPv4显然是完全不够用的。早在2011年,IANA就分配完了最后一个IPv4地址块。如今,想要新的IPv4地址块,需要从已有的人手里购买。MIT就因为手头持有大量的IPv4地址,因此而发了一笔横财。
为了解决这个问题,IPv6被提出来了。虽然IPv6的推广非常缓慢,但其渐渐会成为主网络,而IPv4将慢慢成为一个永远存在的兼容层。在中国,三大运营商目前已经全面启动IPv6,当手机用流量上网时,会优先IPv6,只有目标服务不支持IPv6时才退回IPv4。
4.2 路由
在发送大包裹时,计算机需要首先通过本地的子网掩码来判断IP是本地地址还是远程主机。如果是本地地址,则直接通过ARP协议来找到本地主机的MAC地址,无需路由。如果是远程主机,则需要将大包裹发送给网关,然后开始互联网中的路由。
路由器在功能上包括两个内容:
- 路由
- 转发
其中路由是一个长期、持续进行的决策,决定数据包从源到目的地的完整路径,通过路由协议如OSPF、BGP等进行。路由器会不断与其他路由器交换信息,计算最佳路径,构建并维护路由表。
转发则是瞬间、实时执行的一个动作。路由器会根据已经建立的路由表,确定当前数据包的下一跳(Next Hop)是谁,然后将其从正确的接口发送出去。路由查找(IP Lookup)会根据最长前缀匹配原则查询路由表。
OSPF
OSPF (Open Shortest Path First) 是一种最流行的 内部网关协议(IGP) ,用于在单一自治系统(AS)内部(例如一个大型企业或 ISP 的网络内部)进行路由。
BGP
BGP 是互联网的 核心路由协议 ,它是唯一的 外部网关协议(EGP) ,用于在不同的自治系统(AS)之间(例如不同 ISP 之间)交换路由信息。
4.3 转发
转发是路由器的实时执行动作,核心在于 路由查找(IP Lookup) ,即根据目的 IP 地址从路由表中找到唯一的出口。
当一个数据包到达路由器时:
- 读取目的 IP: 路由器检查 IP 数据包头部的 目的 IP 地址 。
- 查询路由表: 路由器将目的 IP 地址与路由表中所有条目的 CIDR 前缀 进行匹配。
- 遵循原则: 路由器严格遵循 最长前缀匹配原则(Longest Prefix Match, LPM) 。
LPM 原则是路由器解决子网掩码可变性和路由聚合的关键机制:
- 原则: 如果一个目的 IP 地址同时匹配路由表中的多个前缀,路由器必须选择 前缀长度最长 (即子网掩码最精确)的那一条路由。
比如说,我们的目的IP是10.1.1.15,其二进制表示如下:
然后我们开始查询路由表中的网络前缀,路由表中不存储单个主机的IP地址,而是会存储网络块(Network Blocks)。
举例来说,路由表中可能有以下条目:
- \(10.0.0.0/8\)
- \(10.1.0.0/16\)
- \(10.1.1.0/24\)
这些条目代表的是不同的范围:
- \(10.0.0.0/8\) 代表了从 \(10.0.0.0\) 到 \(10.255.255.255\) 的 所有地址 。
- \(10.1.0.0/16\) 代表了从 \(10.1.0.0\) 到 \(10.1.255.255\) 的 所有地址 。
- \(10.1.1.0/24\) 代表了从 \(10.1.1.0\) 到 \(10.1.1.255\) 的 所有地址 。
我们可以看到10.1.1.5被包含在上述三个范围内,那么为了保证数据包能够到达最精确的位置,路由器必须选择包含该 IP 地址的 最小网络块 ,即前缀最长的 \(/24\) 。
一旦通过 LPM 确定了下一跳 IP 地址和出站端口,路由器将执行转发:
- TTL 减 1: IP 头部中的 TTL(Time to Live) 字段减 1。如果 TTL 降到 \(0\),数据包会被丢弃,并发送 ICMP 错误消息给源主机。
- 数据链路层封装: 路由器为数据包重新封装 新的数据链路层头部 。
- 确定下一跳的 MAC 地址 (通常通过 ARP 获得)。
- 源 MAC 地址是路由器出站接口的 MAC 地址。
- 发送: 将新的帧通过指定的出站端口发送给下一跳设备。
Ch.5 Link层
在一些教材中,Link层被分成数据链路层和物理层来讲。在TCP/IP协议中,Link层包含了这些内容。
内容包括:
Frame,校验,局域网,ARP,MAC地址
MAC协议,CSMA/CD, CSMA/CA
Ethernet, PPP, HDLC, Wi-Fi
懒得整理了,如果能遇到相关内容再学吧
Ch.6 网络安全
网络安全包括:防火墙、IDS/IPS、非对称加密、证书、TLS
DDoS攻击、ARP欺骗、跨站攻击
零信任安全架构等