HTTP
“HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范”。
是一个请求响应协议,运行在TCP之上应用层协议。
在互联网世界里,HTTP 通常跑在 TCP/IP 协议栈之上,依靠 IP 协议实现寻址和路由、TCP 协议实现可靠数据传输、DNS 协议实现域名查找、SSL/TLS 协议实现安全通信。此外,还有一些协议依赖于 HTTP,例如 WebSocket、HTTPDNS 等。这些协议相互交织,构成了一个协议网,而 HTTP 则处于中心地位。
1. 方法
常用的请求方法:GET方法,POST方法,HEAD,PUT,DELETE,CONNECT,OPTIONS,TRACE
- GET 用于使用给定的URI从给定服务器中检索信息
- POST 用于将数据发送到服务器以创建或更新资源,请求不会被缓存,长度没有限制
- PUT 用于将数据发送到服务器以创建或更新资源,它可以用上传的内容替换目标资源中的所有当前内容。
区别:PUT方法是幂等的:
-
PUT请求:如果两个请求相同,后一个请求会把第一个请求覆盖掉。(所以PUT用来改资源)
-
POST请求:后一个请求不会把第一个请求覆盖掉。(所以Post用来增资源)
区别*2:
- GET参数通过URL传递,POST放在Request body中
- GET请求会被浏览器主动cache,而POST不会,除非手动设置
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
2. URL
HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。URL是一种特殊类型的URI,包含了用于查找某个资源的足够的信息,URL,全称是Uniform Resource Locator,中文叫统一资源定位符。
http://www.aspxfans.com:8080/news/index.asp?boardID=5&ID=24618&page=1#name
- 协议部分
- 域名部分
- 端口部分
- 虚拟目录部分
- 文件名部分
- 锚部分
- 参数部分
URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。
3. 请求Request
请求 URL: https://www.baidu.com/
请求方法: GET
状态代码: 200 OK
远程地址: 39.156.66.18:443
Accept: text/plain, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Connection: keep-alive
请求行:URL,方法和HTTP协议
请求头部:User-agent浏览器类型,Accept内容类型列表,Cookie:设置cookie,这是最重要的请求头信息之一,Host:初始URL中的主机和端口,Connection:表示是否需要持久连接“Keep-Alive”,请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接)
请求数据:post方法中,会把数据以key value形式发送请求
空行:
Get 参数少,大小有限制,地址栏显示内容
GET /562f25980001b1b106000338.jpg HTTP/1.1
Host img.mukewang.com
User-Agent Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
Accept image/webp,image/*,*/*;q=0.8
Referer http://www.imooc.com/
Accept-Encoding gzip, deflate, sdch
Accept-Language zh-CN,zh;q=0.8
Post相反
POST / HTTP1.1
Host:www.wrox.com
User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022)
Content-Type:application/x-www-form-urlencoded
Content-Length:40
Connection: Keep-Alive
name=Professional%20Ajax&publisher=Wiley
4. 响应Response
Cache-Control: private 缓存控制
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
状态行、消息报头、空行和响应正文
HTTP/1.1 200 OK
Date: Fri, 22 May 2009 06:07:21 GMT
Content-Type: text/html; charset=UTF-8
<html>
<head></head>
<body>
<!--body goes here-->
</body>
</html>
状态行:
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
---|---|
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
5. 打开网址的过程
- DNS服务器请求解析(检查host文件)
- 建立TCP连接,三次握手
- 发送HTTP请求,请求行、请求头部、空行和请求数据4部分组
- 服务器返回,服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成
- 主动释放or延时释放
- 浏览器进行渲染
HTTP协议版本对比
http 1.0
链接无法复用,即不支持持久链接:
http 1.0 规定浏览器与服务器保持较短时间的链接,浏览器每次请求都和服务器经过三次握手和慢启动(基本思想是当TCP开始传输数据或发现数据丢失并开始重发时,首先慢慢的对网路实际容量进行试探,避免由于发送了过量的数据而导致阻塞)建立一个TCP链接,服务器完成请求处理后立即断开TCP链接,而且不跟踪每个浏览器的历史请求。一次连接只能获取一个资源。
注意:由于http 1.0每次建立TCP链接对性能的影响实在是太大,http1.1实现持久化链接之后,又反向移植到http 1.0上,只是默认是没有开启持久链接的,通过http的header部分的 Connection: KeepAlive 来启用)
线头阻塞(Head of Line (HOL) Blocking)
请求队列的第一个请求因为服务器正忙(或请求格式问题等其他原因),导致后面的请求被阻塞。
http 1.1
支持持久连接,支持管道
通过http pipelining实现。多个http 请求可以复用一个TCP连接,服务器端按照FIFO原则来处理不同的Request。
HTTP/1.1 中的连接都会默认启用长连接。不需要用什么特殊的头字段指定,只要向服务器发送了第一次请求,后续的请求都会重复利用第一次打开的 TCP 连接,也就是长连接,在这个连接上收发数据。
缺点
- 一个请求响应阻塞,就会阻塞后续所有请求
- 并行处理请求时,服务器必须缓冲管道中的响应,从而占用服务器资源,如果有个响应非常大
- 响应失败可能终止 TCP 连接
http 1.1 增加了请求头和响应头来扩充功能
http 2.0
HTTP 2.0把解决性能问题的方案内置在了传输层,通过多路复用来减少延迟,通过压缩 HTTP首部降低开销,同时增加请求优先级和服务器端推送的功能。
支持多路复用
多路复用允许同时通过单一的 HTTP 2.0 连接发起多重的请求-响应消息,即所有HTTP 2.0 连接都是持久化的,而且客户端与服务器之间也只需要一个连接即可,所有数据流共用同一个连接 ,减少了因http链接多而引起的网络拥塞。
二进制分帧
即应用层(HTTP)和传输层(TCP or UDP)之间增加一个二进制分帧层,因此在多向请求和响应时,客户端和服务器可以把HTTP消息分解为互不依赖的帧,然后乱序发送,最后再在另一端把它们重新组合起来,解决了http 1.*的对手阻塞问题。
首部压缩
http 2.0支持DEFLATE和HPACK 算法的压缩
服务端推送
服务端推送是一种在客户端请求之前发送数据的机制
请求优先级
HTTP 2.0 使用一个31比特的优先值,0表示最高优先级, 2(31)-1表示最低优先级,服务器端就可以根据优先级,控制资源分配,优先处理和返回最高优先级的请求帧给客户端。
websocket与http
开始
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议。
HTTP的生命周期通过 Request
来界定,也就是一个 Request
一个 Response
,那么在 HTTP1.0
中,这次HTTP请求就结束了。
在HTTP1.1中进行了改进,使得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。但是请记住 Request = Response
, 在HTTP中永远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。
Websocket
Websocket是基于HTTP协议的,或者说借用了HTTP的协议来完成一部分握手。
首先我们来看个典型的 Websocket
握手(借用Wikipedia的。。)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
这段类似HTTP协议的握手请求中,多了几个东西
Upgrade: websocket
Connection: Upgrade
表示发起的是Websocket协议
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
验证信息
然后服务器会返回下列东西,表示已经接受到请求, 成功建立Websocket啦!
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
这里开始就是HTTP最后负责的区域了,告诉客户,我已经成功切换协议啦~
Upgrade: websocket
Connection: Upgrade
后面两个也是验证
至此,HTTP已经完成它所有工作了,接下来就是完全按照Websocket协议进行了。具体的协议就不在这阐述了。
Websocket的作用
HTTP 协议有一个缺陷:通信只能由客户端发起,这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。
轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)
ajax
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。
long poll
long poll
其实原理跟 ajax
轮询差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
场景:
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。
何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。
ajax轮询需要服务器有很快的处理速度和资源。(速度)long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小)
WebSocket
通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。
此外,HTTP还是一个状态协议。
通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务器一遍。
所以在这种情况下出现了,Websocket出现了。他解决了HTTP的这几个难题。首先:
被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端。
减少消耗,Websocket只需要一次HTTP握手,所以说整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性,服务端会一直知道你的信息,直到你关闭请求
HTTPS
端口:443
连接使用了SSL进行加密
HTTPS传输流程:
- 首先客户端通过URL访问服务器建立SSL连接。
- 服务端发送证书(公钥)到客户端
- 客户端解析证书,生成随机值(用于对称加密),使用公钥对随机值非对称加密
- 服务器使用私钥解密,把内容通过随机值加密
- 对称加密后的信息传输给客户端
- 客户端解密
代理
代理有很多的种类,常见的有:
- 匿名代理:完全“隐匿”了被代理的机器,外界看到的只是代理服务器;
- 透明代理:顾名思义,它在传输过程中是“透明开放”的,外界既知道代理,也知道客户端;
- 正向代理:靠近客户端,代表客户端向服务器发送请求;
- 反向代理:靠近服务器端,代表服务器响应客户端的请求;
特点
无状态
“状态”其实就是客户端或者服务器里保存的一些数据或者标志,记录了通信过程中的一些变化信息。
你一定知道,TCP 协议是有状态的,一开始处于 CLOSED 状态,连接成功后是 ESTABLISHED 状态,断开连接后是 FIN-WAIT 状态,最后又是 CLOSED 状态。
这些“状态”就需要 TCP 在内部用一些数据结构去维护,可以简单地想象成是个标志量,标记当前所处的状态,例如 0 是 CLOSED,2 是 ESTABLISHED 等等。
- HTTP 是灵活可扩展的,可以任意添加头字段实现任意功能;
- HTTP 是可靠传输协议,基于 TCP/IP 协议“尽量”保证数据的送达;
- HTTP 是应用层协议,比 FTP、SSH 等更通用功能更多,能够传输任意数据;
- HTTP 使用了请求 - 应答模式,客户端主动发起请求,服务器被动回复请求;
- HTTP 本质上是无状态的,每个请求都是互相独立、毫无关联的,协议不要求客户端或服务器记录请求相关的信息。
Body
数据类型与编码
客户端:Accept, Accept-Encoding, Accept-Language: zh-CN, zh, en, Accept-Charset: gbk, utf-8
服务端:Content-Type, Content-Encoding, Content-Language: zh-CN
MIME type指明了类型:
- text:即文本格式的可读数据,我们最熟悉的应该就是 text/html 了,表示超文本文档,此外还有纯文本 text/plain、样式表 text/css 等。
- image:即图像文件,有 image/gif、image/jpeg、image/png 等。
- audio/video:音频和视频数据,例如 audio/mpeg、video/mp4 等。
- application:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有 application/json,application/javascript、application/pdf 等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是 application/octet-stream,即不透明的二进制数据。
Encoding type就少了很多,常用的只有下面三种:
- gzip:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
- deflate:zlib(deflate)压缩格式,流行程度仅次于 gzip;
- br:一种专门为 HTTP 优化的新压缩算法(Brotli)。
内容协商的质量值
在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候,还可以用一种特殊的“q”参数表示权重来设定优先级,这里的“q”是“quality factor”的意思。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个“;”,然后是“q=value”。
在HTTP的内容协商里却恰好反了过来,“;”的断句语气是小于“,”的。
如何传输大文件
数据压缩
文本数据(text/html
)可以使用gzip进行压缩
分块传输
chunked分块编码传输可以用于大文件和流式数据传输
分块长度:length CRLF
分块数据:chunked data CRLF
分块长度:length CRLF
分块数据:chunked data CRLF
...
结束分块:0 CRLF
空行:CRLF
范围请求
服务器支持范围请求要发送Accept-Ranges: bytes
请求头Range是 HTTP 范围请求的专用字段,格式是“bytes=x-y”,其中的 x 和 y 是以字节为单位的数据范围。
有了范围请求之后,HTTP 处理大文件就更加轻松了,看视频时可以根据时间点计算出文件的 Range,不用下载整个文件,直接精确获取片段所在的数据内容。
断点续传
不仅看视频的拖拽进度需要范围请求,常用的下载工具里的多段下载、断点续传也是基于它实现的,要点是:
- 先发个 HEAD,看服务器是否支持范围请求,同时获取文件的大小;
- 开 N 个线程,每个线程使用 Range 字段划分出各自负责下载的片段,发请求传输数据;
- 下载意外中断也不怕,不必重头再来一遍,只要根据上次的下载记录,用 Range 请求剩下的那一部分就可以了。
多段数据
Range 头里使用多个“x-y”,一次性获取多个片段数据。
这种情况需要使用一种特殊的 MIME 类型:“multipart/byteranges”,表示报文的 body 是由多段字节序列组成的,并且还要用一个参数“boundary=xxx”给出段之间的分隔标记。
重定向
“Location”字段属于响应字段,必须出现在响应报文里。但只有配合 301/302 状态码才有意义,它标记了服务器要求重定向的 URI,浏览器会根据Location从字段里提取出URI发起新的HTTP请求。
301俗称“永久重定向”(Moved Permanently),意思是原 URI 已经“永久”性地不存在了,今后的所有请求都必须改用新的 URI。
302俗称“临时重定向”(“Moved Temporarily”),意思是原 URI 处于“临时维护”状态,新的 URI 是起“顶包”作用的“临时工”。
什么时候使用重定向?
- 资源不可用
- 重定向到主站
问题:
- 性能损耗,多使用了请求应答
- 循环跳转:自动跳出