HTTP 变更历史
HTTP1.0
请求1:
建立TCP连接:三次握手
浏览器发HTTP请求
服务器发HTTP响应
断开TCP连接:四次挥手
请求2:
建立TCP连接:三次握手
浏览器发HTTP请求
服务器发HTTP响应
断开TCP连接:四次挥手
HTTP 1.1
- 连接可以复用:建立一次连接,发送多个请求和响应。(也是有限制数量的6-8个,有阻塞)
建立TCP连接:三次握手
浏览器发HTTP请求a
服务器发HTTP响应a
浏览器发HTTP请求b
浏览器发HTTP请求c
服务器发HTTP响应b
服务器发HTTP响应c
断开TCP连接:四次挥手
- 也可以同时发送请求b,c,下一个请求不用等到上一个响应来了之后再发送
- 缓存控制:用cache-control
HTTP 2.0
- 多路复用:只需一个TCP连接,可以无限制处理多个请求,真正实现请求的并发。
- 二进制分帧:将HTTP报文从字符串拆分为若干个最小的二进制帧组成,在另一端再重组形成一个完整的请求/响应,多个帧之间乱序发送,根据真头部的标识重新组装。
- 压缩HTTP首部:请求/响应的首部太大了,压缩后再发送,避免浪费
- 服务器推送:客户端可以获取服务器端推送的资源。
一、HTTP 缓存
HTTP的缓存分为缓存(强缓存)和内容协商(弱缓存)
缓存(强缓存)
强缓存是本地缓存,指浏览器不会发出请求,直接从本地缓存中读取。控制强缓存的两个字段:Expires和cache-control
- 在HTTP1.0时代,设置
Expires,这是绝对时间,如果发送请求在Expires之前,缓存始终有效,不会去服务器请求。 - 在HTTP1.1时代,使用
cached-control:max-age = number,这是相对时间,如果在相对时间内,那么就不需要去服务端请求,直接从缓存中拿
内容协商(弱缓存)
协商缓存需要在服务器端对比资源是否修改,来判断是否可以使用缓存。若未改动,则返回304状态码,浏览器拿到此状态码就可以直接使用缓存数据了。否则服务器返回新资源。
控制协商缓存的两个字段是:Last-Modify/If-Modified-Since 和 Etag/If-None-Match,
Last-Modified:最后修改时间
当浏览器再次进行请求时,会向服务器传送 If-Modified-Since 报头,询问 Last-Modified 时间点之后资源是否被修改过。如果没有修改,则返回码为 304,使用缓存;如果修改过,则再次去服务器请求资源,返回码和首次请求相同为 200,资源为服务器最新资源。
ETag:
服务器根据实体内容生成一段 hash 字符串,标识资源的状态。浏览器会将这串字符串传回服务器,验证资源是否已经修改,如果没有修改,就返回304, 过程如下
二、HTTP 状态码
- 2xx 表示成功
- 3xx 表示需要进一步操作
- 4xx 表示浏览器方面出错
- 5xx 表示服务器方面出错
| 状态码 | 英文释义 | 中文解释 |
|---|---|---|
| 100 | Continue | 临时响应 |
| 200 | Ok | 请求成功 |
| 301 | Moved Permanently | 永久重定向 |
| 302 | Found | 临时重定向 |
| 304 | Not Modified | 资源未更改过。缓存机制里有 |
| 400 | Bad Request | 请求参数错误 |
| 403 | Forbidden | 请求身份错误,禁止访问 |
| 404 | Not Found | 请求失败,找不到网页 |
| 405 | Method Not Allowed | 请求方法错误 |
| 500 | Internal Server Error | 服务器遇到未知错误 |
| 502 | Bad Gateway | 从上游服务器遇到无效响应 |
三、HTTP 和 HTTPS 的区别有哪些?
- HTTP(hypertext transfer protocol)超文本传输协议
- HTTPS(secure hypertext transfer protocol)安全文本传输协议
- HTTPS = HTTP + SSL / TLS(安全层)
| HTTP | HTTPs | |
|---|---|---|
| 中文释义 | 超文本传输协议 | 安全文本传输协议 |
| 安全性 | 明文传输,不安全 | 加密传输,安全 |
| 端口号 | 80 | 443 |
| 速度 | 较快 | 较慢 |
| 是否需要证书 | 不需要 | 需要 免费/购买 |
四、HTTP报文
分为请求报文、响应报文
请求报文(四部分组成)
- 请求行,请求方法字段 URL字段 HTTP协议版本,如
GET /data/info.html HTTP/1.1 - 请求报头:
Accept,Cache-Control,Cookie,Connection等字段 - 空行
- 请求数据:若方法字段是GET,则此项为空,没有数据;若方法字段是POST,则通常来说此处放置的就是要提交的数据
POST /auth/login HTTP/1.1 //当前HTTP版本
Host: blog-server.hunger-valley.com
Connection: keep-alive //长链接
Content-Length: 41 //数据长度41
Accept: application/json, text/plain, //希望是json的格式
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2Vybm
User-Agent: Mozilla/5.0
Content-Type: application/json;charset=UTF-8 //内容是json格式,utf-8
Origin: https://xulei1998.github.io //请求源
Referer: https://xulei1998.github.io/
Accept-Encoding: gzip, deflate, br //压缩方式
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
{"username":"hunger","password":"123456"}
响应报文(四部分组成)
- 响应行:HTTP协议版本 状态码及其描述,如
HTTP/1.1 200 OK - 响应报头:
Server,Connection,Expires,Last-Modified - 空行
- 响应报文:如果请求的是HTML/CSS/JS,那么返回的就是HTML/CSS/JS代码。
HTTP/1.1 200 OK //状态码
Server: nginx/1.4.6 (Ubuntu) //服务器
Date: Wed, 20 Jan 2021 07:28:09 GMT //时间
Content-Type: application/json; charset=utf-8
Content-Length: 406 //内容长度
Connection: keep-alive
X-Powered-By: Express
Access-Control-Allow-Origin: * //访问控制允许源
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
ETag: W/"196-Ay8U/71Rt0EbDzvYIuK2YtXe7xE"
{"status":"ok","msg":"登录成功","data":{"id":1,"username":"hunger","avatar":"https://avatars.dicebear.com/api/human/hunger.svg?mood[]=happy","createdAt":"2020-09-17T03:03:55.803Z","updatedAt":"2020-09-17T03:03:55.803Z"},"token":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2Vybm"}
五、应用层协议 VS 传输层协议
- 应用层 HTTP 考虑接收到数据的内容格式,不考虑是否这次传输可达
- 传输层 TCP 只保证这次传输是可靠的,不关注后续的传输数据内容,UDP 不保证传输的可靠性
OSI 七层模型
- 物理层
- 数据链路层
- 网络层
- 传输层 TCP / UDP
- 表示层
- 会话层
- 应用层 HTTP
TCP/IP 四层模型
- 网络接口层
- 网络层
- 传输层
- 应用层
六、TCP协议
TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层协议。与之同层的是UDP(用户数据报协议),是不可靠的传输层协议。(HTTP是应用层协议)
目的:让网络传输更加可靠
TCP报文格式
在TCP报文段的TCP首部,有三个值得注意的字段:
-
ACK:应答报文0/1,TCP协议规定,只有ACK=1时有效,也就是连接建立后所有发送的报文的ACK必须为1。
-
SYN:请求报文, 在连接建立时用来同步序号。SYN=1就表示这是一个连接请求或连接接受报文。
- 当SYN=1,ACK=0时,表明这是一个连接请求报文(询问) 。
- 对方若同意建立连接,则应在响应报文(应答) 中使SYN=1和ACK=1。
-
FIN:释放请求报文,当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,想要关闭连接。
结合 序号seq 和 确认号ack 去理解:
- 询问报文:[ACK=0,SYN=1, seq=123] 询问号是123
- 应答+询问报文:[ACK=1,SYN=1, seq=234, ack=124] 对seq为123的报文进行应答。询问下一个报文,询问号是234
- 应答报文:[ACK=1, SYN=0, seq=124, ack=235] 对seq为234的报文进行应答
三次握手——建立TCP连接
可以想象成两人用对讲机在交谈。
A:Are you ok? (SYN=1,seq=100 )询问
B:I am fine. And you? (ACK=1, ack=101. SYN=1, seq=200) 应答+询问
A:Me too. (ACK=1, ack=201) 应答
客户端向服务器端发送数据之前,需要建立连接,经历三次握手:
- 客户端发送 SYN=1的询问报文给服务器端,seq是x,进入 SYN_SENT 状态(请求已发送)。
- 服务器端收到报文,回应一个ACK=1、SYN=1 的应答+询问报文。应答号ack是x+1,询问号seq是y,进入 SYN_RCVD 状态(请求已接收)。
- 客户端收到询问后,回应一个 ACK=1的应答报文,应答号是y+1,进入 Established 状态(建立连接)。
为什么要三次握手?而不是两次?
假设只有两次握手:客户端—>发送请求—>服务器端。服务器端—>返回应答—>客户端
- 数据重传:客户端发送请求报文A,因网络延时服务器端没收到。客户端又发了一遍报文A,服务器收到后应答,建立链接等待客户端发送数据。客户端正常发送数据。 过了一会第一次因网络延迟发送的报文A也到达服务器,服务器再次应答,建立链接等待客户端发送数据,而客户端因已经接收过应答而不再传送数据,服务器端会一直处于“建立好连接,等待传数据的阶段”会浪费大量的服务器资源。
- 而三次握手就会避免这种情况的发生:客户端会在收到应答+询问之后,返回一个应答,这样即使第一次的报文A发送到服务器,服务器也已经知道自己的应答+询问已经被客户端接收,从而不再继续等待,节省了资源。
四次挥手——释放TCP连接
- 客户端发送一个 FIN 报文,告诉服务器想关闭连接。
- 服务器收到这个 FIN ,先发回一个 ACK报文,说我收到了。
- 服务器发送一个 FIN 给客户端,我也想关闭连接。(服务器通知应用程序关闭网络连接,应用程序关闭后通知服务器。)
- 客户端发回 ACK 报文确认。
为什么挥手要四步?而不是2 3合并?
2 3步骤里的ACK报文和FIN报文需要分开发送,因为中间服务器端还可能向客户端发送数据,客户端已经想关闭连接,不再发送数据,但还可以接收数据。
从输入URL到看到页面发生了什么?
- DNS解析域名
- 发起TCP连接(三次握手)
- 发送HTTP请求(请求体,请求报头,请求正文)
- 服务器处理请求并返回HTTP报文(状态码,响应报头,响应报文)
- 断开tcp连接(四次挥手)
- 浏览器解析、渲染页面
1 DNS解析域名
输入www.google.com网址后,首先在本地的域名服务器中查找,没找到去根域名服务器.去查找,没有再去.com顶级域名服务器查找,如此的类推下去,直到找到IP地址,然后把它记录在本地,供下次使用。大致过程就是.-> .com ->google.com. -> www.google.com.。
2 发起TCP连接(三次握手)
三次握手的过程
3 发送HTTP请求(浏览器->服务器)
请求报文的组成:请求行(GET和POST区别)、请求头、请求正文
HTTP缓存(在):强缓存、协商缓存
4 服务器处理请求并返回HTTP报文(服务器->浏览器)
状态码
5 断开tcp连接(四次挥手)
6 浏览器解析渲染页面
- 解析HTML形成DOM树
- 解析CSS形成CSSOM 树
- 合并DOM树和CSSOM树形成render tree渲染树
- 浏览器开始渲染布局
- 并绘制页面
回流:当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸、位置、内容、字体大小变化、添加或者删除可见的DOM元素
- 查询某些属性或调用某些方法
重绘:当页面中元素color、background color的改变并不影响它在文档流中的位置时。
优化
CSS
- 尽可能在DOM树的最末端改变class。
- 避免设置多层内联样式。
- 给动画元素使用绝对定位可以减小reflow,使它脱离文档流(浏览器只会计算这个元素本身位置的变化,小范围计算)
JavaScript
- 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。
- 避免频繁操作DOM,DOM离线后修改。(对虚拟DOM不断修改,再一次性渲染到真实DOM上)