这是参加「青训营 x 字节前端训练营」笔记创作活动的第 5 天的文章
pass: 只是一些记录,具体价值有待评估
课程介绍
这节课会
- 通过一个示例建立对计算机网络的整体认识
- 建立对网络协议分层的认知
- 分析HTTP1、2、3的关系
- 介绍CDN运行的基本原理
- 了解网络安全的最基本原则
这节课不会
- 详细描述如何开发一个基于HTTP协议(或者其他协议)的网络应用
- 深入介绍课程中所涉及协议的规范(Specification)内容和实现细节
蟹堡王帝国
小目标
蟹老板想挣一个“小目标”,制定了三步走战略
- 在比奇堡开通外卖
- 在北京和上海开分店
- 在全国开分店并开通外卖
比奇堡外卖
很快,蟹堡王火了,订餐用户越来越多,但出现了一个问题
章鱼哥:蟹堡王,请问要吃什么
龙虾拉里:“为什么我打不通?
因为人太多,龙虾拉里每次打电话时章鱼哥都在接听别的用户电话,于是电话占线打不通。
但是,聪明如你,能被这种困难所难倒吗
你发现,每次订餐的都可以被总结为三点
- 谁吃 --> 派大星
- 吃什么 --> 两个蟹黄堡和一个炸海草
- 送到哪 --> 比奇堡石头屋
于是,每个用户想订餐时就使用传真机发送这三个消息到蟹堡王即可,章鱼哥终于解放了
北京和上海分店
生意越做越大,蟹老板打算在北京和上海开分店,当然,通信线路得从比奇堡拉过去。
之后这两个分店的客流量越来越多,店铺快要撑不住了,于是蟹老板打算继续开分店来减轻每个店铺的压力,当然这几家分店也得拉通信线路
于是问题出现了,通信线路难道还从比奇堡拉过去吗?(比奇堡在北大西洋,蟹老板抠门的性格肯定不会同意)
不,太浪费了,我们可以从大分店拉一条通信线路到小分店,这样就省去了很多建立通信线路的钱
而每家分店的销售数据可以这样
- 来自 xxx蟹堡王
- 发往 比奇堡蟹堡王
- 内容 今日销售1000个蟹黄堡
总店的消息可以这样发
- 来自 比奇堡蟹堡王
- 发往 xxx蟹堡王
- 内容 明日促销消息:全场8折
全国分店和通信网络
分店越来越多,全国也有了很多分店。当新增或搬迁分店的时候,都会向所有分店发送一次当前所有分店消息,这样用户在每个分店都能知道其他地方哪里有分店了
在其他城市开设分店当然也得拉一条线路,但是如果从比奇堡拉过去,很浪费(右边黄线交界处为比奇堡)
于是可以这样,杭州分店的线路从上海分店转过去,这样就又省了一笔钱(蟹老板狂喜)
原本这样
可以改成这样
这个时候,又发现一个问题
比奇堡居民居住分散,而城市中的小区密度较高,如果每家都直连蟹堡王,成本太高,聪明如你,当然想到了和开分店一样的搭通信线路的套路————小区中建立转发点,每个用户都与这个转发点链接即可。
然后蟹堡王越开越大,通信线路变成了这样
然后,以上这些可以类比为现实世界的终端(用户)、路由器、服务器(转发点)
网络基础
网络组成部分
- 主机:客户端和服务端(负责提供信息或者接收信息)
- 路由器(负责转发主机之间的信息)
- 网络协议(负责统一格式便于主机和路由器对信息的编码与解码)
网络结构
可以称为:网络的网络
- 比奇堡和小区网络:本地网络
- 北京和上海分店+比奇堡:三个本地网络节点的网络
- 全国通信网络:本地网络的网络
也就是:区域网络、城域网和广域网
信息交换方式
电路交换&分组交换
网络分层
物理层、链路层、网络层、运输层和应用层
- 快递员不关心包裹内容
- 卡车司机不关心车厢里拉的是什么
- 高速公路不关心开的什么车
网络协议
协议的存在依赖于链接
协议的存在避免了以错误的方式解读数据
协议定义了在两个或多个通信实体之间交换的报文格式和顺序,以及报文发送和/或接受一条报文或其他事件所采取的动作。
标头和载荷
收件人、寄件人关注:
- 收件地址、寄件地址
- 收件人、寄件人的姓名和电话
- 包裹内容
而快递公司关注:
- 收件人、寄件人关注的东西
- 该由哪个集散点发出,哪个集散点收
- 哪个网点派送
HTTP协议示例
链路层-本地帧头部
链路层-IP协议头部
运输层-TCP协议头部
应用层-HTTP协议头部
TCP协议格式
Web中的网络
HTTP
http报文=链路协议头+ip协议头+TCP协议头+http协议头+http正文
http1.1及以前版本中,头部和载荷是通过两个换行符和回车符进行划分的
HTTP连接模型
这种模型无法避免一种问题:队头堵塞(Head of Line Blocking)
而且HTTP1.1无法多路复用
例如你发送
console.log('hello');
body{
color: red;
}
结果变成了
body{
console.log('hello');
color: red;
}
HTTP2
帧
于是在HTTP2中出现了帧的概念
request=style.css, content='body {'
request=main.js, content='console.log('hello');'
request=style.css, content=' color: red;'
request=style.css, content='}'
前三个字节:载荷长度
第四个字节:类型
第五个字节:类型对应的Flags
第六到第九字节:
第1位:保留位
第2-32位:流ID
随后的8192字节:载荷
帧带来的额外好处
- 调整响应传输的优先级
- 头部压缩
- Server Push
TCP
HTTP2:队头堵塞,但是在TCP上
TCP包0:包含了(包含了style.css的第1行内容)的HTTP2的帧
TCP包1:包含了(包含了main.js的全部内容)的HTTP2的帧
TCP包2:包含了(包含了style.css的第2行内容)的HTTP2的帧
TCP包3:包含了(包含了style.css的第3行内容)的HTTP2的帧
如果某个包丢包了,那客户端会向服务器请求再发送一次这个包
3 RTT启动
HTTP3
QUIC
QUIC-1 RTT
QUIC-0 RTT
CDN
你无法突破物理极限的
你的钱包够鼓吗?
你,够强大吗?
还得是我
CDN (Content Delivery Network),内容分发网络。CDN就是采用更多的缓存服务器(CDN边缘节点),布放在用户访问相对集中的地区或网络中。当用户访问网站时,利用全局负载技术,将用户的访问指向距离最近的缓存服务器上,由缓存服务器响应用户请求。
CDN:最多跳两次!
| 跳数 | 城市 |
|---|---|
| 0 | 北京、上海、广州、成都、长沙、兰州、长春 |
| 1 | 天津、石家庄、沈阳、呼和浩特、南京、杭州、澳门、香港、南宁、福州、南昌、武汉、贵阳、拉萨、重庆、西安、西宁、银、哈尔滨 |
| 2 | 太原、济南、郑州、乌鲁木齐、台北、昆明、海口、合肥 |
DNS劫持
近了!近了吗?
CDN:地主家也没有余粮了
WebSocket
- 有状态的持久连接
- 服务端可以主动推送消息
- 用VebSocket发送消息延迟比HTTP低
服务端代码示例
const {WebSocketServer} = require('ws');
const wss = new WebSocketServer({port: 8080});
wss.on('connection', function connection(ws) {
// 有新链接时监听来着客户端的消息
ws.on('message', function incoming(message) {
// 打印客户端发送的消息,并原封不动的返回给客户端
console.log('received: %s', message);
ws.send(message);
});
});
客户端代码示例
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8080');
ws.on('open', function open() {
// 当连接建立时发送一条消息给服务器
ws.send('something');
});
ws.on('message', function incoming(data) {
// 接收到服务器发送的消息
console.log('received: %s',data);
});
升级!
发送消息
网络安全
网络安全三要素
- 机密性:攻击者无法获知通信内容
- 完整性:攻击者对内容进行篡改时能被发现
- 身份验证:攻击者无法伪装成通信双方的任意一方与另一方通信
对称加密和非对称加密
- 对称加密:加密、解密用同样的密钥
- 非对称加密:加密、解密使用不同的密钥(公钥和私钥),而且公钥加密只能用私钥解密、私钥加密只能用公钥解密
密码散列函数(哈希函数)
- 输入:任意长度的内容
- 输出:固定长度的哈希值
- 性质:找到两个不同的输入使之经过密码散列函数后有相同的哈希值,在计算上是不可能的
机密性
- 加密需要加密算法和密钥等信息(统称为秘密信息)
- 网络是明文的,不安全
完整性和身份验证
完整性和身份验证相互关联。
- 蟹老板向银行发起了转账请求
- 银行需要确认
- 这个请求真的是蟹老板发起的
- 目标账户和转账金额没有被篡改
如何实现机密性
- 已知:网络是明文的
- 如果双方可以通过明文通信商量出秘密信息,那么攻击者也可以
- 所以想要通过明文通信交换秘密信息,通信双方需要先有秘密信息
如何实现完整性
- 密码散列函数性质:找到两个不同的输入使之经过密码散列函数后有相同的哈希值,在计算上是不可能的
- 有明文m,密码散列函数H,以及一个密钥 s
- 计算H(m+s)获得哈希值h
- 将m和h组合成新信息m+h
- 接收方拆分m+h,重新计算H(m+s)得h',对比h'和h
所以想要实现完整性,通信双方需要先有秘密信息
如何实现身份验证
- 签名:用于鉴别身份和防止伪造
- 非对称加密性质:加密、解密使用不同的密钥(公钥和私钥),而且公钥加密只能用私钥解密、私钥加密只能用公钥解密
- 蟹老板用自己的私钥对信件进行加密,并发送给海绵宝宝
- 海绵宝宝使用蟹老板的公钥进行解密,获得原文
- 保证了机密性、完整性和身份验证
数字签名
- 数字签名:对明文内容的哈希值使用私钥加密,验证者使用公钥验证
- 数字签名(指纹)=私钥加密(密码散列函数(原文))
- 消息=原文+数字签名
- 般用于对公开内容(如包含公钥的证书)进行数字签名,防止篡改
证书链 & PKI
- 可信的人验证蟹老板的公钥
- 那谁验证可信的人的公钥?
- 根证书是证书链的尽头
- 验证的一连串证书称为证书链
- 分发证书、验证证书的基础设施称为PKI, Public Key Infrastructure
- 所以想要实现身份验证,通信双方需要先有秘密信息,即根证书中的公钥
PKI保证了普通用户不需要“面对面”和根证书机构交换根证书
证书链示例
HTTPS
把HTTP的明文换成密文,再验证身份,即HTTPS。
HTTPS = HTTP + TLS
TLS = 身份验证 + 加解密
身份验证靠PKI
HTTPS 使用 PKI 完成了除客户端身份验证以外的特性,客户端身份验证靠 HTTP 协议实现
简单来说就是:服务端身份验证靠PKI,客户端身份验证靠HTTP协议。
参考文献和书籍推荐
- 字节前端训练营 - 计算机网络概论.pptx - 飞书云文档 (feishu.cn)
- 计算机网络(原书第7版),机械工业出版社
- HTTP权威指南,人民邮电出版社
- 密码编码学与网络安全(第6版),电子工业出版社
- Introduction to HTTP/2 (web.dev)
- Web Performance Calendar » Head-of-Line Blocking in QUIC and HTTP/3: The Details (perfplanet.com)