# 一篇就够了:HTTP
基础知识一网打尽
# HTTP
协议
HTTP
协议是一种无状态的、处于应用层的、以请求/应答方式运行的协议,使用可扩展的语义和自描述的信息格式,与基于网络的超文本信息系统灵活的相互作用。
# 与HTTP
相关的协议
# OSI
模型
OSI
(Open System Interconnection Reference Model),开放式系统互联通信参考模型,也就是我们常说的 7 层模型。从它的名称就可以看出来,OSI
只是一个供参考的概念模型,它从未被真正的实现。OSI
的 7 层,从上至下分别是:
OSI 分层 | 作用 |
---|---|
L7 应用层 | 解决业务问题,面向具体的应用传输数据 |
L6 表示层 | 将消息转换为应用层可以读取的消息 |
L5 会话层 | 建立会话、握手、维持网络的连接状态 |
L4 传输层 | 包括我们熟悉的 TCP 与 UDP 等,解决进程与进程之间的通讯 |
L3 网络层 | 主要包括 IP 协议,负责将报文从因特网上的一个主机发送到另一个主机上 |
L2 数据链路层 | 工作在局域网中,使用 MAC 地址标记网络上的设备,如路由器,然后将报文转到主机上 |
L1 物理层 | 电缆、光纤等 |
# TCP/IP
模型
OSI
只是一个概念模型,而平常工作我们最常用的还是 TCP/IP
模型。TCP/IP
模型其实就是 OSI
模型的简化版本,也就是我们平时所说的 4 层模型。TCP/IP
的 4 层,由上至下分别是:
通过上图我们可以看出,其实 TCP/IP
模型与 OSI
模型十分相似,主要是省略了表示层、会话层与物理层的实现。这里每一层的功能实际上与对应的 OSI
模型十分类似,所以就不再罗列了。下面是一张 OSI
模型与 TCP/IP
模型的层级对照图,大家可以通过对照图来总结 TCP/IP
模型中各层的职责。
网络分层的好处是,每一次层都只负责自己的任务,其他层的事情完全不需要考虑,层次之间交互的时候,只需要调用接口就可以了。当某一层需要修改的时候,也完全不影响其他的功能。当然,有优势就一定有劣势,每一次进行网络通信的时候,都需要由上至下,一层一层的传递信息,反过来,又要一层一层的向上传递,对于性能的影响是比较大的。
# TCP
三次握手、四次挥手
# 三次握手
所谓三次握手(Three-way Handshake),是指建立一个 TCP
连接时,需要客户端和服务器总共发送3个包。三次握手的目的是连接服务器指定端口,建立 TCP
连接,并同步连接双方的序列号和确认号,交换 TCP
窗口大小信息。在 socket
编程中,客户端执行 connect()
时,将触发三次握手。
- 第一次握手(
SYN=1, seq=x
):客户端发送一个TCP
的SYN
标志位置1
的包,指明客户端打算连接的服务器的端口,以及初始序号X
,保存在包头的序列号(Sequence Number)字段里。发送完毕后,客户端进入SYN_SEND
状态。 - 第二次握手(
SYN=1, ACK=1, seq=y, ACKnum=x+1
):服务器发回确认包(ACK
)应答。即SYN
标志位和ACK
标志位均为1。服务器端选择自己ISN
序列号,放到Seq
域里,同时将确认序号(Acknowledgement Number)设置为客户的ISN
加1,即X+1
。 发送完毕后,服务器端进入SYN_RCVD
状态。 - 第三次握手(
ACK=1,ACKnum=y+1
):客户端再次发送确认包(ACK
),SYN
标志位为0,ACK
标志位为1,并且把服务器发来ACK
的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN
的+1。发送完毕后,客户端进入ESTABLISHED
状态,当服务器端接收到这个包时,也进入ESTABLISHED
状态,TCP
握手结束。
# 四次挥手
TCP
的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket
编程中,任何一方执行 close()
操作即可产生挥手操作。
- 第一次挥手(
FIN=1,seq=x
):假设客户端想要关闭连接,客户端发送一个FIN
标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。发送完毕后,客户端进入FIN_WAIT_1
状态。 - 第二次挥手(
ACK=1,ACKnum=x+1
):服务器端确认客户端的FIN
包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。发送完毕后,服务器端进入CLOSE_WAIT
状态,客户端接收到这个确认包之后,进入FIN_WAIT_2
状态,等待服务器端关闭连接。 - 第三次挥手(
FIN=1,seq=y
):服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN
置为1。发送完毕后,服务器端进入LAST_ACK
状态,等待来自客户端的最后一个ACK
。 - 第四次挥手(
ACK=1,ACKnum=y+1
):客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入TIME_WAIT
状态,等待可能出现的要求重传的ACK
包。服务器端接收到这个确认包之后,关闭连接,进入CLOSED
状态。客户端等待了某个固定时间(两个最大段生命周期,2MSL
,2 Maximum Segment Lifetime)之后,没有收到服务器端的ACK
,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入CLOSED
状态。
# URL
的组成
URL
,统一资源定位符的简称,Uniform Resource Locator,常常被称为网址,是因特网上标准的资源地址。
通用的格式:scheme
: // host
[: port
]/path
/…/?query
#anchor
名称 | 功能 |
---|---|
scheme | 访问服务器以获取资源时要使用哪种协议,比如:http,https 和 FTP 等 |
host | HTTP 服务器的 IP 地址或者域名 |
port | HTTP 服务器的默认端口是 80,HTTPS默认端口是443,这种情况下端口号可以省略,如果使用了别的端口,必须指明。不同的端口,你可以认为是不同的应用程序。 |
path | 访问资源的路径 |
query-string | 发给 http 服务器的数据 |
anchor | 锚点 |
# DNS
工作原理
DNS
协议提供的是一种主机名到 IP
地址的转换服务,就是我们常说的域名系统。是应用层协议,通常该协议运行在UDP
协议之上,使用的是53端口号。
这张图很生动的展示了DNS
在本地DNS
服务器是如何查询的,一般向本地DNS
服务器发送请求是递归查询的。
本地 DNS 服务器向其他域名服务器请求的过程是迭代查询的过程👇
# HTTP
的特点
HTTP
是一个属于应用层的面向对象的协议,HTTP
协议一共有五大特点:
- 支持客户/服务器模式
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有
GET
、HEAD
、POST
。每种方法规定了客户与服务器联系的类型不同。由于HTTP
协议简单,使得HTTP
服务器的程序规模小,因而通信速度很快。 - 灵活:
HTTP
允许传输任意类型的数据对象。正在传输的类型由Content-Type
(Content-Type
是HTTP
包中用来表示内容类型的标识)加以标记。 - 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 无状态:
HTTP
协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
# HTTP
的缺点
- 无状态:无状态在部分场景下是缺点,比如:购物系统,无法保留顾客信息。
- 明文传输:协议里的报文(主要指的是头部)不使用二进制数据,而是文本形式。这让
HTTP
的报文信息暴露给了外界,给攻击者带来了便利。 - 队头阻塞:当同时发起多个
HTTP
请求时,共用一个TCP
连接,当某个请求时间过长时,其他的请求只能处于阻塞状态,这就是队头阻塞问题。
# HTTP
版本
# HTTP 0.9
- 1991年,原型版本,功能简陋,只有一个命令
GET
,只支持纯文本内容,该版本已过时。
# HTTP 1.0
- 任何格式的内容都可以发送,这使得互联网不仅可以传输文字,还能传输图像、视频、二进制等文件。
- 除了
GET
命令,还引入了POST
命令和HEAD
命令。 http
请求和回应的格式改变,除了数据部分,每次通信都必须包括头信息(HTTP header
),用来描述一些元数据。- 只使用
header
中的If-Modified-Since
和Expires
作为缓存失效的标准。 - 不支持断点续传,也就是说,每次都会传送全部的页面和数据。
- 通常每台计算机只能绑定一个
IP
,所以请求消息中的URL
并没有传递主机名(hostname
)
# HTTP 1.1
http1.1
是目前最为主流的http
协议版本,从1999年发布至今,仍是主流的http
协议版本。
- 持久连接:即
TCP
连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive
。长连接的连接时长可以通过请求头中的keep-alive
来设置。 - 管道机制:即在同一个
TCP
连接里,客户端可以同时发送多个请求,进一步改进了HTTP协议的效率。 HTTP 1.1
中新增加了E-tag
,If-Unmodified-Since
,If-Match
,If-None-Match
等缓存控制标头来控制缓存失效。- 断点续传:通过使用请求头中的
Range
来实现。 - 虚拟网络:在一台物理服务器上可以存在多个虚拟主机(
Multi-homed Web Servers
),并且它们共享一个IP
地址。 - 新增方法:
PUT
、PATCH
、OPTIONS
、DELETE
。
# HTTP 2.0
- 二进制分帧:这是一次彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧":头信息帧和数据帧。
- 头部压缩:
HTTP 1.1
版本会出现User-Agent
、Cookie
、Accept
、Server
、Range
等字段可能会占用几百甚至几千字节,而Body
却经常只有几十字节,所以导致头部偏重。HTTP 2.0
使用HPACK
算法进行压缩。 - 多路复用:复用
TCP
连接,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,且不用按顺序一一对应,这样子解决了队头阻塞的问题。 - 服务器推送:允许服务器未经请求,主动向客户端发送资源,即服务器推送。
- 请求优先级:可以设置数据帧的优先级,让服务端先处理重要资源,优化用户体验。
# 请求方法
HTTP1.0
定义了三种请求方法: GET
、 POST
和 HEAD
方法。
HTTP1.1
新增了五种请求方法:OPTIONS
、PUT
、 DELETE
、 TRACE
和 CONNECT
。
方法 | 含义 |
---|---|
GET | 通常用于请求服务器发送某些资源 |
HEAD | 请求资源的头部信息, 并且这些头部与 HTTP GET 方法请求时返回的一致. 该请求方法的一个使用场景是在下载一个大文件前先获取其大小再决定是否要下载, 以此可以节约带宽资源 |
OPTIONS | 用于获取目的资源所支持的通信选项 |
POST | 发送数据给服务器 |
PUT | 用于新增资源或者使用请求中的有效负载替换目标资源的表现形式 |
DELETE | 用于删除指定的资源 |
PATCH | 用于对资源进行部分修改 |
CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
# 常见错误码
RFC 规定 HTTP 的状态码为「三位数」,第一个数字定义了响应的类别,被分为五类:
# 1xx 信息类
- 接受的请求正在处理,信息类状态码。
# 2xx 成功
- 200 OK 表示从客户端发来的请求在服务器端被正确请求。
- 204 No content,表示请求成功,但没有资源可返回。
- 206 Partial Content,该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的 GET 请求 响应报文中包含由 「Content-Range」 指定范围的实体内容。
# 3xx 重定向
- 301 moved permanently,永久性重定向,表示资源已被分配了新的 URL,这时应该按 Location 首部字段提示的 URI 重新保存。
- 302 found,临时性重定向,表示资源临时被分配了新的 URL。
- 303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源。
- 304 not modified,当协商缓存命中时会返回这个状态码。
- 307 temporary redirect,临时重定向,和302含义相同,不会改变method
当 301、302、303 响应状态码返回时,几乎所有的浏览器都会把 POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送 301、302 标准是禁止将 POST 方法改变成 GET 方法的,但实际使用时大家都会这么做
# 4xx 客户端错误
- 400 bad request,请求报文存在语法错误。
- 401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息。
- 403 forbidden,表示对请求资源的访问被服务器拒绝。
- 404 not found,表示在服务器上没有找到请求的资源。
- 405 Method Not Allowed,服务器禁止使用该方法,客户端可以通过options方法来查看服务器允许的访问方法,如下 👇
Access-Control-Allow-Methods →GET,HEAD,PUT,PATCH,POST,DELETE
# 5xx 服务器错误
- 500 internal sever error,表示服务器端在执行请求时发生了错误。
- 502 Bad Gateway,服务器自身是正常的,访问的时候出了问题,具体啥错误我们不知道。
- 503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求。
# 缓存机制
缓存机制无处不在,有客户端缓存,服务端缓存,代理服务器缓存等。在HTTP
中具有缓存功能的是浏览器缓存。 HTTP
缓存作为web
性能优化的重要手段,对于从事web
开发的朋友有重要的意义。
# 缓存的规则
我们知道HTTP
的缓存属于客户端缓存,后面会提到为什么属于客户端缓存。所以我们认为浏览器存在一个缓存数据库,用于储存一些不经常变化的静态文件(图片、css、js等)。我们将缓存分为强制缓存和协商缓存。下面我将分别详细的介绍这两种缓存的缓存规则。
# 强制缓存
当缓存数据库中已有所请求的数据时。客户端直接从缓存数据库中获取数据。当缓存数据库中没有所请求的数据时,客户端的才会从服务端获取数据。
# 协商缓存
又称对比缓存,客户端会先从缓存数据库中获取到一个缓存数据的标识,得到标识后请求服务端验证是否失效(新鲜),如果没有失效服务端会返回304,此时客户端直接从缓存中获取所请求的数据,如果标识失效,服务端会返回更新后的数据。
两类缓存机制可以同时存在,强制缓存的优先级高于协商缓存,当执行强制缓存时,如若缓存命中,则直接使用缓存数据库数据,不在进行缓存协商。
# 缓存的方案
上面的内容让我们大概了解了缓存机制是怎样运行的,但是,服务器是如何判断缓存是否失效呢?我们知道浏览器和服务器进行交互的时候会发送一些请求数据和响应数据,我们称之为HTTP
报文。报文中包含首部header
和主体部分body
。与缓存相关的规则信息就包含在header
中。boby
中的内容是HTTP
请求真正要传输的部分。举个HTTP
报文header
部分的例子如下:
Response Header
Cache-Control: max-age=30000000
Connection: keep-alive
Content-Encoding: gzip
Content-Type: application-javascript
Date: Tue, 24 Jan 2020 10:30:30 GMT
ETag: W/"'58846adf-110dfe'"
Last-Modified: Tue, 24 Jan 2020 10:30:30 GMT
接下来我们将对HTTP
报文中出现的与缓存规则相关的信息做出详细解释。(我们依旧分为强制缓存和协商缓存两个方面来介绍)。
# 强制缓存
对于强制缓存,服务器响应的header
中会用两个字段来表明 —— Expires
和Cache-Control
。
# Expires
Exprires
的值为服务端返回的数据到期时间。当再次请求时的请求时间小于返回的此时间,则直接使用缓存数据。但由于服务端时间和客户端时间可能有误差,这也将导致缓存命中的误差,另一方面,Expires
是HTTP1.0
的产物,故现在大多数使用Cache-Control
替代。
# Cache-Control
Cache-Control
有很多属性,不同的属性代表的意义也不同。
- private:客户端可以缓存
- public:客户端和代理服务器都可以缓存
- max-age=t:缓存内容将在t秒后失效
- no-cache:需要使用协商缓存来验证缓存数据
- no-store:所有内容都不会缓存。
# 协商缓存
协商缓存需要进行对比判断是否可以使用缓存。浏览器第一次请求数据时,服务器会将缓存标识与数据一起响应给客户端,客户端将它们备份至缓存中。再次请求时,客户端会将缓存中的标识发送给服务器,服务器根据此标识判断。若未失效,返回304状态码,浏览器拿到此状态码就可以直接使用缓存数据了。 对于协商缓存来说,缓存标识我们需要着重理解一下,下面我们将着重介绍它的两种缓存方案。
# Last-Modified
/If-Modified-Since
**Last-Modified**
: 服务器在响应请求时,会告诉浏览器资源的最后修改时间。
**if-Modified-Since**
:
浏览器再次请求服务器的时候,请求头会包含此字段,后面跟着在缓存中获得的最后修改时间。服务端收到此请求头发现有if-Modified-Since
,则与被请求资源的最后修改时间进行对比,如果一致则返回304
和响应报文头,浏览器只需要从缓存中获取信息即可。从字面上看,就是说:从某个时间节点算起,是否文件被修改了。
- 如果真的被修改:那么开始传输响应一个整体,服务器返回:200 OK
- 如果没有被修改:那么只需传输响应header,服务器返回:304 Not Modified
# ETag
/If-None-Match
**ETag**
:服务器响应请求时,通过此字段告诉浏览器当前资源在服务器生成的唯一标识(生成规则由服务器决定)。
**If-None-Match**
:再次请求服务器时,浏览器的请求报文头部会包含此字段,后面的值为在缓存中获取的标识。服务器接收到次报文后发现If-None-Match
则与被请求资源的唯一标识进行对比。
- 不同,说明资源被改动过,则响应整个资源内容,返回状态码200。
- 相同,说明资源无心修改,则响应header,浏览器直接从缓存中获取数据信息。返回状态码304。
但是实际应用中由于Etag的计算是使用算法来得出的,而算法会占用服务端计算的资源,所有服务端的资源都是宝贵的,所以就很少使用Etag了。
# 缓存的优点
- 减少了冗余的数据传递,节省宽带流量
- 减少了服务器的负担,大大提高了网站性能
- 加快了客户端加载网页的速度 这也正是HTTP缓存属于客户端缓存的原因
# 不同刷新的请求执行过程
- 浏览器输入URL:浏览器发现缓存中有这个文件了,不用继续请求了,直接去缓存拿(最快)。
F5
刷新:F5
就是告诉浏览器,别偷懒,好歹去服务器看看这个文件是否有过期了。于是浏览器就胆胆襟襟的发送一个请求带上If-Modify-since
。Ctrl+F5
:告诉浏览器,你先把你缓存中的这个文件给我删了,然后再去服务器请求个完整的资源文件下来。于是客户端就完成了强行更新的操作。
# HTTPS
HTTPS
(全称:Hyper Text Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP
通道,简单讲是HTTP
的安全版。即HTTP
下加入SSL
层,HTTPS
的安全基础是SSL
,因此加密的详细内容就需要SSL
。 现在它被广泛用于万维网上安全敏感的通讯,例如交易支付方面。
# HTTPS
和HTTP
的区别
HTTP
是明文传输,HTTPS
通过SSL\TLS
进行了加密。HTTP
的端口号是80
,HTTPS 是443
。HTTPS
需要到CA
申请证书,一般免费证书很少,需要交费。HTTP
的连接很简单,是无状态的;HTTPS
协议是由SSL
+HTTP
协议构建的可进行加密传输、身份认证的网络协议,比HTTP
协议安全。
# 为什么使用HTTPS
HTTPS
最主要的用处是以下两点:
- 建立一个信息安全通道,来保证数据传输的安全
- 确认网站的真实性,防止钓鱼网站
# HTTPS
的原理
由于HTTPS
的原理不是很容易看懂,我们用一个简单的实例来进行说明:来自中国的 张大胖 和位于米国的 Bill 想要进行安全的通信。
由于张大胖和 Bill 都是使用 HTTP
进行通信,HTTP
是明文的,所以他们的聊天都是可被窥视的。于是,二人准备想要改变现状,所以 HTTPS
首先要解决的问题就是要保证传输的内容只有这两个人能看懂
# 对称加密
两人商量了一下,可以使用对称密钥进行加密。(对称密钥:加密和解密使用的是同一个密钥)。
对称加密的问题:既然网络是不安全的,那么最开始的时候怎么将这个对称密钥发送出去呢?如果对称密钥在发送的时候就已经被拦截,那么发送的信息还是会被篡改和窥视。
所以这种对称密钥的弊端就是,可能被中间人拦截,这样中间人就可以获取到了密钥,就可以对传输的信息就行窥视和篡改。
# 非对称加密
非对称加密算法(RSA
):双方必须协商一对密钥,一个私钥一个公钥。用私钥加密的数据,只有对应的公钥才能解密,用公钥加密的数据, 只有对应的私钥才能解密。
Bill 将自己的公钥给张大胖,张大胖发送的信息使用 Bill 的公钥加密,这样,只有 Bill 使用自己的私钥才能获取。
非对称加密的弊端:虽然非对称加密解决了安全的问题,但是非对称加密算法很慢,比对称加密算法慢很多。
所以为了解决这个问题,我们可以使用非对称密钥
+对称密钥
结合的方式。
# 对称加密 + 非对称加密
对称加密的优点是速度比较快,非对称加密的优点是传输的内容不能被破解。所以结合两者的优点,使用非对称加密的方法将对称加密的密钥发送过去,之后就可以使用使用这个密钥,利用对称密钥来通信了。就比如说我将钥匙放进了保险柜,然后将保险柜寄给对方。
# 公钥的传输
中间人攻击:还有一个问题就是在使用非对称密钥的时候,首先需要将 Bill 的公钥给张大胖,那么在这个过程中,安全是没有保障的,中间人可以拦截到 Bill 的公钥,就可以对拦截到的公钥进行篡改。
但是怎么安全地分发公钥呢? 似乎又回到了最初的问题: 怎么安全的保护密钥?可是似乎和最初的问题还不一样,这一次的公钥不用保密,但是一定得有个办法声明这个公钥确实是Bill的, 而不是别人的。
在网络世界也可以建立一个这样的具备公信力的认证中心, 这个中心给大家颁发一个证书, 用于证明一个人的身份。这个证书里除了包含一个人的基本信息之外,还有包括最关键的一环:这个人的公钥!
证书怎么安全传输?
简单来讲是这样的, Bill可以把他的公钥和个人信息用一个Hash算法生成一个消息摘要, 这个Hash算法有个极好的特性,只要输入数据有一点点变化,那生成的消息摘要就会有巨变,这样就可以防止别人修改原始内容。
可是作为攻击者的中间人笑了: “虽然我没办法改公钥,但是我可以把整个原始信息都替换了, 生成一个新的消息摘要, 你不还是辨别不出来?”
数字签名 张大胖说你别得意的太早 , 我们会让有公信力的认证中心(简称CA
)用它的私钥对消息摘要加密,形成签名:
数字证书 这还不算, 还把原始信息和数据签名合并, 形成一个全新的东西,叫做“数字证书”:
张大胖接着说:当Bill把他的证书发给我的时候, 我就用同样的Hash 算法, 再次生成消息摘要,然后用CA的公钥对数字签名解密, 得到CA创建的消息摘要, 两者一比,就知道有没有人篡改了!
如果没人篡改, 我就可以安全的拿到Bill的公钥喽,有了公钥, 后序的加密工作就可以开始了。
这些CA本身也有证书来证明自己的身份,并且CA的信用是像树一样分级的,高层的CA给底层的CA做信用背书,而操作系统/浏览器中会内置一些顶层的CA的证书,相当于你自动信任了他们。 这些顶层的CA证书一定得安全地放入操作系统/浏览器当中,否则世界大乱。
# HTTPS流程图
一个简化的HTTPS流程图是这样的: