前言
这篇文章主要是我平时在学习HTTP过程中看到的一些知识点,现在把他们总结成一篇文章,建立一个自己的知识体系,全是干货。另外,推荐一本非常棒的HTTP书——《图解HTTP》,这本书图文并茂,挺有趣的。
HTTP是基于TCP/IP协议的应用层协议,用于客户端和服务器之间的通信,默认80端口。我们按照他的发展历程的时间顺序开始说。
1. HTTP/0.9
1990年提出的,是最早期的版本,只有一个命令GET。
2. HTTP/1.0
1996年5月提出的。
-
缺点
:每个TCP连接只能发送一个请求。 -
解决方法
:Connection:keep-alive
3. HTTP/1.1
1997年1月提出,现在使用最广泛的。
3.1 特性
-
长连接
:TCP连接默认不关闭,可以被多个请求复用。对于同一个域名,大多数浏览器允许同时建立6个持久连接。默认开启Connection:keep-alive。 -
管道机制
:在同一个TCP连接里,可以同时发送多个请求。但是服务器还是要按照请求的顺序进行响应,会造成“队头阻塞”。
3.2 HTTP首部
HTTP首部分为请求报文和响应报文。它们的格式如下所示:
- 请求报文:
- 响应报文:
其中首部字段又分为很多种,我们先看通用首部字段,这是请求报文和响应报文种都会使用的首部。
3.2.1 通用首部字段
1、Cache-Control
:操作缓存的工作机制
参数:
- public:明确表明其他用户也可以利用缓存
- private:缓存只给特定的用户
- no-cache:客户端发送这个指令,表示客户端不接收缓存过的响应,必须到服务器取;服务器返回这个指令,指缓存服务器不能对资源进行缓存。其实是不缓存过期资源,要向服务器进行有效期确认后再处理资源。
- no-store:指不进行缓存
- max-age:缓存的有效时间(相对时间)
2、Connection
:
- Connection:keep-Alive (持久连接)
- Connection:不再转发的首部字段名
3、Date
:表明创建http报文的日期和时间
4、Pragma
:兼容http1.0,与Cache-Control:no-cache含义一样。但只用在客户端发送的请求中,告诉所有的中间服务器不返回缓存。形式唯一:Pragma:no-cache
5、Trailer
:会事先说明在报文主体后记录了哪些首部字段,该首部字段可以应用在http1.1版本分块传输编码中。
6、Transfer-Encoding
:chunked (分块传输编码),
规定传输报文主体时采用的编码方式,http1.1的传输编码方式只对分块传输编码有效
7、Upgrade
:升级一个成其他的协议,需要额外指定Connection:Upgrade。服务器可用101状态码作为相应返回。
8、Via
:追踪客户端和服务器之间的请求和响应报文的传输路径。可以避免请求回环发生,所以在经过代理时必须要附加这个字段。
3.2.2 请求首部字段
1、Accept:通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级
q表示优先级的权重值,默认为q = 1.0,范围是0~1(可精确到小数点后3位,1为最大值)
当服务器提供多种内容时,会先返回权重值最高的媒体类型
2、Accept-Charset:支持的字符集及字符集的相对优先顺序,跟Accept一样,用q来表示相对优先级。这个字段应用于内容协商机制的服务器驱动协商。
3、Accept-Encoding:支持的内容编码及内容编码的优先级顺序,q表示相对优先级。
内容编码:gzip、compress、deflate、identity(不执行压缩或者不会变化的默认编码格式)。
可以使用*作为通配符,指定任意的编码格式。
4、Accept-Language:能够处理的自然语言集,以及相对优先级。
3.3 状态码
101 协议升级
,主要用于升级到websocket,也可以用于http2
200 OK
204 No content
,服务器成功处理请求,但是返回的响应报文中不含实体的主体部分
206 Partial Content
,表示客户端像服务器进行了范围请求(Content-Range字段),服务器成功返回指定范围的实体内容
301 永久性重定向
,表示请求的资源已经被分配了新的url,旧地址以后都不能再访问了,服务器会返回location字段,包含的是新的地址。
302 临时性重定向
,表示请求的资源临时移动到一个新地址
注意:尽量使用301跳转,因为302会造成网址劫持,可能被搜索引擎判为可疑转向,甚至认为是作弊。
原因:从网站A(网站比较烂)上做了一个302跳转到网站B(搜索排名很靠前),这时候有时搜索引擎会使用网站B的内容,但却收录了网站A的地址,这样在不知不觉间,网站B在为网站A作贡献,网站A的排名就靠前了。
303 See Other
,与302功能相同,但是它明确规定客户端应采用GET方法获取资源
304 未修改
,协商缓存中返回的状态码
307 临时重定向
,与302功能相同,但规定不能从POST变成GET
当301、302、303响应状态码返回时,几乎所有浏览器都会把post改成get,并删除请求报文内的主体,之后请求会自动再次发送。然而301、302标准是禁止将post方法改变成get方法的,但实际使用时大家都会这么做。所以需要307。
400 Bad Request
,表示请求报文中存在语法错误。当错误发生时,需要修改请求的内容再次发送请求
401 unauthorized
,表示发送的请求需要有通过HTTP认证(BASIC认证、DIGEST认证)的认证信息。如果之前已经进行过一次请求,表示用户认证失败。
403 禁止
,表示拒绝对请求资源的访问
404 Not Found
,表明服务器上无法找到请求的资源
500 Internet Server Error
,该状态码表示服务器在执行请求时发生了错误
500 Service Unavailable
,表示服务器暂时处于超负荷或者处于停机维护状态,现在无法处理请求
4. SPDY协议
2009年谷歌提出。
-
SPDY结构
:
-
新增特性
:
(1)多路复用:通过一个TCP连接,可以无限制处理多个HTTP请求。
(2)赋予请求优先级:给请求逐个分配优先级顺序。可以解决在发送多个请求时,因带宽低而导致响应变慢的问题。
(3)压缩HTTP首部:压缩方式:DELEFT
(4)推送功能
(5)服务器提示功能:服务器可以主动提示客户端请求所需的资源。
-
缺点
:
SPDY强制使用https。而且SPDY基本上只是将单个域名下的通信多路复用,所以当一个web网站上使用多个域名下的资源时,改善效果就会受到限制。
5. WebSocket
html5新提出来的,是web浏览器与web服务器之间的全双工通信标准。主要是为了解决ajax和comet里的xmlhttprequest附带的缺陷所引起的问题。
5.1 特性
(1)推送功能:服务器可直接发送数据,不需要等待客户端的请求;
(2)基于TCP传输协议,并复用HTTP的握手通道;
(3)支持双向通信,用于实时传输消息;
(4)更好的二进制支持;
(5)更灵活,更高效。
5.2 建立连接过程
1、客户端:发起协议升级请求
GET / HTTP/1.1 `采用HTTP报文格式,只支持get请求`
Host: localhost:8080
Origin: http://127.0.0.1:3000
Connection: Upgrade `表示要升级协议`
Upgrade: websocket `表示升级到websocket协议`
Sec-WebSocket-Version: 13 `表示websocket 的版本`
Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw== `是一个 Base64 encode 的值,是浏览器随机生成的`
Sec-WebSocket-Protocol:chat, superchat `用来指定一个特定的子协议,一旦这个字段有设置,那么服务器需要在建立连接的响应头中包含同样的字段,内容就是选择的子协议之一。`
2、服务端:响应协议升级
HTTP/1.1 101 Switching Protocols `101表示协议切换==`
Connection:Upgrade
Upgrade: websocket
Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU= `经过服务器确认,并且加密过后的 Sec-WebSocket-Key`
Sec-WebSocket-Protocol:chat `表示最终使用的协议`
Sec-WebSocket-Key 的加密过程为:
- 将Sec-WebSocket-Key跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
- 通过SHA1计算出摘要,并转成base64字符串。
3、双方握手成功后,就是全双工的通信了,接下来就是用websocket协议来进行通信了。
5.3 Ajax 轮询、长轮询、WebSocket原理解析
1、ajax轮询
让浏览器每隔一定的时间就发送一次请求,询问服务器是否有新信息。
2、长轮询(Long Poll)
采用的阻塞模式。客户端发起连接后,如果没消息,服务器不会马上告诉你没消息,而是将这个请求挂起(pending),直到有消息才返回。返回完成或者客户端主动断开后,客户端再次建立连接,周而复始。Comet就是采用的长轮询。
3、websocket
WebSocket 是类似 Socket 的TCP长连接通讯模式。一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。而且浏览器和服务器就可以随时主动发送消息给对方,是全双工通信。
优点:在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。
6. HTTP2
2015年发布,它是基于SPDY的,以下是它的一些新特性:
1、 二进制分帧
:
http/1.x 是一个超文本协议,而 http2 是一个二进制协议,被称之为二进制分帧。
二进制格式在协议的解析和优化扩展上带来更多的优势和可能。
协议格式为帧,帧由 Frame Header(头信息帧)和 Frame Payload(数据帧)组成,如下所示:
- Length 字段用来表示 Frame Payload 数据大小。
- Type 字段用来表示该帧中的 Frame Payload 保存的是 header 数据还是 body 数据。除了用于标识 header/body,还有一些额外的 Frame Type。
- Stream Identifier 用来标识该 frame 属于哪个 stream。
- Frame Payload 用来保存 header 或者 body 的数据。
2、头部压缩 HPACK
:
请求和响应首部压缩,客户端和服务端共同维护一张头信息表,所有字段存入这个表,生成一个索引号,通过发送索引号提高速度。HPACK压缩会经过两步:
- 传输的value,会经过一遍Huffman coding来节省资源;
- 为了server和client同步, 两边都需要保留一份Header list, 并且,每次发送请求时,都会检查更新。
3、服务端推送
:
服务端主动向客户端推送数据。如果客户端请求一个html文件,服务端把html文件返回给客户端之后,还会相应的把html文件中的js、css、图片推送给客户端。
4、多路复用
:
只需要建立一个TCP连接,浏览器和服务器可以同时发送多个请求或者回应,而且不需要按照顺序一一对应,避免了“队头阻塞”。
5、数据流
:
当客户端同时向服务端发起多个请求,那么这些请求会被分解成一一个的帧,每个帧都会在一个 TCP 链路中无序的传输,同一个请求的帧的 Stream Identifier 都是一样的。当帧到达服务端之后,就可以根据 Stream Identifier 来重新组合得到完整的请求。
并且规定:客户端发出的数据流ID为奇数,服务器发出的ID为偶数。Stream Identifier (数据流ID)就是用来标识该帧属于哪个请求的。
7. HTTPS
HTTPS = HTTP+加密+认证+完整性保护它的加密过程是:
- server生成一个公钥和私钥,把公钥发送给第三方认证机构(CA);
- CA把公钥进行MD5加密,生成数字签名;再把数字签名用CA的私钥进行加密,生成数字证书。CA会把这个数字证书返回给server;
- server拿到数字证书之后,就把它传送给浏览器;
- 浏览器会对数字证书进行验证,首先,浏览器本身会内置CA的公钥,会用这个公钥对数字证书解密,验证是否是受信任的CA生成的数字证书;
- 验证成功后,浏览器会随机生成对称秘钥,用server的公钥加密这个对称秘钥,再把加密的对称秘钥传送给server;
- server收到对称秘钥,会用自己的私钥进行解密,之后,它们之间的通信就用这个对称秘钥进行加密,来维持通信。
下图是加密过程的图解,可以对照着图片理一遍。
8. HTTP缓存机制
8.1 缓存分类
HTTP的缓存分为强缓存和协商缓存(对比缓存)。
-
强制缓存
在缓存数据未失效的情况下,可以直接使用缓存数据;在没有缓存数据的时候,浏览器向服务器请求数据时,服务器会将数据和缓存规则一并返回,缓存规则信息包含在响应header中。
- Expires:缓存过期时间(HTTP1.0)
缺点:生成的是绝对时间,但是客户端时间可以随意修改,会导致误差。
- Cache-Control :HTTP1.1,优先级高于Expires
可设置参数:
private: 客户端可以缓存
public: 客户端和代理服务器都可缓存
max-age=xxx: 缓存的内容将在 xxx 秒后失效
no-cache: 需要使用协商缓存来验证缓存数据(后面介绍)
no-store: 所有内容都不会缓存,强制缓存,对比缓存都不会触发
Expires和Cache-Control决定了浏览器是否要发送请求到服务器,ETag和Last-Modified决定了服务器是要返回304+空内容还是新的资源文件。
协商缓存
浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给客户端,客户端将二者备份至缓存数据库中。再次请求数据时,客户端将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。
- Last-Modified / If-Modified-Since
Last-Modified
:服务器在响应请求时,告诉浏览器资源的最后修改时间。
If-Modified-Since
:再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源最后修改时间。
缺点:Last-Modified 标注的最后修改时间只能精确到秒,如果有些资源在一秒之内被多次修改的话,他就不能准确标注文件的新鲜度了。如果某些资源会被定期生成,当内容没有变化,但 Last-Modified 却改变了,导致文件没使用缓存有可能存在服务器没有准确获取资源修改时间,或者与代理服务器时间不一致的情形。
- Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)
Etag
:给资源计算得出的一个唯一标志符。
If-None-Match
:再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识。
8.2 缓存判断顺序
- 先判断Cache-Control,在Cache-Control的max-age之内,直接返回200 from cache;
- 没有Cache-Control再判断Expires,再Expires之内,直接返回200 from cache;
- Cache-Control=no-cache或者不符合Expires,浏览器向服务器发送请求;
- 服务器同时判断ETag和Last-Modified,都一致,返回304,有任何一个不一致,返回200。
具体过程如下图:
8.3 cookie、session
8.3.1 cookie
解决http的无状态问题,是客户端保存用户信息的一种机制,用来记录用户的一些信息,来实现session的跟踪。
- cookie属性
name、value
:以key/value的形式存在
comment
:说明该cookie的用处
domain
:可以访问该cookie的域名
Expires/maxAge
:cookie失效时间。负数:临时cookie,关闭浏览器就失效;0:表示删除cookie,默认为-1
path
:可以访问此cookie的页面路径
size
:cookie的大小
secure
:是否以https协议传输
version
:该cookie使用的版本号,0遵循Netscape规范,大多数用这种,1遵循W3C规范
HttpOnly
:此属性为true,则只有在http请求头中会带有此cookie的信息,而不能通过document.cookie来访问此cookie,能防止XSS攻击。
- cookie机制原理:
客户端请求服务器时,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。而客户端浏览器会把Cookie保存起来。当浏览器再请求服务器时,浏览器把请求的网址连同该Cookie一同提交给服务器。服务器通过检查该Cookie来获取用户状态。
- cookie同源和跨域:
cookie的同源是域名相同,忽略协议和端口,不可跨域。
8.3.2 session
session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。
- session的运行依赖session id,而session id是存在cookie中的
- session机制原理:
当客户端请求创建一个session的时候,服务器会先检查这个客户端的请求里是否已包含了一个session标识——sessionId。如果已包含这个sessionId,则说明以前已经为此客户端创建过session,服务器就按照sessionId把这个session检索出来使用(如果检索不到,可能会新建一个。如果客户端请求不包含sessionId,则为此客户端创建一个session并且生成一个与此session相关联的sessionId。
- 如果禁用cookie怎么办?
使用URL重写技术来进行会话跟踪。在 url 中传递 session id,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。
8.3.3 区别
- cookie和session的区别
- cookie 数据存放在客户的浏览器上,session数据放在服务器上;
- cookie 不是很安全,别人可以分析存放在本地的 cookie 并进行 cookie 欺骗考虑到安全应当使用 session;
- session 会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用 cookie;
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个 cookie。
鉴于上述区别我们建议:
(1)将登陆信息等重要信息存放为 session;
(2)其他信息如果需要保留,可以放在 cookie 中。
- localStorage,sessionStorage和cookie的区别
共同点:都是保存在浏览器端、且同源的。
-
数据存储方面
- cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。
- sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。
-
存储数据大小
- 存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。
- sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
-
数据存储有效期
- sessionStorage:仅在当前浏览器窗口关闭之前有效;
- localStorage:始终有效,窗口或浏览器关闭也一直保存,本地存储,因此用作持久数据;
- cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭。
-
作用域不同
- sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;
- localstorage在所有同源窗口中都是共享的;也就是说只要浏览器不关闭,数据仍然存在。
- cookie: 也是在所有同源窗口中都是共享的.也就是说只要浏览器不关闭,数据仍然存在。
9. 跨域
跨域产生的原因,是因为受到同源策略的限制。同源策略指的是协议、域名、端口不相同。这里我将介绍三种跨域的方式:JSONP、CORS(跨域资源共享)、document.domain + iframe。
9.1 JSONP
1. 原理
动态插入script标签(因为script标签不受同源策略的限制),通过插入script标签引入一个js文件,这个js文件加载成功之后会执行我们在url中指定的回调函数,并且会把我们需要的json数据作为参数传入。
2. 实现
(1)原生实现:
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调函数
function onBack(res) {
alert(JSON.stringify(res));
}
//服务端返回如下(返回时即执行全局函数):
onBack({"status": true, "user": "admin"})
(2)jquery ajax:
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: "onBack", // 自定义回调函数名
data: {}
});
9.2 CORS
1. 原理
服务器在响应头中设置相应的选项,浏览器如果支持这种方法的话就会将这种跨站资源请求视为合法,进而获取资源。
2. 实现
CORS分为简单请求和复杂请求,简单请求指的是:
(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)。
其他情况就是非简单请求了。
- 简单请求
(1)请求头
:
Origin: http://www.domain.com
(2)响应头
:
Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Credentials: true `是否允许传送cookie`
Access-Control-Expose-Headers: FooBar `CORS请求时,只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须指定。`
(3)另外,ajax请求中
,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名,还要设置以下内容:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
- 非简单请求
(1)预检请求
:
OPTIONS /cors HTTP/1.1 `OPTIONS请求是用来询问的`
Origin: http://www.domian.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
(2)响应头
:
Access-Control-Allow-Origin: http://www.domain.com
Access-Control-Allow-Methods: GET, POST, PUT `服务器支持的所有跨域请求的方法`
Access-Control-Allow-Headers: X-Custom-Header `服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。`
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000 `指定本次预检请求的有效期,单位为秒`
(3)之后的步骤就同简单请求了
。
这是CORS的整个流程图:
与JSOP的比较:
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
9.3 document.domain + iframe
此方案仅限主域相同,子域不同的跨域应用场景。
1.原理
两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
2.实现
(1)父窗口:(www.domain.com/a.html)
<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>
<script>
document.domain = 'domain.com';
var user = 'admin';
</script>
(2)子窗口:(child.domain.com/b.html)
<script>
document.domain = 'domain.com';
// 获取父窗口中变量
alert('get js data from parent ---> ' + window.parent.user);
</script>