前端必须要知道的http知识.
前言
众所周知,http是初中级钱端就需要了解和掌握的协议,前端进行的请求基本都离不开http请求,http也是面试时离不开的内容。所以今天来整理一些前端必须了解和掌握的http协议的内容。希望能对大家有所帮助。
什么是http
一句话概括: http是一个应用层的无状态的超文本传输协议。
特点
- 无连接:限制每次连接只处理一个请求,处理完客户的请求,并收到客户的应答后,即断开连接。
- 媒体独立:只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。
- 无状态:协议对于事务处理没有记忆能力。
输入URL后,浏览器都做了什么
最常见的http面试题,常用来切入http题目。答案不唯一。
当我们输入url,点击回车之后,浏览器都偷偷为我们做了什么呢?
- NDS域名解析
- 建立TCP连接
- 发起HTTP请求(request)
- 接收响应结果(response)
- 浏览器解析html
- 浏览器渲染布局
http1.1
tcp3次握手
大部分资料说的是http的三次握手,其实是tcp的三次握手,了解tcp的三次握手是理解http的基础。
作用:3次握手用于开启tcp连接。
用大白话来说,就是
客户端:你好,我是客户端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连接。
以下以客户端断开连接为例。
大白话版:
客户端:我想要断开请求
服务器:好,我知道了,等我先发送完数据
服务器:我发送完了,可以准备断开了
客户端:知道了,可以断开了
第一次挥手:客户端发送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一般都会分开发送,从而导致多了一次。
常见状态码
http头部
http报文,是由方法、URI、HTTP版本和HTTP首部等字段构成。
操作http,其实更多的是通过操作http协议的头部,接下来我们先来将一些必须知道的头部。
CORS跨域
每个开发者肯定会遇到的问题:如何跨域?
- JSONP跨域
- image标签跨域
- CORS跨域
因为本文主讲http,所以只将CORS跨域方法。
首先来了解一下什么是同源策略。
mdn官方讲解:developer.mozilla.org/zh-CN/docs/…
简单讲解:我需要证明这个请求是不是和我是一个妈生的,确保信息的安全,不被不法人员窃取。
所以我们需要一种方法来验证我们是同源的。这里就要引出一个http头部:Access-Control-Allow-Origin
让后端在返回的http报文首部中加入这个头部Access-Control-Allow-Origin: *即可实现跨域。
response.writeHead(200,{
'Content-Type':'text/html',
'Access-Control-Allow-Origin':'*'
})
但是!这是不安全的跨域,只加上这个头部,说明这个资源是公共的,也就是说谁都可以用http来请求这个资源,一些敏感资源我们不能只使用这个头部来进行跨域。
那么,如何进行安全跨域呢?
安全跨域需要分为两种:简单请求和非简单请求
简单请求
简单请求有两大条件:
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2) HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- 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',
})
这里又出现了三个新头部。
- Access-Control-Allow-Headers ---- 设置自定义头部
- Access-Control-Allow-Methods ---- 设置允许的请求方法
- 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
缓存头,支持多个指令,用逗号隔开。
常用指令:
- max-age=20 --- 缓存20s
- s-max-age=20 --- 设置代理缓存20s(若同时设置max-age,浏览器优先使用s-max-age)
- private --- 表明响应只能被单个用户(浏览器)缓存,不能作为共享缓存(即代理服务器不能缓存它)。
- public --- 表明响应可以被任何对象缓存
- no-store --- 不能够被任何人缓存
- 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 中使用了两种加密技术,分别为:对称加密和非对称加密。
非对称加密
推荐文章:www.zhihu.com/question/33…
简而言之就是有一个公钥和私钥。公钥谁都可以查阅,使用公钥进行数据加密,只有用私钥能进行解密。
https原理
HTTPS使用非对称加密传输对称加密需要的秘钥。再通过对称加密的秘钥进行数据加密和传输。
特点:很安全,但是耗时。
对称加密
双方都使用同个秘钥进行加密和解密。
特点:在秘钥不被第三方获取的前提下,安全且耗时少
https原理
在了解了对称加密和非对称加密的特点后,知道https的加密方式就是把两者相结合。
HTTPS使用非对称加密传输对称加密需要的秘钥,再通过对称加密的秘钥进行数据加密和传输。
如果看不懂上面这句话,看图
- 客户端生成一个随机数,将随机数和加密套件传输给服务端。服务端进行储存
- 服务端生成一个随机数,将随机数和公钥传输给客户端。客户端进行储存
- 客户端验证证书之后,再生成一个随机数(预主秘钥),并使用公钥进行加密,传输加密后的预主秘钥。
- 服务端使用私钥(自带的)进行解密,获得预主秘钥。那么双方都已经有了三个随机数。通过加密套件生成一个主秘钥。
- 使用主秘钥进行对称加密和传输。
由此可见,https前半部分是非对称加密,获得主秘钥后使对称加密传输。
参考连接:
通俗大白话来理解TCP协议的三次握手和四次分手: github.com/jawil/blog/…
TCP的三次握手与四次挥手: blog.csdn.net/qzcsu/artic…