## 前言
>众所周知,http是初中级钱端就需要了解和掌握的协议,前端进行的请求基本都离不开http请求,http也是面试时离不开的内容。所以今天来整理一些前端必须了解和掌握的http协议的内容。希望能对大家有所帮助。
## 什么是http
一句话概括: http是一个应用层的无状态的超文本传输协议。
特点
1. 无连接:限制每次连接只处理一个请求,处理完客户的请求,并收到客户的应答后,即断开连接。
2. 媒体独立:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。
3. 无状态:协议对于事务处理没有记忆能力。
## 输入URL后,浏览器都做了什么
最常见的http面试题,常用来切入http题目。答案不唯一。
当我们输入url,点击回车之后,浏览器都偷偷为我们做了什么呢?
>1. NDS域名解析
2. 建立TCP连接
3. 发起HTTP请求(request)
4. 接收响应结果(response)
5. 浏览器解析html
6. 浏览器渲染布局
## http1.1
### tcp3次握手
大部分资料说的是http的三次握手,其实是tcp的三次握手,了解tcp的三次握手是理解http的基础。
**作用:3次握手用于开启tcp连接。**
![tcp三次握手](https://user-gold-cdn.xitu.io/2019/8/26/16ccde52c611f5b8?w=958&h=653&f=png&s=40555)
用大白话来说,就是
>客户端:你好,我是客户端A,我来请求了。
服务器:你好,我是服务器,来吧。
客户端:好的我来了。
当然,上面的只是便于理解用的,真正面试官问起来肯定不能那么答,那么就结合上面的图来理解三次握手。
**第一次握手**:客户端发送**SYN(请求标记)和Seq(序号值)**,告诉服务器我要来请求了,然后客户端进入待定状态。
**第二次握手**:服务器如果接收到信息,就会开启一个tcp的socket端口。并返回客户端**ACK(确认标记)**,ACK的值等于Seq的值+1,用来确保信息已传达。还会返回SYN和另个Seq。
**第三次握手**:客户端接收到服务器返回的消息,知道服务器已经允许接收请求。客户端再返回一个ACK和Seq,告诉客户端我要开始发送数据了。另外,**第三次握手是有超时时间**,如果第二次握手没有返回消息,或者返回超时,会直接返回信息,告诉服务器连接失败,请直接关闭socket连接。
**注:**
**ACK : TCP协议规定,只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1**
**SYN : 在连接建立时用来同步序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1. 因此, SYN置1就表示这是一个连接请求或连接接受报文。**
### 三次握手的意义
回答完这些之后,面试官可能会再问你:为什么要设置那么麻烦的三次握手呢?**三次握手的意义是什么**。
引用谢希仁著《计算机网络》第四版中的话
>为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
感觉不结合例子,理论就会变得很苍白,他还举了一个典型例子。
>“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。”
**一句话概括就是:防止服务器端一直等待服务器的消息,一直开着tcp端口,浪费资源。**
### tcp4次挥手
**作用:用于关闭tcp连接。**
![tcp4次挥手](https://user-gold-cdn.xitu.io/2019/8/26/16ccde52e1dfb222?w=1057&h=832&f=png&s=64567)
**特别注意:断开tcp连接可以是服务端,也可以是客户端**
以下以客户端断开连接为例。
大白话版:
>客户端:我想要断开请求
服务器:好,我知道了,等我先发送完数据
服务器:我发送完了,可以准备断开了
客户端:知道了,可以断开了
**第一次挥手**:客户端发送FIN请求,请求关闭tcp连接。客户端进入FIN-WAIT状态
**第二次挥手**:服务器接收到请求,返回一个ACK和ack,ack=接收的seq+1。服务器知道了客户端的请求,但是要先等待数据发送完毕。所以返回告诉客户端说请我已知道请求,请先等待我发送完毕。服务器进入CLOSE-WAIT状态
**第三次挥手**:服务器数据发送完毕,发送FIN告诉客户端我数据已发送完毕,可以开始关闭tcp连接。服务器进入LAST-ACK状态。
**第四次挥手**:客户端接收到断开信息,向服务器发送发送ACK,进入TIME-WAIT状态。服务器接收到ACK后,关闭连接。客服端等待了2MSL后(后文会提到),没有收到回复,说明服务器已关闭,客户端也关闭连接。
### 为什么是2MSL
>MSL:Maximum Segment Lifetime,最大分段寿命。
作用:
1.保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
2.防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。
### 4次挥手的意义
为什么要四次挥手而不是三次?
>建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,**所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接**,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
## 常见状态码
https://juejin.im/post/5cde9b83f265da1ba431bbf4
## http头部
http报文,是由方法、URI、HTTP版本和**HTTP首部**等字段构成。
操作http,其实更多的是通过操作http协议的头部,接下来我们先来将一些必须知道的头部。
### CORS跨域
每个开发者肯定会遇到的问题:如何跨域?
1. JSONP跨域
2. image标签跨域
3. **CORS跨域**
因为本文主讲http,所以只将CORS跨域方法。
首先来了解一下什么是同源策略。
mdn官方讲解:[https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy)
简单讲解:我需要证明这个请求是不是和我是一个妈生的,确保信息的安全,不被不法人员窃取。
所以我们需要一种方法来验证我们是同源的。这里就要引出一个http头部:**Access-Control-Allow-Origin**
让**后端**在返回的http报文首部中加入这个头部`Access-Control-Allow-Origin: *`即可实现跨域。
```
response.writeHead(200,{
'Content-Type':'text/html',
'Access-Control-Allow-Origin':'*'
})
```
**但是!**这是不安全的跨域,只加上这个头部,说明这个**资源是公共的**,也就是说谁都可以用http来请求这个资源,一些敏感资源我们不能只使用这个头部来进行跨域。
那么,如何进行安全跨域呢?
安全跨域需要分为两种:**简单请求和非简单请求**
#### 简单请求
简单请求有两大条件:
(1) 请求方法是以下三种方法之一:
1. HEAD
2. GET
3. POST
(2) HTTP的头信息不超出以下几种字段:
1. Accept
2. Accept-Language
3. Content-Language
4. Last-Event-ID
5. Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
满足以上两个条件之后,我们只需要把*允许请求的域名即可,如:
```
response.writeHead(200,{
'Content-Type':'text/html',
'Access-Control-Allow-Origin':'http://127.0.0.1:8888'
})
```
以上代码说明允许IP 127.0.0.1 来跨域。
#### 非简单请求
非简单请求比较复杂,需要前后端配合。
先看后端代码(node.js):
```
response.writeHead(200,{
'Content-Type':'text/html',
'Access-Control-Allow-Origin':'http://127.0.0.1:8888',
'Access-Control-Allow-Headers':'X-Test-Cors',
'Access-Control-Allow-Methods':'PUT,POST,Delete',
'Access-Control-Max-Age':'1000',
})
```
这里又出现了三个新头部。
1. Access-Control-Allow-Headers ---- 设置自定义头部
2. Access-Control-Allow-Methods ---- 设置允许的请求方法
3. Access-Control-Max-Age ---- 请求验证时间
简单来说,复杂请求需要我们使用自定义头部来验证是够需要跨域,对比一下前端代码。
```
fetch('http://127.0.0.1:8888',{
method:'POST',
headers:{
// 设置和后端对应的自定义头部
'X-Test-Cors': '123'
}
})
```
即可实现安全跨域。
这里还有个**预请求**的概念。
**当你的请求时一个复杂请求时,浏览器会先发送一个预请求,若返回204,则说明你的请求被允许,然后会发出正式请求,返回200。**
### Content-Type
代表内容的媒体类型和编码格式。最常用的如:
`Content-Type: text/html; charset=utf8` 表示返回的信息时html,编码方式是utf8
### Cache-Control
缓存头,支持多个指令,用逗号隔开。
常用指令:
1. max-age=20 --- 缓存20s
2. s-max-age=20 --- 设置代理缓存20s(若同时设置max-age,浏览器优先使用s-max-age)
3. private --- 表明响应只能被单个用户(浏览器)缓存,不能作为共享缓存(即代理服务器不能缓存它)。
4. public --- 表明响应可以被任何对象缓存
5. no-store --- 不能够被任何人缓存
6. no-cache --- 强制不经过缓存,直接请求服务器进行验证,若服务器告知缓存有效,再使用缓存(注意不要和no-store混淆)
### Vary
Vary用来设置指定的头信息的值相同时才进行缓存。
挺抽象的,举个例子:
后端代码
```
response.writeHead(200,{
'Content-Type':'text/html',
'Vary': 'X-Test-Cache'
})
```
前端代码
```
fetch('http://127.0.0.1:8887',{
method:'POST',
// 每次请求时X-Test-Cache都不变才能缓存
headers:{
'X-Test-Cache': 1
//'X-Test-Cache': 1
}
})
```
只有在每次请求时X-Test-Cache都不变才能缓存,若第一次X-Test-Cache=1,第二次为X-Test-Cache=2,则不会缓存。
使用场景: 若我们需要根据不同语言来进行缓存,若语言不同则不缓存,就需要设置`Vary: 'Content-Language'`
## https
首先,我们知道http是不安全的,原因是因为它是使用明文传输的,所以衍生了https。
### TLS加密
安全传输层协议(TLS)用于在两个通信应用程序之间提供保密性和数据完整性。
在 TLS 中使用了两种加密技术,分别为:对称加密和非对称加密。
### 非对称加密
推荐文章:https://www.zhihu.com/question/33645891
简而言之就是有一个**公钥**和**私钥**。公钥谁都可以查阅,使用公钥进行数据加密,只有用私钥能进行解密。
### https原理
HTTPS使用非对称加密传输对称加密需要的秘钥。再通过对称加密的秘钥进行数据加密和传输。
**特点:很安全,但是耗时。**
### 对称加密
双方都使用同个秘钥进行加密和解密。
**特点:在秘钥不被第三方获取的前提下,安全且耗时少**
### https原理
在了解了对称加密和非对称加密的特点后,知道https的加密方式就是把两者相结合。
**HTTPS使用非对称加密传输对称加密需要的秘钥,再通过对称加密的秘钥进行数据加密和传输。**
如果看不懂上面这句话,看图
![https握手](https://user-gold-cdn.xitu.io/2019/8/26/16ccde531e676d23?w=1104&h=899&f=png&s=53532)
1. 客户端生成一个随机数,将**随机数和加密套件**传输给服务端。服务端进行储存
2. 服务端生成一个随机数,将**随机数和公钥**传输给客户端。客户端进行储存
3. 客户端验证证书之后,**再生成一个随机数(预主秘钥)**,并使用公钥进行加密,传输加密后的预主秘钥。
4. 服务端使用私钥(自带的)进行解密,获得预主秘钥。那么双方都已经有了三个随机数。通过加密套件生成一个主秘钥。
5. 使用主秘钥进行**对称加密**和传输。
由此可见,**https前半部分是非对称加密,获得主秘钥后使对称加密传输。**
---
参考连接:
通俗大白话来理解TCP协议的三次握手和四次分手: [https://github.com/jawil/blog/issues/14](https://github.com/jawil/blog/issues/14 "通俗大白话来理解TCP协议的三次握手和四次分手")
TCP的三次握手与四次挥手: [https://blog.csdn.net/qzcsu/article/details/72861891](https://blog.csdn.net/qzcsu/article/details/72861891 "TCP的三次握手与四次挥手")
菜鸟教程:[https://www.runoob.com/http/http-intro.html](https://www.runoob.com/http/http-intro.html)
欢迎关注我的新公众号 前端stack
我的宗旨是:没有华而不实的边缘知识,只有能够用上的实用功能!
扫描二维码添加