通向万维网的协议 - HTTP
当在浏览器中输入 URL 时,浏览器会根据地址栏的 URL,从 Web 服务端获取文件资源等信息,从而显示出网页,像这种发送请求获取服务器资源的 Web 浏览器等,都可称为客户端
Web 使用一种名为 HTTP 的协议作为规范,完成从客户端到服务器端等一系列的运作流程,Web 是建立在 HTTP 协议上通信的
提示
HTTP(Hyper Text Transfer Protocol)本来译为超文本转移协议,但是通常被译为超文本传输协议
HTTP 的诞生
在最初的时候,接触到的 Web 的人很少,但是为了形成共享信息的设想,便设计出了多文档相互关联形成的超文本,连接成可互相参阅的万维网,因此提出了三种万维网构建技术:
- HTML 作为文档的文本标记语言
- HTTP 作为文档的传递协议
- URL 作为文档的所在地址
这是历史发展:
- HTTP 在 1990 年问世,但是并没有作为正式的标准,HTTP 正式作为标准是 1996 年,版本被定为 HTTP/1.0,这个标准一直被服务端使用至今,增加了很多请求方法,状态码,响应头,缓存等
- 1990 年,第一台 Web 服务器和 Web 浏览器诞生,HTML 1.0 诞生,由于存在很多模糊不清的地方,被废弃了
- 1993 年,可以显示图像的浏览器问世
- 1994 年,网景公司发布了自家的浏览器,微软公司也发布了 IE 1.0 和 2.0,HTML 也到了 2.0 时代,微软和网景爆发浏览器大战,浏览器厂商对标准化视而不见
- 1997 年公布的 HTTP/1.1 是目前主流的 HTTP 协议版本,增加了持久性连接,流水线,host 字段
- 2000 年,网景公司逐渐衰落,宣布浏览器大战告一段落
- 2004 年,Mozilla 发布了 Firefox 浏览器,第二次浏览器大战宣布开始。与此同时 IE 一直升级到了 10 版本,其他浏览器厂商,Chrome、Opera、Safari 等开始抢占市场份额
- HTTP/2 是 HTTP 协议的第二个主要的版本,在 2015 年被发布,所有的数据都是以帧进行传输,服务端可以主动推送信息到客户端,主要解决了前面的版本效率低下的问题
HTTP 的出现主要是解决文本传输的难题,由于协议本身很简单,现在的 HTTP 已经超出了 Web 的局限,被运用到了各种场景
三次握手
HTTP 连接是以 HTTP 协议为通信协议的 TCP 连接。在 HTTP 中不存在连接的概念,只有请求和响应,产生的数据传输必须有一个通道,这就不是 HTTP 做的事情了,因此必须先了解 TCP 连接
协议
计算机与其他网络设备通信,双方就必须基于相同的方法,比如如何发现通信目标、由哪一边先发送通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的一切都需要规则,而这种规则就被称为协议
像把一些互联网协议集合起来就总称为 TCP/IP,TCP/IP 最重要的是分层,按层次分为以下 4 层:应用层、传输层、网络层、数据链路层
当层次化后,设计就变的非常简单,处于应用层上的应用只考虑如何分发,而不需要弄清对方在哪个地方,以及对方的传输路线,是否能够传输送达等问题
- 应用层:决定了向用户提供应用服务时的通信活动,HTTP 就属于该层
- 传输层:提供处于网络连接中的两台设备之间的数据传输,有两个不同的协议:TCP 和 UDP
- 网络层:用来处理在网络上流动的数据包,规定通过什么样的路径送达对方,并把数据包传给对方
- 链路层:用来处理连接网络的硬件部分
利用 TCP/IP 进行网络通信的时候,会通过分层顺序与对方进行通信,发送端从应用层往下走,接收端从链路层往上走
比如以 HTTP 为例,会在应用层发出一个想要看某个 Web 页面的 HTTP 请求,然后在传输层把应用层接收到的数据(HTTP 请求报文)进行分割,并打上记号和端口号发给网络层
网络层中,增加作为通信目的地的 MAC 地址后发送给链路层,这样发送的请求信息已经准备齐全了
此时链路层将信息发送到接收端的链路层,按顺序往上发送,一直到应用层,这才算上真正接收到了客户端发送过来的 HTTP 请求
此外,发送端在层与层之间传输时,没经过一层都会打上一个该层所属的首部信息,反之,在接收端层与层传输时,每经过一层时,会把对应的首部信息抹去,这种把数据信息包装起来的做法叫做封装
与 HTTP 关系密切的协议:IP、TCP、DNS
所以,HTTP 与各种协议之间的关系就是:
发送端:
- DNS 解析域名对应的 IP 地址
- HTTP 生成针对目标服务器的 HTTP 请求报文
- TCP 将 HTTP 请求报文切割成段,将每一段可靠的发送给对方
- IP 负责互相搜索对方的地址,一边中转一边传送
服务端:
- TCP 接收报文段,根据序号重组请求报文
- HTTP 根据请求方的报文作出对应的处理,并将结果同样以 TCP/IP 的方式向客户端回传
URI 和 URL
URL 是浏览器访问 Web 服务器时需要输入的地址,而 URI 是统一资源定位符,它是一个标准,标准的 URI 协议方案有很多种,URI 用于标记某一互联网资源,而 URL 标志资源的地点,由此可见,URL 是 URI 的子集
一个完整的 URI 格式是这样的:协议方案://登录信息@服务器地址:端口号/文件路径?查询字符串#片段标记
这是一个网址的例子:http://user:pass@www.example.com:80/dir/index.html?id=1#p1
- 协议方案:这个 URI 采用 HTTP 协议
- 登录信息:指定用户名和密码作为从服务器获取资源时必要的身份验证,是可选的
- 服务器地址:可以是域名或者 IP 地址
- 端口号:指定服务器连接的网络端口,可选的
- 带层次的文件路径:指定服务器上的文件路径访定位特指的资源
- 查询字符串:针对已经指定的文件路径内的资源,是可选的
- 片段标记,用于获取已获得资源中的子资源(文档中的某个位置),是可选的
报文
用于 HTTP 协议交互的信息被称为 HTTP 报文,请求端叫请求报文,响应端的叫做响应报文。报文本身是由多行数据构成的字符串文本,报文大致可以分为首部和主体,通常情况下不一定有主体部分
请求报文的首部由下面数据组成:
请求头 - 由起始行和首部组成
请求体
请求头:包含应用于请求的方法,请求 URI 和 HTTP 版本
状态行:包含响应结果的状态吗,原因短语和 HTTP 版本
首部字段:包含请求和响应的各种条件和属性的各类首部,一般是通用首部、请求首部、响应首部和实体首部
其他:包含 HTTP 的 RFC 里未定义的首部,比如 Cookie 等
响应报文由以下部分组成:
- 响应头 - 由起始行和首部组成
- 响应体
HTTP 在传输数据的时候可以直接传输数据本体,但也可能会通过编码的方式提升传输速率,在传输时进行编码会有效地处理大量的访问请求,但是通常会消耗更多 CPU 资源
报文是 HTTP 通信的基本单位,而实体是作为请求或响应的有效载荷数据被传输,其内容由实体首部和实体主体组成。通常情况下,报文主体等于实体主体
HTTP 在进行传输时,为了提升速率,可能会先压缩一下数据,然后再发送,HTTP 中有一种被称为内容编码的功能也可以进行这样的操作,内容编码能够指明实体内容上的编码格式,并原样压缩实体,编码后的实体由客户端接受并负责解码,这是常用的内容编码:
- gzip:GNU zip
- compress:UNIX 标准压缩
- deflate:zlib
- identity:不进行编码
在实体资源未全部传输完成之前,浏览器是无法显示请求页面的,因此在传输大体积数据时,通过把数据分割成小的数据块,能够让浏览器逐步的显示页面,这种分块功能叫做分块传输编码
使用分块传输编码的实体主体会由客户端负责解码,恢复到编码前的实体主体
HTTP 协议中使用了多部份对象集合的方法,在一份报文中可以含有多种类型的实体,通常是在上传图片或文件时使用
这是一些多部份对象集合所包含的对象:
multipart/form-data
:表单上传二进制文件时使用multipart/byteranges
:响应报文包含了多个范围的内容时使用
在报文中使用多部份对象集合时,需要在首部字段里加上Content-Type
Content-Type
是很重要的字段,决定了 Body 的编码方式:
- application/x-www-form-urlencoded:默认的编码方式
- application/json:序列化后的 JSON 字符串
- text/xml:XML 作为编码方式
- text/plain:普通纯文本
动词
在进行 HTTP 通信时,必有一个客户端和服务端,通常情况下,客户端和服务端是可以互换的,而 HTTP 能够明确区分哪端是客户端和服务端
在发送请求的时候,可以通过一些明确的动词来告诉服务器的意图,比如:
- GET:常用于获取资源
- POST:用来传输实体的主体,POST 最主要的目的是并不是获取响应的主体内容
- PUT:用于传输文件
- DELETE:删除文件
- HEAD:获取报文首部,和 GET 一样,只是不返回主体部分
- OPTIONS:询问支持的方法
- TRACE:追踪路径
- CONNECT:使用安全隧道协议连接代理
状态码
HTTP 状态码负责表示客户端的请求返回结果,标记服务端的处理是否正常,借助状态码可以知道服务端是进行了正常的处理,还是出现了错误
状态码是以 3 位数字和原因短语组成的,数字中的第一位指定了响应的类别,后两类没有分类:
- 1xx:信息行状态码,表示接收的请求正在处理
- 2xx:成功状态码,表示请求正常处理完毕
- 3xx:重定向状态码,需要进行附加操作以完成请求
- 4xx:客户端错误状态码,表示服务端无法处理请求
- 5xx:服务端错误状态码,表示服务端处理请求出错
只要遵守状态码类别的定义,便可以改变所定义的状态码,或者服务端自行创建状态码都可以,仅仅记录的已知状态码就有很多种,实际上正常使用的大致只有 14 种
- 200:OK
- 204:Not Content,没有返回内容
- 206:Partial Content,进行了范围请求
- 301:Moved Permanently,永久重定向
- 302:Found,临时重定向
- 303:See Other,表示由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源
- 304:No Modified,浏览器缓存相关,允许访问资源,但是服务不会响应内容
- 307:Temporary Redirect,临时重定向,不会从 POST 变为 GET
- 400:Bad Request,
- 401:Unauthorized,
- 403:Forbidden
- 404:Not Found
- 500:Internal Server Error
- 503:Service Unavailable
内容协商
同一个 Web 网站可能存在多份相同的内容页面,比如英文和中文的页面,虽然内容上相同,但使用的语言并不同,当浏览器的默认语言为英文或中文,访问相同的页面时,则会显示对应的英文或中文版的页面,这种机制被称为内容协商
内容协商是指客户端和服务端就响应的资源内容进行交涉,然后提供给客户端最适合的资源,内容写上会以响应资源的语言、字符集、编码方式等作为判断的条件,比如包含在请求报文中的某些首部字段:
- Accept:客户端接受哪些类型的信息
- Accept-Charset:接受的字符集
- Accept-Encoding:可接受的内容编码
- Accept-Language:自然语言
- Content-Type:Body 编码方式
- Authorization:证明客户端有权限查看某个资源
- Host:指定被请求的资源主机和端口号
- User-Agent:用户代理:操作系统及版本、CPU 类型、浏览器及版本、渲染引擎、浏览器语言
对于内容协商来说有三种类型:
- 服务器驱动协商:由服务端进行内容协商,以请求的首部字段进行参考,在服务端处理
- 客户端驱动协商:由客户端进行内容协商,用户从浏览器上显示的可选项中进行选择,或者通过 JavaScript 进行自动选择
- 透明协商:是服务端和客户端的结合体,由服务端和客户端各自进行内容协商的一种办法
持久连接
在 HTTP 协议中,每一次通信,就会断开一次 TCP 连接,随着通信次数增加,每次都会造成无谓的TCP 连接建立和断开,增加了消耗。比如发送了一个包含多张图片的 HTML 文档,会产生大量的通信消耗
持久连接就是用来解决这个问题的方案,只要任意一端没有提出断开连接,则保持 TCP 连接状态。即建立 1 次 TCP 连接后进行多次请求和响应的交互,减少了通信量,减轻了服务端的负载,另外请求和响应过早的结束,使页面显示速度也提高了,在 HTTP/1.1 中,所有的连接默认都是持久连接
在 Header 中使用Connection
进行控制,默认为keep-alive
,如果使用了close
,则每个请求都会重新建立 TCP 连接
获取部分内容的请求
在下载一个体积稍大时的文件时,一旦出现了网络中断,就必须从头开始,为了解决这种问题,就出现了一种可恢复的下载机制,就是从之前下载中断的地方恢复下载,要实现这种功能就需要指定下载的范围,将一份文件分成范围下载,即使在某个范围中断了下载,那么重新下载的代价就不会很大
在 HTTP 中进行范围请求时,会用到首部字段 Range 来指定资源的 byte 范围,针对范围请求,响应会返回状态码为 206 的响应报文,对于多重范围的范围请求,会在首部字段 Content-Type 中标明 multipart/byteranges 后返回报文。当然,如果服务器无法响应范围请求,这回返回状态码 200 和完整的实体内容
Web 服务器
一台服务器可以搭建多个独立域名的 Web 网站,也可以作为通信路径上的中转服务器提升传输效率,HTTP/1.1 允许一台 HTTP 服务器搭建多个站点,比如使用一台服务器为多个用户服务,也可以以每个用户持有的域名运行各自不同的网站,这是利用了虚拟主机的功能
在物理层面上即使只有一台服务器,但是用了虚拟主机后,便可以假想成很多个服务器,在客户端使用 HTTP 协议访问服务器时,会通常使用类似于xxx.com
这样的域名来进行定位,在互联网上,域名通过 DNS 解析成域名对应的 IP 地址之后便可以访问目标服务器,因此之后的访问都实际上是以 IP 地址形式的访问
基于 Cookie 的状态管理
HTTP 是一种不保存状态的协议,不会对请求和响应之间的通信状态保存。无状态的 HTTP 会减少性能开销,但是又要解决类似的矛盾问题,比如无法保留一个用户从一个页面跳转到另一个页面的登陆状态,于是引进了 Cookie 技术,即在请求和响应报文中写入 Cookie 信息来管理状态,cookie 是一种持久化数据的技术
Cookie 会根据服务器发送的响应报文内的一个叫做Set-Cookie
的首部字段信息,通知客户端保存 Cookie,当下次客户端再向该服务器发送请求时,客户端会自动再请求报文中加入 Cookie 值再发送。服务器发现发送过来的 Cookie 后,会检查从哪个客户端发送的请求,然后对比服务器上的 Cookie 记录,最后验证状态
HTTPS
超文本传输安全协议(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行安全通信的传输协议。HTTPS经由HTTP进行通信,利用SSL/TLS来加密数据包。HTTPS的主要目的是提供对网站服务器的身份认证,保护交换数据的隐私与完整性
HTTPS的优点如下:
- 使用 HTTPS 协议可以认证用户和服务器,确保数据发送到正确的客户端和服务器
- 使用 HTTPS 协议可以进行加密传输、身份认证,通信更加安全,防止数据在传输过程+ 中被窃取、修改,确保数据安全性
- HTTPS 是现行架构下最安全的解决方案,虽然不是绝对的安全,但是大幅增加了中间+ 人攻击的成本
HTTPS 的缺点如下:
- HTTPS 需要做服务器和客户端双方的加密个解密处理,耗费更多服务器资源,过程复杂
- HTTPS 协议握手阶段比较费时,增加页面的加载时间
- SSL 证书是收费的,功能越强大的证书费用越高
- HTTPS 连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本
- SSL 证书需要绑定 IP,不能在同一个 IP 上绑定多个域名
跨域资源共享
CORS 预请求
在进行跨域请求的时候,允许使用 GET、POST、HEAD 方法,Content-Type 也限制为 text/plain、multipart/form-data、application/x-www-form-urlencoded,不能使用自定义的请求头,XMLHttpRequestUpload 对象均没有注册任何事件监听器,请求中没有使用 ReadableStream 对象
缓存控制
默认情况下,浏览器不会缓存从服务器上请求的资源,可以通过Cache-Control
来进行控制
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control?from=from_parent_mindnote
在前端中使用打包工具的时候,脚本文件名是不一样的,这避免了缓存带来了无法刷新的问题