趣谈网络协议-笔记
公司开了个极客时间企业版,一人挑五门课
看了下这个《趣谈网络协议》挺有意思的
只看了前两讲就感觉收获挺大,所以记记笔记方便以后查找知识
通信协议综述
协议三要素
- 语法: 符合一定的规则和格式。例如括号要成对,换行要有分号。
- 语义: 代表某种意义。例如数字可以相间,字符串相减没有意义。
- 顺序: 就是先干什么后干什么。例如 1+1-1 就是 先执行 1+1 在用结果减 1。
网络协议和层级
| 层级 | 协议 |
|---|---|
| 应用层 | DHCP HTTP HTTPS RTMP P2P DNS GTP RPC |
| 传输层 (TCP/UDP 层) | UPD TCP |
| 网络层 (IP层) | ICMP IP OSPF BGP IPSec GRE |
| 链路层 (MAC层) | ARP VLAN STP |
| 物理层 | 网络跳线 |
当网络包到达一个城关的时候,可以通过路由表得到下一个城关的 IP 地址,直接通过 IP 地址找就可以了,为什么还要通过本地的 MAC 地址呢?
- mac地址 相当于身份证号(网卡厂商分配,理论上是唯一的但可以被修改),ip地址 相当于现居住地(连接到网络时,网络运营商分配)。
- 历史原因,网络发展史是先有 数据链路层(MAC地址) 再有 网路层(IP地址) 的。
- 网络分层,只要是在网络上跑的包,都是完整的。可以有下层没上层,绝对不可能有上层没下层。也就是说在网络分层的角度 数据包不可能只有IP地址没有MAC地址。
为什么要网络分层?
复杂的程序都要分层,这是程序设计的要求。比如,复杂的电商还会分数据库层、缓存层、Compose 层、Controller 层和接入层,每一层专注做本层的事情。
ip地址
如何查看ip地址?
- windows:ipconfig
- linux:ifconfig 和 ip addr
ip地址的组成?
10.100.122.2 就是一个 IP 地址。这个地址被点分隔为四个部分,每个部分 8 个 bit,所以 IP 地址总共是 32 位。
ip地址被分为五类
| IP地址范围 | 最大主机数 | 私有IP地址范围 | ||
|---|---|---|---|---|
| A类 | 0丨网络号(7位)丨主机号(24位) | 0.0.0.0 - 127.255.255.255 | 16777214 | 10.0.0.0 - 10.255.255.255 |
| B类 | 10丨网络号(14位)丨主机号(16位) | 128.0.0.0 - 191.255.255.255 | 65534 | 172.16.0.0 - 172.31.255.255 |
| C类 | 110丨网络号(21位)丨主机号(16位) | 192.0.0.0 - 223.255.255.255 | 254 | 192.168.0.0 - 192.168.255.255 |
| D类 | 1110丨多播组号(28位) | |||
| E类 | 11110丨留待后用(27位) |
无类型域间选路(CIDR)
由于 C 类地址包含的最大主机数太少,只有 254个。而 B 类地址包含主机数又太多。
所以有了一种折中的方式叫 无类型域间选路,简称 CIDR。
CIDR 将 32 位IP地址一分为二,前面是网络号,后面是主机号。
比如 10.100.122.2/24,这个IP地址有一个斜杠,斜杠后的 24 代表前24位是网络号,后8位是主机号。
伴随 CIDR 存在的是广播地址和子网掩码。
- 10.100.122.2/24 的广播地址是 10.100.122.255
- 10.100.122.2/24 的子网掩码是 255.255.255.0
将子网掩码和IP地址进行按位与运算就可以得到网络号
- 10.100.122.2 按位与 255.255.255.0 就是 10.100.122.0
计算 CIDR 表示的 IP地址 16.158.165.91/22 的第一个地址、子网掩码 和 广播地址。
- 第一个地址是 16.158.<101001><00>.1 即 16.158.164.1
- 子网掩码是 255.255.<111111><00>.0 即 255.255.252.0
- 广播地址是 16.158.<101001><11>.255 即 16.158.167.255
小结
- IP 是地址,有定位功能;MAC 是身份证,无定位功能
- CIDR 可以用来判断是不是本地人
- IP 分公有的 IP 和私有的 IP
DHCP 和 PXE
如何获取目标MAC地址?
- linux 会判断目标IP地址和我是一个网段的吗,或者和我的一个网卡是同一网段的吗?如果是一个网段的,会发送 ARP 请求,获取MAC地址。
- 如果不是,linux 会认为这个一个跨网关调用,会获取网关的 MAC地址,然后将包发到网关。
- 每经过一个网关,MAC地址都会被替换下一次要去的网关的MAC地址,直到到达和目标IP地址同网段的网关时,发送 ARP请求获取目标IP的主机 MAC 地址。
DHCP 的工作方式
当一台新机器假如网络时,只知道自己的 MAC地址,先”吼”一句告诉别人我来了。这一步称为 DHCP Discover。
- 新来的机器使用 IP地址 0.0.0.0 发送一个广播包,目的IP为 255.255.255.255
- 广播包封装了 UDP,UDP封装了BOOTP

