HTTP全称为超文本传输协议(HyperText Transfer Protocol)。
超文本
如HTTP名字中所说的,其传输的是超文本,超文本从字面上理解是超过了普通的文本文字,也就是还包括文字以外的内容,如图片、视频、超链接等,超文本 就是这些内容的混合体。
HTML(Hyper Text Markup Language)就是最常见的超文本了,它本身只是纯文字的文件,但是内部用很多标签定义了图片、视频的链接,经过浏览器解释之后,呈现给我们的就是一个有文字、有图片和视频的网页了。
传输
所谓“传输”,就是将东西从一个地方搬到另一个地方。
HTTP协议是一个双向协议。
我们上网的时候,浏览器是请求方A,访问的网站就是应答方B,双方约定使用HTTP协议来通信,于是浏览器把请求数据发送给网站,网站再把一些数据返回给浏览器,最后由浏览器渲染出来显示在屏幕上。
虽然数据是在A点和B点传输,但是中间也允许有中转或接力。
这就好比传纸条,第一排的同学传到最后面一排的同学,传递的过程中就要经过好多同学(中间人),而在HTTP里,需要中间人遵从HTTP协议,只要不打扰基本的数据传输,就可以任意添加额外的东西。
协议
我们生活中的协议,本质上和计算机中的协议是相同的,协议的特点:
针对HTTP协议,我们可以这么理解,HTTP就是一个用在计算机世界里的协议,他是用计算机能够理解的语言建立了一种计算机之间交流通信的规范,以及相关的各种控制和错误处理方式(行为约定和规范)。
所以,总结以上说的三点,“超文本传输协议”就可以表述为:HTTP是一个用在计算机世界里专门在两点之间传输文字、图片、视频、音频等超文本数据的约定和规范。
那 HTTP是用于从互联网服务器传输到本地浏览器的协议,这种说法正确吗?
这种说法并不完全正确,也可以是从服务器传输到服务器的协议,所以用两点之间更加的准确。
1XX
不详细描述了,如上图所述。
2XX
这类状态码表示服务器成功处理了客户端的请求。
200 OK
是最常见的成功状态码,表示一切正常。如果是非HEAD
请求,服务器返回的响应头都会有body数据。204 No Content
也是一个比较常见的成功状态码,与200 OK基本相同,但是响应头没有body数据。206 Partial Content
是应用于HTTP分块下载或断点重传的,表示响应返回的body数据并不是资源的全部,而只是其中的一部分,也是服务器处理成功的状态。3XX
这类状态码表示客户端请求的资源发生了变动,需要客户端用新的URL重新发送请求获取资源,也就是重定向
301 Moved Permanently
表示永久重定向,说明请求的资源已经不存在了,需要用心的URL再次访问。
302 Found
表示临时的重定向,说明请求的资源还在,但是需要另一个URL来访问
301和302都会在响应头中使用Location
字段,指明后续需要跳转的URL,浏览器会自动重定向新的URL。
304 Not Modified
不具有跳转含义,表示资源未修改,重定向已存在的缓存文件,也称为缓存重定向,也就是告诉客户端缓存资源没有过期,可以继续使用。4XX
这类状态码表示客户端发送的报文有误,服务器无法处理。
4000 Bad Request
表示客户端请求的报文有错误,但只是一个笼统的错误。403 Forbidden
表示服务器禁止访问资源,客户端请求未出错。404 Not Found
表示请求的资源在服务器上不存在,或未找到,所以无法提供给客户端。5XX
这类状态码表示客户端请求报文没问题,但是服务器处理时内部发生了错误,属于服务器端的错误码
500 Internal Service Error
和400类型相似,是一个笼统通用的错误码,服务器端发生的具体错误并没有提示出来。501 Not Implemented
表示客户端请求的功能还不支持,类似于“即将业务,敬请期待”的意思。502 Bad Gateway
通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。503 Service Unavailable
表示服务器当前很忙,无法响应客户端,类似于“网络正忙,请稍后重试”。Host字段
客户端发请求时用来指定服务器的域名
Host: www.baidu.com
Content-Length字段
服务器返回数据时,会带有该字段,表名本次回应数据的长度。
Content-Length: 1000
如上就是告诉浏览器,本次服务器回应的数据长度是1000个字节,后面的字节就属于下一个回应了。
Connection字段
该字段最常用于客户端要求服务器使用TCP持久连接,以便其他请求复用。
HTTP/1.1版本的默认连接都是持久连接,但是为了兼容老版本的HTTP,需要指定Connection
首部字段的值为Keep-Alive
。
Connection: keep-alive
Content-Type字段
该字段用于服务器回应时告诉客户端,本次数据是什么格式。
Content-Type: text/html; charset=utf-8
上面的类型表名发送的是网页,而且编码是UTF-8。
客户端请求的时候可以试用Accept
字段声明自己可以接受那些数据格式。
Accept: */*
上面代码中,客户端声明自己可以接受任何格式的数据。
Content-Encoding 字段
该字段说明了数据的压缩方法,表名服务器返回的数据使用了上面压缩格式。
Content-Encoding: gzip
上面表示服务器返回的数据采用了gzip的压缩方式,告知客户端需要用此方法来解压数据。
和Content-Type
类似,客户端在请求时,可以用Accept-Encoding
来说明自己可以接受那些压缩方法。
Accept-Encoding: gzip, deflate
GET
根据RFC规范,GET的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片、视频等。GET请求参数的位置一般是写在URL当中,URL规定只能支持ASCII,所以GET请求的参数只允许ASCII字符,而且浏览器会对URL的长度有限制(HTTP协议本上对URL长度没有做规定)。
POST
根据RFC规范,POST请求的语义是根据请求负荷(报文body)对指定资源做出处理,处理的具体方式视资源类型而不同、POST请求携带数据的位置一般是在报文body中,body中的数据可以是任意格式的数据,只要客户端与服务器协商好即可,并且浏览器不会对body大小做限制。
比如,在我文章底部的评论区留言后,点击提交,浏览器就会执行一次POST请求,把你们的留言文字放进报文body里,然后拼接好POST请求头,通过TCP协议发送到服务器。
首先说明一下安全和幂等的概念:
如果从RFC规范定义的语义来说:
需要注意的,上面说的都是基于RFC规范定义的语义来分析的,实际开发中,开发者不一定会按照规范来实现GET和POST方法,比如:
有一个笑话,有人写了个博客,删除博客用的是GET请求,他觉得没人访问就连鉴权都没做。
然后Google服务器爬虫爬了一遍,他所有博文就没了。
对于一些重复性的HTTP请求,比如每次请求得到的数据都一样,我们可以把这对「请求-响应」的数据缓存到本地,那么下一次请求就可以直接读取本地的数据了,不需要通过网络获取服务器的响应,这样的话HTTP/1.1的性能会有肉眼可见的提升。
所以避免发送同样HTTP请求的方法就是通过缓存技术,HTTP设计者早在之前就考虑到了这点,所以HTTP协议的头部有不少字段都是针对缓存设计的。
HTTP缓存的实现方式总体上可以分为两种,分别是强制缓存和协商缓存。
前置缓存指的是只要浏览器判断缓存没过期,则直接使用浏览器本地的缓存,决定是否使用缓存的主动性在浏览器。
如下图,返回的状态码是200,但是括号中的标识是赖在内存缓存,也就是使用了强制缓存。
强制缓存是利用下面这两个HTTP响应头部(Response Header)字段来实现的,他们都用来表示资源在客户端缓存的有效期:
Cache-Control
,是一个相对时间;Expires
,是一个绝对时间;如果HTTP响应头同时有这两个字段的话,Cache-Controle的优先级高于Expire。
Cache-Control的选项更多一些,设置更加精细,所以建议使用Cache-Control来实现强缓存。具体实现流程如下:
当我们在浏览器使用开发者工具的时候,可能看到过某些请求的响应码是304
,这个是告诉浏览器可以使用本地缓存的资源,通常这种服务端告知客户端是否可用使用缓存的方式被称为协商缓存。
协商缓存可以基于两种头部字段来实现。
If-Modified-Since
字段与响应头部中的Last-Modified
字段实现
Last-Modified
:表示这个响应资源最后修改的时间,服务器给客户端返回资源的时候会带上。If-Modified-Since
:当需要再次请求服务器该资源的时候,客户端会将该资源的修改时间作为If-Midified-Since
的值放到请求头,一起发送给服务器,服务器收到后返现有If-Modified-Since
,则将该资源的最后修改时间与之对比,若最后修改时间较新,则说明资源又被修改过,则返回新资源,HTTP 200 OK;如果最后修改时间较旧,则返回HTTP 304,浏览器继续使用本地缓存。If-None-Match
字段与响应头部中的 ETag
字段
Etag
:唯一标识响应资源。If-None-Match
:当资源过期时,浏览器发现该资源的响应头中有Etag,则再次向服务器发起请求时,会将请求头中的If-None-Match的值设为Etag的值。服务器收到请求后就行对比,如果资源没有变化则返回HTTP 304,如果变化了则返回新的资源,HTTP 200 OK。第一种方式是基于时间来实现的,而第二种方式是基于一个位移标识来实现的,相对来说后者可以更加准确地判断出文件内容是否被修改该,避免时间被篡改而导致的不可靠问题。
如果HTTP响应头同时有Etag和Last-Modified字段的时候,Etag的优先级更高,就是先会通过判断Etag是否变化了,如果未发生变化,再看Last-Modified。
注意:协商缓存这两种方法都需要配合强制缓存中的Cache-Control字段来使用,只有在未命中强制缓存的时候,才可以发起带有协商缓存字段的请求。
下图是浏览器判断缓存是否过期的完整流程。
HTTP最突出的优点是「简单、灵活和易于扩展、应用广泛和快平台」。
简单
HTTP报文基本格式就是header + body
,头部信息也是key-value
的简单文本形式,易于理解,降低了学习和使用的门槛。
灵活和易于扩展
HTTP协议里的各类请求方法、URI/URL、状态码、头部字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。
同时HTTP由于是工作在应用层(OSI第七层),则他下层可以随意变化。
HTTPS也就是在HTTP与TCP层之间增加了SSL/TLS安全创术层,HTTP/3甚至吧TCP层换成基于UDP的QUIC。
应用广泛和跨平台
互联网发展至今,HTTP的应用范围非常的广阔,从台式机的浏览器到手机上的各种APP,HTTP的应用遍地开花,同时具有跨平台的优越性。
HTTP协议里有优缺点一题的双刃剑,分别是「无状态、明文传输」,同时还有一大缺点「不安全」。
无状态双刃剑
无状态的好处,因为服务器不需要去记忆HTTP的状态,所以不需要额外的资源来记录状态信息,这可以减轻服务器的负担,能够把更多的CPU和内存来对外提供服务。
无状态的坏处,没有记忆的服务器,在完成一些关联性的操作时会非常麻烦,例如:登录->添加购物车->下单->结算->支付,这一系列操作都要知道用户的身份才行。单服务器不知道这些请求是有关联的,每次都要问一遍身份信息,这就显得非常的“不聪明”。
对于无状态的问题,解决方案也有多种,比较简单的方式是使用Cookie技术,通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
Cookie就相当于一个“小贴纸”, 在客户端第一次请求后,服务器会下发一个装有客户信息的“小贴纸”,后续客户端请求服务器的时候,带上“小贴纸”,服务器就认识这是谁了。
明文双刃剑
明文意味着在传输的过程中不需要对信息进行加密和解密,可以在一定程度上减少客户端和服务器的性能消耗。
但也正是这样,HTTP所有的信息都暴露在光天化日之下,相当于信息裸奔了,在明文传输的漫长过程中,很容易就会被别人窃取,如果里面有你的账号密码信息,那就相当于号没了。
不安全
HTTP比较严重的缺点就是不安全:
HTTP的安全问题可以用HTTPS的方式来解决,也就是通过引入SSL/TLS层,使得安全性可靠。
HTTP是基于TCP/IP,并且使用了「请求 - 应答」的通信模式,所以性能关键就在这两点。
长连接
早起的HTTP/1.0性能上有一个比较大的问题,那就是每次发起一个请求,都要新建一次TCP连接(三次握手),而且是串行请求,做了很多无谓的TCP连接建立和断开,增加了通信开销。
为了解决上述问题,HTTP/1.1提出了长连接的通信方式,也叫持久连接,这种方式的好处在于减少了TCP连接的重复建立和断开所带来的的额外开销,减轻了服务器端的负载。
持久连接的特点是,只要任意一端没有明确要断开连接,就保持TCP连接的状态。
当然如果HTTP长连接超过一定时间没有数据交互,服务器就会主动断开这个连接。
管道网络传输
HTTP/1.1采用了长连接的方式,这使得管道(pipeline)网络传输称为可能。
也就是在同一个TCP连接中,客户端可以发起多个请求,主要第一个请求发出去了,不需要邓起返回,就可以发出第二个请求,可以减少真题的响应时间。
煮个栗子,客户端需要请求两个资源,之前的做法是,在同一个TCP连接里面先发送A请求,然后等服务器做出回应,收到后在发出B请求。那么,管道机制是允许浏览器同时发出A请求和B请求。
但是服务器必须按照接收请求的顺序发送这些请求的响应。
这就会出现一种情况,如果服务器在处理A请求时耗时比较长,那么后续的请求处理都会被阻塞,这被称为「队头阻塞」。所以HTTP/1.1解决了请求的队头阻塞,但是没有解决响应的队头阻塞。
注意:实际上HTTP/1.1管道化技术不是默认开启的,而且浏览器基本都没有支持,所以后面讨论HTTP/1.1都是建立在没有使用管道化的前提。
对头阻塞
「请求 - 应答」的模式加剧了HTTP的性能问题。
因为当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也同样被阻塞了,会导致客户端一直请求不到数据,这就是队头阻塞。
总之,HTTP/1.1的性能比较一般,后续的HTTP/2和HTTP/3就是在优化HTTP的性能。
众所周知,HTTP是明文传输,所以安全上存在一下三点风险:
而HTTPS在HTTP与TCP层之间加入了SSL/TLS
协议,可以很好的解决上述的风险:
下面详细介绍这三项解决措施:
混合加密
HTTPS采用的是对称加密和非对称加密结合的混合加密方式:
采用混合加密的原因:
摘要算法+数字签名
为了保证传输的内容不被篡改,我们需要对内容计算出一个「指纹」,然后同内容一起传输给对方,对方收到数据后,也对内容计算出一个「指纹」,然后更发送方的「指纹」做一个比较,如果「指纹」相同,说明内容没有被篡改,否则就可以判断内容被篡改过。
在计算机中,会使用摘要算法(哈希函数)来计算内容的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容,也就是哈希的过程是不可逆的。
通过摘要算法虽然可以保证内容不会被篡改,但是并不能保证内容和哈希值不会被一起修改掉,因为这里缺少对客户端收到的消息是否来源于服务器的证明。
为了避免这种情况的发生,这里通常会使用非对称加密算法来解决,非对称加密的两个秘钥可以双向加密和解密,比如用公钥加密,私钥解密,也可以用私钥加密,公钥解密,这两种不同的做法,目的也完全不同:
一般我们不会使用非对称加密来解密实际的传输内容,因为非对称加密比较耗费性能。非对称加密一般是通过「私钥加密,公钥解密」的方式,来确认消息的身份,数字签名算法,就是采用这种方式,不过私钥加密的内容不是内容本身,而是对内容的哈希值加密。
到这里,完整的流程如下图:
私钥是有服务端保管,然后服务端会向客户端分发对应的公钥,如果客户端收到的信息能被公钥解密,就说明该消息是由服务器发送的。
数字证书
通过前面我们知道:
但还是存在存在着一个漏洞,缺少了对公钥的验证,万一公钥是被伪造的,那前面的这些加密都白搭。
所以还需要一个权威机构来证明公钥的身份,这个全为机构就是CA(数字证书认证机构),将服务器公钥放在数字证书(数字证书认证机构颁发)中,只要证明证书可信的,那公钥就是可信的。
通过数字证书的方式可以保证服务器公钥的身份,解决冒充的风险。
小林coding HTTP常见面试问题