前端必须要知道的http知识.

## 前言 

 

>众所周知,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
我的宗旨是:没有华而不实的边缘知识,只有能够用上的实用功能!
扫描二维码添加

前端必须要知道的http知识._第1张图片

你可能感兴趣的:(http)