如果网络管理员配置了 DHCP Server的话,它就会立刻知道来了”新人”,这时候会验证 MAC地址的唯一性,并且租给新人一个IP地址。这个过程我们称为 DHCP Offer。
DHCP Server 会保留为该”新人”分配的IP地址,防止分配给其他人
DHCP Server仍然使用广播地址作为目的地址,因为”新人”还没有IP地址

新来的机器收到了 “吼”的回复,并且如果同时收到多个 DHCP Server的回复,它会决定使用谁提供的IP地址,一般是第一个回复的,并且告诉其它 DHCP服务,请求它们撤销提供的IP地址,以便可以分配给其他人。
- 由于还没有得到 DHCP Server 的最后确认,客户端仍然使用 0.0.0.0 为源 IP 地址、255.255.255.255 为目标地址进行广播。
当 DHCP Server 接收到新来的机器的 DHCP request 之后,会广播返回给它一个 DHCP ACK 消息包,表明已经接受客户机的选择,并将这一 IP 地址的合法租用信息和其他的配置信息都放入该广播包,发给客户机,欢迎它加入网络大家庭。

PXE(预启动执行环境)工作过程
可以自动安装操作系统。
ps: 了解一下工作过程就行了。。
从2层(MAC层)到3层(网络层)
从物理层到链路层
物理层
- 电脑连电脑。
- 集线器。
数据链路层(MAC层)
数据链路层其实包含:
- LLC(The logical link control),逻辑控制链路
- MAC(Medium Access Control),媒体访问控制
由于TCP/IP 体系经常使用的局域网是 DIX Ethernet V2而不是 IEEE 802 标准中的几种局域网,因此现在 802 委员会制定的逡辑链路控制子层 LLC (即 802.2 标准)的作用已经不大了。
很多厂商生产的网卡上就仅装有 MAC 协议而没有 LLC 协议
所以一般不考虑 LLC 层,只考虑MAC层。
主要解决这几个问题:
- 这个包是发给谁的?谁应该接收?
- 这里用到一个物理地址,叫作链路层地址。但是因为第二层主要解决媒体接入控制的问题,所以它常被称为MAC 地址
- 大家都在发,会不会产生混乱?有没有谁先发、谁后发的规则?
- 信道划分:分多个车道,每个车一个车道,你走你的我走我的
- 轮流协议:今天单号,明天双号,轮流出行
- 随机接入协议:不管三七二十一,有事儿先出门,发现特堵,就回去,错过高峰再出。以太网就是这个方式。
- 如果发送的时候出现了错误,怎么办?
- CRC(循环冗余检测):通过 XOR 异或的算法,来计算整个包是否在发送的过程中出现了错误
ARP协议
ARP协议是一个网络层协议,也就是已知 IP 地址,求 MAC 地址的协议。
原理是发送广播包,目标IP回答。
为避免每次都是用ARP请求,机器会进行ARP缓存(ARP缓存表),并且该缓存会有过期时间。
交换机
交换机一般工作在 数据链路层(MAC层)
交换机可以通过学习记录连接某个口的MAC地址。交换机的学习结果称为转发表,并且有过期时间。
一台 MAC1 电脑将一个包发送给另一台 MAC2 电脑,当这个包到达交换机的时候,一开始交换机也不知道 MAC2 的电脑在哪个口,所以没办法,它只能将包转发给除了来的那个口之外的其他所有的口。但是,这个时候,交换机会干一件非常聪明的事情,就是交换机会记住,MAC1 是来自一个明确的口。以后有包的目的地址是 MAC1 的,直接发送到这个口就可以了。
RARP协议
根据MAC地址获取IP的协议。。知道就行了。。
如果一个局域网里面有多个交换机,ARP 广播的模式会出现什么问题呢?
ARP广播时,交换机会将一个端口收到的包转发到其它所有的端口上。
比如数据包经过交换机A到达交换机B,交换机B又将包复制为多份广播出去。
如果整个局域网存在一个环路,使得数据包又重新回到了最开始的交换机A,这个包又会被A再次复制多份广播出去。
如此循环,数据包会不停得转发,而且越来越多,最终占满带宽,或者使解析协议的硬件过载,行成广播风暴。
交换机与VLAN
拓扑结构是如何形成的
假设现在两台交换机,连接着三个局域网,机器1想要访问机器4,但是机器1只知道机器4的IP地址,不知道MAC地址。
- 机器1发起一个广播,机器2收到广播,但跟自己没关系。
- 交换机A也收到了广播,但是它不知道任何拓扑信息,于是它向除了广播包的来源方向外,转发给其它所有网口。
- 机器3收到了广播信息,发现也不是找自己的。
- 交换机B收到广播后,采用交换机A相同操作。
- 机器4和机器5也收到广播,机器4发现是找自己的(IP地址一致),于是响应说这是找我的。
- 最后,交换机A和交换机B现在都知道机器1是左边这个网口的。
- 如果这时机器3需要访问机器1,交换A和交换机B就不会再广播右边的网口了。
如何解决环路问题
- STP协议就是使用最小生成树的思想。关于最小生成树可以看我之前分享的:Kruskal算法
- 物理隔离和虚拟隔离(VLAN形成虚拟局域网隔离)
ICMP和ping
ICMP协议
ICMP 全称 Internet Control Message Protocol,就是互联网控制报文协议。
ICMP是一个网络层协议。
| 类型值 | ICMP消息类型 | 错误消息 | 查询消息 |
|---|---|---|---|
| 0 | Echo Reply 回送响应消息 |
✅ | |
| 3 | Destination Unreachable 目的不可达 |
✅ | |
| 5 | Redirect 重定向 |
✅ | |
| 8 | Echo Request 回送请求消息 |
✅ | |
| 11 | Time Exceeded 超时 |
✅ | |
| 12 | Parameter Problem 参数问题 |
✅ | |
| 13 | Timestamp Request 时间戳请求 |
✅ | |
| 14 | Timestamp Reply 时间戳响应 |
✅ |
ping
ping是ICMP最著名的一个应用,可以测试 网络的可达性
tracert
ping 工具只能测试目的设备的连通性,但是看不到数据包的传输路径
tracert 工具可以查看数据包的整条传输路径,包括途中经过的中间设备
IP 头部的 TTL 字段是为避免数据包循环转发而设计的。每经过一个路由器,数据包头中的 TTL 值减 1 。如果 TTL 值为 0 则丢弃报文,并向源设备回应一个 Time Exceeded 消息,告知错误类型。
使用 tracert 命令时,源设备的 tracert 逐跳发送数据包,并等待每一个响应报文。发送第一个数据包时,TTL 值设为 1 。第一个路由器收到数据包后 TTL 值减 1 ,随即丢弃数据包,并返回一个 Time Exceeded 消息。
怎么知道 UDP 有没有到达目的主机呢?Traceroute 程序会发送一份 UDP 数据报给目的主机,但它会选择一个不可能的值作为 UDP 端口号(大于 30000)。当该数据报到达时,将使目的主机的 UDP 模块产生一份“端口不可达”错误 ICMP 报文。如果数据报没有到达,则可能是超时。
网关
一台机器访问另一个ip地址
- 先判断 目标IP和当前机器的IP地址,是不是一个网段的。
- 如果是一个网段的,那就使用ARP协议获取目标IP的MAC地址。没网关什么事情。
- 如果不是同一网段的,那请求就要发往默认网关(通过ARP协议获取网关MAC地址)。
IP 头和 MAC 头哪些变、哪些不变?
静态路由: IP不变,MAC地址变。
NAT(Network Address Translation)路由:IP变,MAC地址变。
传输层
UDP协议
UDP包头
UDP和TCP的区别
面向连接
什么是连接?连接其实就是 客户端和服务端建立一个数据结构来维护双方交互状态。
- TCP是面向连接的
- UDP是面向无连接的
可靠性
IP包是没有任何可靠性保证的,一旦发出去,不管是超时了还是丢了,都随它而去
- TCP提供可靠交付,通过 TCP 连接传输的数据,无差错、不丢失、不重复、并且按序到达
- UDP 继承了 IP 包的特性,不保证不丢失,不保证按顺序到达
面向字节流
IP包不是流,而是一个个的IP包。
- TCP是面向字节流的,发送的是一个流,没头没尾
- UDP发送是基于数据包的,一个个的发,一个个的收
- 所以TCP会发生粘包和拆包,而UDP不会
ps: 粘包和拆包是一个通俗的说法,在知乎上会被很多大佬喷成是伪命题、伪科学。这里我们知道原理就行了。
拥塞控制
- TCP意识到包丢弃了或者网络环境不好了的时候,会根据情况调整自己的行为,看看是不是发快了,要不要发慢点
- UDP则不会
如果 MAC 层定义了本地局域网的传输行为,IP 层定义了整个网络端到端的传输行为,这两层基本定义了这样的基因:网络传输是以包为单位的,二层叫帧,网络层叫包,传输层叫段。我们笼统地称为包。包单独传输,自行选路,在不同的设备封装解封装,不保证到达。基于这个基因,生下来的孩子 UDP 完全继承了这些特性,几乎没有自己的思想。
UDP的特点
- 沟通简单:UDP的包头比起TCP来说相当简单
- 轻信他人:UDP不需要建立连接,谁都可以给它发送数据,它也可以传给任何人数据
- 愣头青:不会根据网络的情况进行发包的拥塞控制,无论网络丢包丢成啥样了,它该怎么发还怎么发。
TCP协议
TCP包头
TCP的三次握手
tcp是面向连接的,双方都需要维护一个状态机,三次握手状态变化时序图:
- 一开始,客户端和服务端都处于 CLOSED 状态
- 服务端主动监听某个端口,处于 LISTEN 状态
- 客户端发起连接发送 SYN,之后客户端处于 SYN_SENT 状态
- 服务端收到 SYN包后,应答发送 SYN + ACK 包,之后服务端处于 SYN_REVD状态
- 客户端收到 SYN + ACK 后应答发送 ACK 包, 客户端处于 ESTABLISHED 状态,因为客户端已经完成了一次发送一次接收
- 服务端收到 ACK后,也处于 ESTABLISHED 状态,它也完成了一次发送,一次接收
TCP的四次挥手
- 一开始,客户端和服务端都处于 ESTABLISHED 状态
- 客户端:我不玩了。发送 FIN 包并进入 FIN_WAIT_1 状态
- 服务端:我知道你不玩了。发送 ACK 包,进入等待关闭 CLOSED_WAIT 状态
- 客户端:我知道了。收到 ACK 包,进入 FIN_WAIT_2 状态
- 如果服务端在这个时候跑路了,客户端将永远处于这个状态
- 服务端:我这边处理完了,我也不玩了。 发送 FIN+ACK 包,进入 LAST_ACK 状态
- 客户端:我知道你不玩了。发送 ACK 包,进入 TIME_WAIT 状态
- 这里的 TIME_WAIT 时间要足够长,长到如果服务端没有收到这次的 ACK 包的话,会重复第五步,重发 FIN+ACK 包
- 等待的时间设为 2MSL,MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃
- 服务端收到客户端最后一个 ACK 包后,进入 CLOSED 状态
- 客户端在 TIME_WAIT 时间过了之后,进入CLOSED 状态
TCP状态机
将连接建立和连接断开的两个时序状态图综合起来,就是这个著名的 TCP 的状态机。
在这个图中,加黑加粗的部分,是上面说到的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户端 A 的状态变迁,加粗的虚线是服务端 B 的状态变迁。
小结
TCP 包头很复杂,但是主要关注五个问题,顺序问题,丢包问题,连接维护,流量控制,拥塞控制。
连接的建立是经过三次握手,断开的时候四次挥手
TCP 的连接有这么多的状态,如何在系统中查看某个连接的状态吗?
linux中可以使用 netstat 命令
套接字Socket
基于TCP
在内核中,为每个 Socket 维护两个队列。一个是已经建立了连接的队列,这时候连接三次握手已经完毕,处于 established 状态;一个是还没有完全建立连接的队列,这个时候三次握手还没完成,处于 syn_rcvd 的状态。
工作过程:
- 服务端和客户端初始化
socket,得到文件描述符; - 服务端调用
bind,将绑定在 IP 地址和端口; - 服务端调用
listen,进行监听; - 服务端调用
accept,等待客户端连接; - 客户端调用
connect,向服务器端的地址和端口发起连接请求; - 服务端
accept返回用于传输的socket的文件描述符; - 客户端调用
write写入数据;服务端调用read读取数据; - 客户端断开连接时,会调用
close,那么服务端read读取数据的时候,就会读取到了EOF,待处理完数据后,服务端调用close,表示连接关闭。
这里需要注意的是,服务端调用 accept 时,连接成功了会返回一个已完成连接的 socket,后续用来传输数据。
所以,监听的 socket 和真正用来传送数据的 socket,是「两个」 socket,一个叫作监听 socket,一个叫作已完成连接 socket。
成功连接建立之后,双方开始通过 read 和 write 函数来读写数据,就像往一个文件流里面写东西一样。
基于UDP
对于 UDP 来讲,过程有些不一样。UDP 是没有连接的,所以不需要三次握手,也就不需要调用 listen 和 connect。
但是,UDP 的交互仍然需要 IP 和端口号,因而也需要 bind。UDP 是没有维护连接状态的,因而不需要每对连接建立一组 Socket,而是只要有一个 Socket,就能够和多个客户端通信。
也正是因为没有连接状态,每次通信的时候,都调用 sendto 和 recvfrom,都可以传入 IP 地址和端口。
应用层
HTTP协议
http请求格式
http请求过程
- DNS解析:域名解析为IP地址。
- http 是基于 tcp协议的,请求前需要先通过三次握手建立连接,如果开启了 keep-alive,该连接就可以多次复用。
- http1.1 是默认开启 keep-alive的
- keep-alive 也有超时时间,超时关闭之后,后面需要的http请求需要重新建立连接
- tcp层,拼接源和目标端口。
- ip层,拼接源和目标IP地址。
- 如果网段相同,通过arp协议获取mac地址,直接发送
- 否则通过arp协议获取网关mac地址,发送给网关
- mac层,拼接mac地址。
tcp报文段:
http响应格式
HTTPS协议
- 对称加密:加密和解密用的密钥相同。优点是效率高、性能好。
- 非对称加密:加密和解密的密钥不同,一般是一个公钥一个私钥。效率比较低。
- CA( Certificate Authority):一个权威的颁发证书的机构,证书里有公钥等信息。最高级的ca机构成为 root CA。
https协议握手过程:
这里老师讲的感觉有点不太清晰,推荐这篇文章:http://www.ruanyifeng.com/blog/2014/09/illustration-ssl.html
- 客户端->服务端:给出协议版本号、一个客户端生成的随机数(Client random)、以及客户端支持的加密方法。
- 服务端->客户端:服务端选取确认双方使用的加密方法、给出数字证书、以及一个服务端生成的随机数(Server random)。
- 客户端->服务端:客户端验证数字证书是否有效,生成一个随机数(Premaster secret),并使用约定的加密方法和公钥加密这个随机数。
- 服务端:使用自己的私钥,解密获取上一步客户端发过来的随机数(Premaster secret)。
- 之后,双方使用约定的加密方法和前面的三个随机数,生成“对话密钥”(session key),用来对称加密接下来的对话。
陌生的数据中心
DNS协议
DNS服务器
DNS解析流程
- 电脑客户端会发出一个 DNS 请求,问 www.163.com 的 IP 是啥啊,并发给本地域名服务器 (本地 DNS)。那本地域名服务器 (本地 DNS) 是什么呢?如果是通过 DHCP 配置,本地 DNS 由你的网络服务商(ISP),如电信、移动等自动分配,它通常就在你网络服务商的某个机房。
- 本地 DNS 收到来自客户端的请求。你可以想象这台服务器上缓存了一张域名与之对应 IP 地址的大表格。如果能找到 www.163.com,它就直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大,能告诉我 www.163.com 的 IP 地址吗?”根域名服务器是最高层次的,全球共有 13 套。它不直接用于域名解析,但能指明一条道路。
- 根 DNS 收到来自本地 DNS 的请求,发现后缀是 .com,说:“哦,www.163.com 啊,这个域名是由.com 区域管理,我给你它的顶级域名服务器的地址,你去问问它吧。”
- 本地 DNS 转向问顶级域名服务器:“老二,你能告诉我 www.163.com 的 IP 地址吗?”顶级域名服务器就是大名鼎鼎的比如 .com、.net、 .org 这些一级域名,它负责管理二级域名,比如 163.com,所以它能提供一条更清晰的方向。
- 顶级域名服务器说:“我给你负责 www.163.com 区域的权威 DNS 服务器的地址,你去问它应该能问到。”
- 本地 DNS 转向问权威 DNS 服务器:“您好,www.163.com 对应的 IP 是啥呀?”163.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。
- 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。
- 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。
负载均衡
某个应用要访问另外一个应用,如果配置另外一个应用的 IP 地址,那么这个访问就是一对一的。但是当被访问的应用撑不住的时候,我们其实可以部署多个。但是,访问它的应用,如何在多个之间进行负载均衡?只要配置成为域名就可以了。在域名解析的时候,我们只要配置策略,这次返回第一个 IP,下次返回第二个 IP,就可以实现负载均衡了。
CDN
CDN分发系统架构
CDN 系统的缓存,也是一层一层的,能不访问后端真正的源,就不打扰它。这也是电商网站物流系统的思路,北京局找不到,找华北局,华北局找不到,再找北方局。