应用层协议定义了应用进程间的交互和通信规则,不同主机的应用进程间如何相互传递报文,比如传递的报文的类型、格式、 有哪些字段等等
一、HTTP 协议
1. 概况
HTTP (HyperText Transfer Protocol) 是超文本传输协议,是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」
它定义了客户端和服务器之间交换报文的格式和方式,默认使用 80 端口。
它使用 TCP 作为传输层协议,保证了数据传输的可靠性
「文本」,在互联网早期的时候只是简单的字符文字 但现在「文本」的涵义已经可以扩展为图片、视频、压缩包等,在 HTTP 眼里这些都算做「文本」
「超文本」,它就是超越了普通文本的文本,它是文字、图片、视频等的混合体最关键有超链接,能从一个超文本跳转到另外一个超文本。
HTTP 是一个无状态的协议,HTTP 服务器不会保存关于客户的任何信息
HTTP 有两种连接模式
- 非持续连接
- 指的是服务器必须为每一个请求的对象建立和维护 一个全新的连接
- HTTP1.0 以前使用的非持续的连接,但是可以在请求时,加上
Connection: keep-a live
来要求服务器不要关闭 TCP 连接
- HTTP1.0 以前使用的非持续的连接,但是可以在请求时,加上
- 指的是服务器必须为每一个请求的对象建立和维护 一个全新的连接
- 持续连接
- TCP 连接默认不关闭,可以被多个请求复用
- HTTP1.1 以后默认采用的是持续的连接。目前对于同一个域,大多数浏览器支持同时建立 6 个持久连接
- 优点:可以避免每次建立 TCP 连接三次握手时所花费的时间
- TCP 连接默认不关闭,可以被多个请求复用
HTTP报文有两种
- HTTP请求报文
- HTTP响应报文
2. HTTP 请求报文
HTTP 请求报文格式如下
GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*
图:HTTP请求报文的组成
HTTP 请求报文的组成
- 请求行
- 其包含三个字段:方法字段、URL 字段和 HTTP 版本字段
- 首部行(请求头部)
- 空行(回车符+换行符),这个空行不能省略,它用来划分首部与实体
- 实体主体(请求数据)
3. HTTP 响应报文
- HTTP 响应报文格式如下 ```http HTTP/1.0 200 OK Content-Type: text/plain Content-Length: 137582 Expires: Thu, 05 Dec 1997 16:00:00 GMT Last-Modified: Wed, 5 August 1996 15:55:28 GMT Server: Apache 0.84
Hello World
```
图:HTTP相应报文组成
- HTTP 响应报文的组成
- 状态行
- 其包含三个字段:HTTP 协议版本字段、状态码、状态码的描述文本
- 状态码
- 1xx:提示信息、表示目前是协议处理的中间状态,还需要后续的操作
- 2xx:成功,报文已经收到并被正确处理
- 3xx:重定向,资源位置发生变动,需要客户端重新发送请求
- 4xx:客户端错误,请求报文有误,服务器无法处理
- 5xx:服务器错误,服务器在处理请求时内部发送了错误
- 状态码
- 其包含三个字段:HTTP 协议版本字段、状态码、状态码的描述文本
- 首部行
(响应头部)
- 空行
(回车符+换行符)
,这个空行不能省略,它用来划分首部与实体 - 实体主体
(响应数据)
- 状态行
4. 说说状态码
1xx
- 属于提示信息,是协议处理中的一种中间状态,实际用到的比较少
2xx
- 表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态
- 「200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据
- 「204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据
- 「206 Partial Content」是应用于 HTTP 分块下载或断电续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态
- 表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态
3xx
- 表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向
- 「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问
- 301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL
- 「302 Moved Permanently」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问
- 「304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,用于缓存控制
- 「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问
- 表示客户端请求的资源发送了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向
4xx
- 表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义
- 「400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误
- 「403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错
- 「404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端
- 表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义
5xx
- 表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码
- 「500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道
- 「501 Not Implemented」表示客户端请求的功能还不支持,类似『即将开业,敬请期待』的意思
- 「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误
- 「503 Service Unavailable」表示服务器当前很忙,暂时无法响应服务器,类似『网络服务正忙,请稍后重试』的意思
5. HTTP/1.0 和 HTTP/1.1
5.1 HTTP/1.0 和 HTTP/1.1的区别
早期 HTTP/1.0 性能上的一个很大的问题,那就是每发起一个请求,都要新建一次 TCP 连接(三次握手),而且是串行请求,TCP 需要重新建立连接和断开,增加了通信开销
- 表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码
为了解决上述 TCP 连接问题,HTTP/1.1 提出了 长连接 的通信方式,也叫 持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。
持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态
图:短连接和长连接
5.2 管道网络传输
HTTP/1.1 采用了 长连接 的方式,发展出了 管道网络传输,即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以 减少整体的响应时间
举例来说,客户端需要请求两个资源。以前的做法是,在同一个TCP连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。管道机制则是允许浏览器同时发出 A 请求和 B 请求,但是服务器还是按照 顺序 ,先回应 A 请求,完成后再回应 B 请求。
图:管道网络传输
这就引出 HTTP/1.1 的性能问题:要是前面的回应特别慢,后面就会有许多请求排队等着。这称为『队头阻塞』
5.2 队头阻塞
当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是 队头阻塞
图:队头阻塞
6. 说说首部行
报文首部包含了请求行、首部行。
首部行是由一个个字段组成的。
使用首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容
若 HTTP 首部字段重复了会如何? 这种情况在规范内尚未明确,根据浏览器内部处理逻辑的不同,结果可能并不一致。有些浏览器会优先处理第一次出现的首部字段,而有些则会优先处理最后出现的首部字段
6.1 HTTP 首部字段类型
HTTP 首部字段根据实际用途被分为以下 4 种类型:
6.1.1 通用首部字段
请求报文和响应报文两方都会使用的首部
6.1.2 请求首部字段
从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息
6.1.3 响应首部字段
从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息
6.1.4 实体首部字段
针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息
图:请求报文
图:响应报文
6.2 HTTP 首部字段结构
HTTP 首部字段是由首部字段名和字段值构成的,中间用冒号 :
分隔。若有多个字段值,通过 ,
分隔
Cache-Control: private, max-age=0, no-cache
6.3 HTTP/1.1 首部字段
HTTP/1.1 规范定义了如下 47 种首部字段
首部字段名 | 字段值 | 说明 |
---|---|---|
Cache-Control | 通用首部字段的值的详细介绍 | 控制缓存行为 |
Connection | (1) 控制不再转发给代理的首部字段 (2) 管理持久连接 |
|
Date | 创建报文的日期 | |
Pragma | 历史遗留字段,只用在客户端发送的请求中。 客户端会要求所有的中间服务器不返回缓存的资源 |
|
Upgrade | 升级为其他协议 | |
via | 代理服务器的相关信息 | |
Wraning | 错误和警告通知 | |
Trailer | 事先说明在报文主体后记录了哪些首部字段 | |
Transfor-Encoding | 规定了传输报文主体时采用的编码方式 |
表1:通用首部字段
首部字段名 | 字段值 | 说明 |
---|---|---|
Accept | 请求首部字段的值的详细介绍 | 客户端或者代理能够处理的媒体类型 |
Accept-Encoding | 优先可处理的编码格式 | |
Accept-Language | 优先可处理的自然语言 | |
Accept-Charset | 优先可以处理的字符集 | |
Authorization | web的认证信息 | |
If-Modified-Since | 比较资源更新时间(Last-Modified) | |
User-Agent | 客户端程序信息 | |
Referer | 对请求中 URI 的原始获取方 | |
Host | 请求资源所在服务器 | |
Proxy-Authorization | 代理服务器要求客户端的认证信息 | |
Max-Forwards | 最大传输逐跳数 | |
From | 用户的邮箱地址 | |
Expect | 期待服务器的特定行为 | |
Range | 实体的字节范围请求 |
表2:请求首部字段
首部字段名 | 字段值 | 说明 |
---|---|---|
Accept-Ranges | 响应首部字段的值的详细介绍 | 能接受的字节范围 |
Location | 令客户端重定向的URI | |
Age | 告知客户端,源服务器在多久前创建了响应 | |
vary | 代理服务器的缓存信息 | |
ETag | 能够表示资源唯一资源的字符串 | |
Server | 服务器的信息 | |
Proxy-Authenticate | 代理服务器要求客户端的验证信息 | |
WWW-Authenticate | 服务器要求客户端的验证信息 | |
Retry-After | 告知客户端应该在多久之后再次发送请求 |
表3:响应首部字段
首部字段名 | 字段值 | 说明 |
---|---|---|
Allow | 实体首部字段的值的详细介绍 | 资源可支持http请求的方法 |
Content-Language | 实体的资源语言 | |
Content-Encoding | 实体的编码格式 | |
Content-Length | 实体的大小 | |
Content-Type | 实体媒体类型 | |
Content-MD5 | 实体报文的摘要 | |
Content-Ranges | 告知客户端作为响应返回的实体的哪个部分符合范围请求 | |
Last-Modified | 资源最终修改的时间 | |
Expires | 资源失效的日期告知客户端 |
表4:实体首部字段
二、HTTP/2 协议
2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。
这个协议在 Chrome 浏览器上证明可行以后,就被当作 HTTP/2 的基础,主要特性都在 HTTP/2 之中得到继承。2015 年,HTTP/2 发布。
1. HTTP/2 的特性
1.1 二进制协议
HTTP/2 是一个二进制协议。
在 HTTP/1.1 版中,报文的头信息必须是文本(ASCII 编码),数据体可以是文本,也可以是二进制。
在 HTTP/2 是一个彻底的二进制协议,头信息和数据体都是二进制,并且统称为『帧』,可以分为头信息帧和数据帧。
帧的概念是它实现多路复用的基础
图:报文区别
1.2 多路复用
HTTP/2 实现了多路复用,HTTP/2 仍然复用 TCP 连接,但是在一个连接里,客户端和服务器都可以同时发送多个请求或回应,而且不用按照顺序一一发送,这样就避免了『队头堵塞』的问题
图:多路复用
1.3 数据流
HTTP/2 使用了数据流的概念,因为 HTTP/2 的数据包是不按顺序发送的,同一个连接里面连续的数据包,可能属于不同的请求。因此,必须要对数据包做标记,指出它属于哪个请求。
HTTP/2 将每个请求或回应的所有数据包,称为一个数据流。每个数据流都有一个独一无二的编号。数据包发送的时候,都必须标记数据流 ID ,用来区分它属于哪个数据流
每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数
客户端还可以 指定数据流的优先级。优先级高的请求,服务器就先响应该请求
1.4 头信息压缩
HTTP/2 实现了头信息压缩,由于 HTTP 1.1 协议不带有状态,每次请求都必须附上所有信息。所以,请求的很多字段都是重复的,比如 Cookie 和 User Agent ,一模一样的内容,每次请求都必须附带,这会浪费很多带宽,也影响速度
HTTP/2 对这一点做了优化,引入了头信息压缩机制。如果你同时发出多个请求,他们的头是一样的或是相似的,那么,HTTP/2 协议会帮你 消除重复的部分
- 头信息使用 gzip 或 compress 压缩后再发送
- 客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就能提高速度了 —— 这就是所谓的 HPACK 算法
1.5 服务器推送
HTTP/2 还在一定程度上改善了传统的『请求 - 应答』工作模式,服务不再是被动地响应,也可以 主动 向客户端发送消息
举例来说,在浏览器刚请求 HTML 的时候,就提前把可能会用到的 JS、CSS 文件等静态资源主动发给客户端,减少延时的等待,也就是服务器推送(Server Push,也叫 Cache Push)
2. HTTP/2 的缺陷
HTTP/2 主要的缺陷在于:多个 HTTP 请求在复用一个 TCP 连接,下层的 TCP 协议是不知道有多少个 HTTP 请求的。
所以一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的 所有的 HTTP 请求都必须等待这个丢了的包被重传回来
3. HTTP/3 协议
HTTP/2 问题的根源:是基于 TCP 传输层的出错重传而出现的问题。
HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP,UDP 发生是不管顺序,也不管丢包的,所以不会出现 HTTP/1.1 的『队头阻塞』 和 HTTP/2 的一个『丢包全部重传』问题
图:HTTP/1.1 ~ HTTP/3
总所周知,UDP 是不可靠传输的
- QUIC 协议 是一个在 UDP 之上的 伪 TCP + TLS + HTTP/2 的多路复用的协议,使得 UDP 可以实现类似 TCP 的可靠性传输
- QUIC协议 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响
- HTTPS 要建立一个连接,要花费 6 次交互,先是建立三次握手,然后是 TLS/1.3 的三次握手。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互 合并成了 3 次,减少了交互次数
另外,TLS 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPack
三、HTTPS协议
四、DNS协议
零碎知识
1. Get 请求和 POST 请求的区别
与协议相关的
GET 请求的参数放在 URL 中,而 POST 请求的参数放在请求体中
按照 HTTP 协议要求,GET 请求的参数,应该拼接在报文的 URL 上 按照 HTTP 协议要求,POST 请求的参数,放在请求体中
这并不意味着 GET 请求没有请求体,如果用 node 的 net 模块获取 HTTP GET 请求的请求体,如果有,可以拿到的。
每个浏览器 XMLHttpRequest 实现不一样,GET 请求报文 URL 最大长度限制就不一样
GET 请求只能进行 URL 编码,而 POST 请求支持多种编码方式
- GET 产生一个 TCP 数据包,POST 产生两个 TCP 数据包
对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200 而对于 POST,浏览器先发送 header,服务器响应100 continue,浏览器再发送 data,服务器响应200
与浏览器相关的
- GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会。
缓存失效前,发起同一个 GET 请求(报文相同),浏览器会从缓存中获取结果,直接返回。 解决方式:
xhr.open(url)
时,给 url 添加随机数参数