HTTP
HTTP 即 (HyperText Transfer Protocol) 超文本传输协议。
用途:
为 Web 浏览器与 Web 服务器之间的通信而设计的,但也可以用于其他目的,比如grpc就使用了http2.0作为传输协议。
组成:
-
服务端
-
客户端
-
传输协议 TCP
- 报文
- 起始行
- 方法
- GET
- POST
- PUT
- DELETE
- HEAD
- 请求URL
- 版本 HTTP的版本
- 0.9
- 1.0
- 1.1 主流
- 2.0
- 状态码 ElasticSearch就使用了状态码来表示请求状态
- 1xx 信息展示
- 101 Switching Protocol 比如websocket
- 2xx 成功
- 204 No Content
- 3xx 重定向
- 301 Moved Permanently
- 304 Not Modified 协商缓存有用到
- 4xx 客户端错误
- 400 Bad Request
- 401 nauthorized 比如未登录
- 403 Forbidden 比如无权限
- 404 Not Found
- 411 Length Required
- 414 URI Too Long
- 429 Too Many Requests 限流
- 5xx 服务器错误
- 500 Internal Server Error 比如接口出错
- 502 Bad Gateway 错误的响应
- 503 Service Unavailable 服务器因维护或重载而停机
- 504 Gateway Timeout 不能及时得到响应
- 1xx 信息展示
- 方法
- 首部字段
- Accept MIME TYPES
- Cookie
- Expires
- If-Modified-Since
- Keep-Alive
- Referer
- User-Agent
- 主体
- 起始行
-
基本性质
- 简单 报文可以读懂
- 可扩展 headers
- 无状态,有会话 cookie
- 基于连接
-
缺点
太多了,后续的协议一个个来补坑。
DNS
作用:
HTTP中的URL通常使用的是地址信息而非IP信息,所以需要使用DNS服务获取到服务器的IP信息。
A 记录和 CNAME:
A 记录直接指向 IP 地址,CNAME 记录指向域名。
CNAME 记录可用于 CDN 加速,通过 CDN 加速别名解析网站域名,这样既可以起到加速网站的作用,又能隐藏网站的真实 IP,减少被攻击的几率。
解析机制:
因为域名服务器是按照树状结构组织的,因而域名查找是使用递归的方法,并通过缓存的方式增强性能。
优先从本地DNS缓存中读取,如果读取不到则按照下面的顺序
- 本地的 DNS 客户端向 DNS 解析器发出解析 github.com 域名的请求;
- DNS 解析器首先会向就近的根 DNS 服务器
.
请求顶级域名 DNS 服务的地址; - 拿到顶级域名 DNS 服务
com.
的地址之后会向顶级域名服务请求负责github.com.
域名解析的命名服务; - 得到授权的 DNS 命名服务时,就可以根据请求的具体的主机记录直接向该服务请求域名对应的 IP 地址;
注意
实践:
- 负载均衡 通过CNAME的方式,在不同级别的DNS服务进行处理
- 内部负载均衡
- 全局负载均衡
- 服务发现
缺点:
- 域名缓存问题 本地缓存会让全局负载均衡失败
- 域名转发问题 导致无法选择最优服务器
- 域名更新问题 在权威 DNS 服务器解析变更的时候,本地 DNS 服务器更新缓慢
- 解析延迟问题 要查很多次
HttpDNS:
不走传统的 DNS 解析,而是自己搭建基于 HTTP 协议的 DNS 服务器集群,分布在多个地点和多个运营商。当客户端需要 DNS 解析的时候,直接通过 HTTP 协议进行请求这个服务器集群,得到就近的地址。
HTTPS
HTTP 协议存在的一个很严重的问题是, HTTP 的数据是明文传输的,不做任何加密,相当于在网络上裸奔;容易被中间人恶意篡改,这种行为叫做中间人攻击;
而 HTTPS 的核心主要的思想是在http基础上增加了 ssl 安全认证。
加密通信
加密方式的分类:
- 对称加密 如 DES 只有一个密钥。
- 非对称加密 如 RSA 有公钥和私钥之分,私钥用来解密,而公钥用来加密。
HASH算法 如 SHA,来确认信息没有被篡改。
由于非对称加密比对称加密慢,所以HTTPS的实现是公钥私钥主要用于传输对称加密的秘钥,而真正的双方大数据量的通信都是通过对称加密进行的。
为什么不直接全部使用对称加密呢?因为,这样的话其他人也可以获取到公钥,从而窃取信息。
证书
现在新的问题又出现了,非对称加密也有公钥,怎么将不对称加密的公钥给对方呢?
一种是放在一个公网的地址上,让对方下载;另一种就是在建立连接的时候,传给对方。
但是这两种方式都不能保证,拿到的公钥不是第三方冒充的。因此 HTTPS 引入了证书的机制。
所谓证书,就是服务端将它的公钥还有一些基本信息,发给 CA,CA用它的私钥对这些信息进行签名。
现在的交互过程变成了,服务端直接把证书发给客户端,客户端通过CA的公钥解析证书是否合法,并获取到服务端的公钥,最后利用这个公钥来进行数据的通信。
证书链
问题现在如何获取CA的公钥呢? 简单的来说,CA是分级别的
-
end-user 包含用来加密传输数据的公钥的证书,是HTTPS中使用的证书
-
intermediates:CA用来认证公钥持有者身份的证书,即确认HTTPS使用的end-user证书是属于 end-user 的证书。这类intermediates证书甚至可以有很多级。
-
root 用来认证intermediates证书是合法证书的证书。
其中,Root Certificates 可以下载安装或者是浏览器或着操作系统内置。
这样就通过证书链来获取到 end-user 证书的公钥了。
HTTP 2.0
新增特性:
-
二进制分帧
HTTP 2.0 还将所有的传输信息分割为更小的消息和帧,并对它们采用二进制格式编码。
常见的帧有 Header 帧,用于传输 Header 内容,并且会开启一个新的流。再就是 Data 帧,用来传输正文实体。多个 Data 帧属于同一个流。
在这个机制下,HTTP2.0实现了:
- 多路复用 (Multiplexing) / 连接共享
- 请求优先级
-
头部压缩
HTTP 1.1 在应用层以纯文本的形式进行通信。每次通信都要带完整的 HTTP 的头,而且不考虑 pipeline 模式的话,每次的过程总是像上面描述的那样一去一回。
这样在实时性、并发性上都存在问题。为了解决这些问题,HTTP 2.0 会对 HTTP 的头进行一定的压缩,将原来每次都要携带的大量 key value 在两端建立一个索引表,对相同的头只发送索引表中的索引。
-
服务端推送
服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。
注意这里和websocket不同,HTTP2.0则是对HTML、CSS等JS资源的传输方式进行了优化,并没有提供新的JS API,也不能用于实时传输消息。Server是可以主动关闭连接的,所以并不是长连接。
缺点:
因为现在所有的压力集中在底层一个TCP连接之上,TCP很可能就是下一个性能瓶颈,比如TCP分组的队首阻塞问题,单个TCP packet丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响。
QUIC
从 TCP 切换到 UDP, 在应用层里面维护连接的机制。
机制:
-
自定义连接机制
不再以四元组(源 IP、源端口、目的 IP、目的端口)标识,而是以一个 64 位的随机数作为 ID 来标识连接,所以当 IP 或者端口变化的时候,只要 ID 不变,就不需要重新建立连接。
-
自定义重传机制
用递增的序列号来取代序号和应答机制
-
阻塞的多路复用
同一条 QUIC 连接上可以创建多个 stream,来发送多个 HTTP 请求。但是,QUIC 是基于 UDP 的,一个连接上的多个 stream 之间没有依赖
-
自定义流量控制
TCP 的流量控制是通过滑动窗口协议。QUIC 的流量控制也是通过 window_update,来告诉对端它可以接受的字节数。
只是TCP的流量控制是基于序列号的累计应答,一旦 ACK 了一个序列号,就说明前面的都到了,所以只要前面的没到,后面的到了也不能 ACK,就会导致后面的到了,也有可能超时重传,浪费带宽。
QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空档会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小。
缺陷:
老的硬件不支持。在一些 NAT 网络环境下,UDP 协议会被路由器等中间网络设备禁止
TCP/UDP
比较:
-
相同点
- 都是传输层协议
- 都根据端口号,将数据交给相应的应用程序
-
不同点
-
面向连接
TCP 面向连接 在客户端和服务端建立数据结构来维护双方交互的状态
UDP 面向无连接
-
可靠交互
TCP 提供可靠交付。
UDP 继承了 IP 包的特性,不保证不丢失,不保证按顺序到达。
-
面向字节流/基于数据报
TCP 是面向字节流的。发送的时候发的是一个流,没头没尾。
而 UDP 继承了 IP 的特性,基于数据报的,一个一个地发,一个一个地收。
-
拥塞控制
TCP 是可以有拥塞控制的。包丢弃了或者网络的环境不好了,就会根据情况调整自己的行为
-
有无状态
TCP 其实是一个有状态服务,UDP 则是无状态服务
-
TCP三次握手和四次挥手:
三次握手:
一句话概括,TCP连接握手,握的是通信双方数据原点的序列号
-
第一次握手(SYN, seq=x)
为了验证客户端的发送能力。
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的序列号(Sequence Number)字段里。发送完毕后,客户端进入
SYN_SEND
状态。 -
第二次握手(SYN, ACK, seq=y, ack=x+1)
为了验证服务端的接受和发送能力。
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN 序列号,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入
SYN_RCVD
状态。 -
第三次握手(ACK,ack=y+1)
为了验证客户端的接受能力。
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1。
发送完毕后,客户端进入
ESTABLISHED
状态,当服务器端接收到这个包时,也进入ESTABLISHED
状态,TCP 握手结束。
四次挥手:
-
第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入
FIN_WAIT_1
状态。 -
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入
CLOSE_WAIT
状态,客户端接收到这个确认包之后,进入FIN_WAIT_2
状态,等待服务器端关闭连接。 -
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入
LAST_ACK
状态,等待来自客户端的最后一个ACK。 -
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入
TIME_WAIT
状态,等待可能出现的要求重传的 ACK 包。服务器端接收到这个确认包之后,关闭连接,进入
CLOSED
状态。客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入
CLOSED
状态。