你应该知道的http

代码已经关联到github: 链接地址 文章有更新也会优先在这,觉得不错可以顺手点个star,这里会持续分享自己的开发经验(:

HTTP全称超文本传输协议(HyperText Transfer Protocol),HTTP 是一种能够获取像 HTML、图片等网络资源的通讯协议(protocol)。它是在 web 上进行数据交换的基础,是一种 client-server 协议。

HTTP基础特性

1. 简单可拓展协议

组成的部分简单,起始行 + 头部 + 空行 + 实体
HTTP 1.0 出现的 HTTP headers 让协议拓展变得更加的容易。只要服务端和客户端就 headers 达成语义一致,新功能就可以被轻松的加入进来。

2. 请求-应答

也就是一发一收、有来有回, 当然这个请求方和应答方不单单指客户端和服务器之间,如果某台服务器作为代理来连接后端的服务端,那么这台服务器也会扮演请求方的角色。

3. 无状态、有会话的

无状态:每个 HTTP 请求都是独立的,隔离的,自解释的。
有会话:为了解决无状态,如cookie、token和session。

4. http与连接

HTTP 与连接。通过 TCP,或者 TLS(加密的 TCP )连接来发送,理论上任何可靠的传输协议都可以使用。连接是传输层控制的,这从根本上来讲不是 HTTP 的范畴。

缺点

1.无状态

很多情况需要有状态,比如登录等,会话解决了这一问题

2.明文传输

HTTP 2.0之前报文头部是使用文本传输

3.队头阻塞

因为 HTTP 规定报文必须是“一发一收”,这就形成了一个先进先出的“串行”队列,请求->响应->请求->响应…,后一个请求必须在前一个响应之后发送,这就导致前一个请求未响应导致后续的请求阻塞,这就是队头阻塞。
解决的方法在不同的版本不一样,HTTP 1.1使用并发长连接缓解这个问题,同时可以用域名切片还提高长连接的并发数量。HTTP 2.0使用一个域名单一TCP连接发送请求,请求包被二进制分帧,不同请求可以互相穿插,避免了HTTP层面的请求队头阻塞。

注意:TCP也存在队头阻塞,因为TCP是有序传包,传输阶段如果发生丢包,即使后续的数据包已经到达,也需要等待,这就产生了阻塞。这就不是HTTP层面可以解决的。

HTTP组件系统

1. 客户端 user-agent
2. Web服务端
3. 代理(Proxies)

HTTP请求流程

image.png

1. 构建请求

首先,浏览器会解析URL,构建请求行信息 GET /index.html HTTP1.1 ,构建好后,浏览器准备发起网络请求。
RFC规定了url只能使用英文字母、数字和部分符号,所以这里会对请求数据进行编码解析,也就是对中文和其他字符的过滤。
URL的默认解析使用的是百分比编码,这也是一般get请求的编码;但为了保证不同浏览器的编码的一致性,我们可以自行将参数编码,比如使用encodeURIComponent或者encodeURI
encodeURI方法不会对下列字符编码 ASCII字母 数字 ~!@#$&*()=:/,;?+'encodeURIComponent方法不会对下列字符编码 ASCII字母 数字 ~!*()'

2.查找缓存

览器发现请求的资源已经在浏览器缓存中存有副本,它会拦截请求,返回该资源的副本,并直接结束请求,提高性能。

3.准备IP地址和端口

**HTTP**是应用层协议,浏览器需要通过 TCP来与服务器来建立连接,所以要准备好IP和端口(注意这里会有域名系统DNS)

DNS查找
  • 先查找浏览器缓存
  • 然后是本机hosts,如果还是没有,则查找本地DNS服务器。
  • 本地DNS服务器一般是我们自己指定的地址(当然这里还有个路由器的缓存)。
  • 再查找不到,则向根服务器发送递归查找,边缘DNS > 顶级DNS > 二级DNS > 三级DNS(也就是网站注册的)
4.等待TCP队列

不同浏览器,同时建立的TCP的连接是又限制的,如Chrome,同一个域名同时最多只能并发6个TCP连接

5.建立TCP链接

三次握手建立连接

  1. 男孩写信给女孩【seq=x(序列号)、SYN=1(同步序号、1表示请求建立连接)】
  2. 女孩收到回信给男孩确认&同意建立链接【seq=y(序列号)、SYN=1(同步序号、1表示请求建立连接)、ack=x+1(确认号,序列号+1,表示收到请求)】
  3. 男孩收到,双方建议关系【seq=x+1、ack=y+1(确认号,表示收到请求)】防止中间出错(超时)就可以重传
6.发送HTTP请求

浏览器构造请求报文,与服务器开始通信。

7.服务器处理请求

对请求报文进行解析,并获取请求的资源及请求方法等相关信息,根据方法,资源,请求头和可选的请求体部分对请求进行处理,最后开始访问资源。

8.服务器响应请求

访问完资源后,就执行请求方法中描述的动作,并返回_响应报文_。

9.断开链接

四次挥手断开链接

  1. 男孩提分手【发出请求】【seq=x(序列号)、FIN=1(终止序号、1表示请求释放连接)】
  2. 女孩回复等我收拾完【确认请求】【seq=y(序列号)、ack=x+1】
  3. 收拾完后再回复分手【资源释放完,同意释放链接】【seq=z(序列号)、ack=x+1、FIN=1】
  4. 男孩收到,双方分手【收到&防止出错】【seq=u(序列号)、ack=z+1】

HTTP 报文

1.HTTP请求和响应(报文)

image.png

  • 请求报文 Requests

请求起始行(start-line):请求方法、请求路径 和HTTP 版本号。
消息头(HTTP headers): 也叫请求头,该请求的描述信息,服务器会以此确定行为,(content-type和content-length是对请求体的描述)。
空行(empty line): 发送回车符和换行符,通知服务器以下不再有请求头。
请求体 (body):get请求不存在,一般存在post请求中,向服务器提供请求数据,get可以直接在url中拼接数据。

  • 响应报文 Responses

响应起始行(start-line):也较做状态行,HTTP 版本号、响应状态码以及状态文本描述
消息头(HTTP headers): 也叫响应头,该响应的描述信息,会规定请求体的数据格式(content-type和content-length是对响应体的描述)。
空行(empty line): 发送回车符和换行符,通知服务器以下不再有响应头。
响应体 (body):服务端返回的数据。

2.方法
  • GET:请求一个指定资源的表示形式. 使用GET的请求应该只被用于获取数据
  • HEAD:请求一个与GET请求的响应相同的响应,只返回了头部,但没有响应体.
  • POST:用来向服务器中输入数据的。通常我们提交表单数据给服务器。
  • PUT:向服务器中写入文档。语义:用请求的主体部分来创建一个由所请求的 URL 命名的新文档
  • DELETE:删除指定的资源。
  • CONNECT:建立一个到由目标资源标识的服务器的隧道。
  • OPTIONS:预检请求,跨域时存在,用于描述目标资源的通信选项。
  • TRACE:诊断,沿着到目标资源的路径执行一个消息环回测试。
  • PATCH :用于对资源应用部分修改。

GET和POST的区别
都是HTTP请求,能做的事情一样,只不过是请求的规范,就好像我们去北京,可以火车、高铁,还可以是飞机。

  • 核心的区别在于get是幂等的,也就是调用多少次接口,结果都不会改变,而post不是
  • 编码不一样,但其实是url(ASCII)和body(任意binary)的编码是不一样的
  • get的请求长度有限?其实是url的长度有限
  • 两次请求?其实是客户端决定的。比如上传先传请求头,服务端判断通过返回100- Continue
3.状态码
  • 100~199——信息性状态码
    101 Switching Protocols。在HTTP升级为WebSocket的时候,如果服务器同意变更,就会发送状态码 101。
  • 200~299——成功状态码
    200 OK,表示从客户端发来的请求在服务器端被正确处理
    204 No content,表示请求成功,但响应报文不含实体的主体部分
    205 Reset Content,表示请求成功,但响应报文不含实体的主体部分,但是与 204 响应不同在于要求请求方重置内容
    206 Partial Content,进行范围请求
  • 300~399——重定向状态码
    301 moved permanently,永久性重定向,表示资源已被分配了新的 URL
    302 found,临时性重定向,表示资源临时被分配了新的 URL
    303 see other,表示资源存在着另一个 URL,应使用 GET 方法获取资源
    304 not modified,服务端已经执行了GET,但文件未变化。缓存
    307 temporary redirect,临时重定向,和302含义类似,但是期望客户端保持请求方法不变向新的地址发出请求
  • 400~499——客户端错误状态码
    400 bad request,请求报文存在语法错误
    401 unauthorized,表示发送的请求需要有通过 HTTP 认证的认证信息
    403 forbidden,表示对请求资源的访问被服务器拒绝
    404 not found,表示在服务器上没有找到请求的资源
  • 500~599——服务器错误状态码
    500 internal sever error,表示服务器端在执行请求时发生了错误
    501 Not Implemented,表示服务器不支持当前请求所需要的某个功能
    502 Bad Gateway, 连接超时(仅针对当前连接)
    503 service unavailable,表明服务器暂时处于超负载或正在停机维护,无法处理请求
4.消息头

HTTP 消息头允许客户端和服务器通过** request response**传递附加信息。一个请求头由名称(不区分大小写)后跟一个冒号“:”,冒号后跟具体的值(不带换行符)组成。该值前面的引导空白会被忽略。

  • 通用首部(General headers)同时适用于请求和响应消息,但与最终消息主体中传输的数据无关的消息头。如 Date
  • 请求首部(Request headers)包含更多有关要获取的资源或客户端本身信息的消息头。如 User-Agent
  • 响应首部(Response headers)包含有关响应的补充信息
  • 实体首部(Entity headers)含有关实体主体的更多信息,比如主体长(Content-Length)度或其 MIME 类型。如 Accept-Ranges

我们常见的有:

数据格式

请求方字段名为accept,响应端为**content-type **一般值为 text/htmlapplication/jsonx-www-form-urlencodedmultipart/form-data 等。

压缩方式

对数据进行的编码压缩方式,请求方字段名Accept-Encoding,响应方为Content-Encoding。取值有:gzipdeflatebr

支持语言

请求方字段名Accept-Language,响应方为Content-Language。取值有:zh-CN, zh, en。

支持字符集

文件的编码,请求方字段名Accept-Charset,响应方放在了为Content-Type。取值有:utf-8等。

定长和不定长的数据

对于定长的数据,响应端可以指定返回数据的长度Content-Length
而对于不定长的数据,响应端会使用另一个字段Transfer-Encoding: chunked,用来标识分块传输数据:

  • Content-Length 字段会被忽略
  • 基于长连接持续推送动态内容 Connection: keep-alive
  • 最后一个大小为0的块为结束
大文件传输

对于大文件,HTTP可以采取范围请求的方案,具体为:

服务端加上响应头:

//表示是范围请求
Status Code: 206
//表示支持格式为字节的范围请求
Accept-Ranges: byte 
//当前接受的范围(0-100)和文件总大小(22400)
Content-Range: bytes 0-100/22400

客户端可以在请求头中:

// 单段数据
Range: bytes=0-100
// 多段数据
Range: bytes=0-100, 100-200

其他可查看Http Header

HTTP2中的帧

HTTP2.0中,消息不再以报文传输,而是被封装成了多个帧,由 HEADER帧+ 若干个 DATA 帧 组成的,除此之外还有SETTINGS、PING、GOWAY、WINDOW_UPDATE等控制帧。所有帧都以固定的 9 字节大小的头作为帧开始,后续的组成有:

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+
  • Length

帧有效负载的长度,使用无符号的24位整数标识,帧头的 9 个八位字节不包含在此长度值中。

  • Flags

这个字段是为特定于帧类型的布尔标志保留的 8 位字段,为标志分配特定于指示帧类型的语义,比如已经是该流的最后一帧。

  • Type
    这 8 位用来表示帧类型的。帧类型确定帧的格式和语义。实现方必须忽略并丢弃任何类型未知的帧。
  • stream标识符

stream 流使用无符号的 31 位整数标识。由客户端发起的流必须使用奇数编号的流标识符;那些由服务器发起的必须使用偶数编号的流标识符。流标识符零(0x0)用于连接控制消息。也是实现多路复用的关键,接收端的实现可以根据这个 ID 并发组装消息,但是注意同一个 stream 内 frame 必须是有序的。

HTTP发展史

1. HTTP 0.9

单行协议,只有 get 请求,且也只有请求起始行,响应则只有返回的数据。

2. HTTP 1.0

提高了扩展性。增加了协议版本、状态码和消息头(如content-type)。
但是存在痛点:

  • TCP 连接无法复用。
  • HTTP 队头阻塞,一个 HTTP 请求响应结束之后,才能发起下一个 HTTP 请求。
  • 一台服务器只能提供一个 HTTP 服务。
  • 不支持动态内容。
3. HTTP 1.1 97年

改进的版本,连接可复用,增加了缓存及控制策略、管道技术、响应分块和host头,

  • 长连接HTTP/1.1 默认开启长连接Connection: keep-alive,在 TCP 连接建立后不立即关闭,让多个 HTTP 请求得以复用。
  • 支持请求管线化(pipelining)HTTP1.1中试图通过管线化的技术来解决队头阻塞的问题。HTTP/1.1中的管线化请求可以并行发出,但是响应必须串行返回。后一个响应必须在前一个响应之后。原因是,没有序号标明顺序,只能串行接收。也正式由于这个串行接收的原因,前面的响应未及时返回,会造成后续的响应被阻塞,所以大部分浏览器均未支持该功能。
  • Host头处理,也就是域名分片的原理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
  • 支持部分响应和响应分块HTTP1.0中,不能只获取某个对象的一部分数据。HTTP1.1则在请求头引入了Range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content)。而对较大的数据,HTTP1.1则在请求头引入了Transfer-Encoding头域,将数据分块传输。
  • 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则增加 Cache-ControlE-Tag 缓存头。
  • 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。

存在问题:

  • ​同时开启了多条TCP连接,那么这些连接会竞争固定的带宽
  • 队头阻塞的问题。
4. HTTP 2.0 2015年

流用来承载消息,消息又是有一个或多个帧组成,而帧就是HTTP通信的最小单位。HTTP 2.0 中的帧将 HTTP/1.x 消息分成帧并嵌入到流 (stream) 中,做到了并发请求。

  • 二进制传输,将之前文本传输修改成了二进制帧传输,原来Headers + Body的报文格式如今被拆分成了一个个二进制的帧,用Headers帧存放头部字段,Data帧存放请求体数据。
  • 头部信息压缩,客户端和服务器之间建立字典,常见的头部字段都存在其中,传输只需要传索引。其次对于静态、动态字典中不存在的内容,还可以使用哈夫曼编码来减小体积。
  • 多路复用,通信双方都可以给对方发送二进制帧,这种二进制帧的双向传输的字节流,也叫做流(Stream),每个流都有唯一的ID,且会对帧进行顺序标识和增加流标识符(stream_id) ,保证接收方收到数据后对数据进行合并可得到正常的数据。
  • 服务器推送,服务器端推送使得服务器可以预测客户端需要的资源,主动推送到客户端。实现原理就是客户端发出页面请求时,服务器 端能够分析这个页面所依赖的其他资源,主动推送到客户端的缓存,当客户端收到原始网页的请求时,它需要的资源已经位于缓存。

5. HTTPS

HTTPS是身披SSL外壳的HTTP。HTTP协议 + SSL/TLS协议,在HTTPS数据传输的过程中,需要用SSL/TLS对数据进行加密和解密

  • 内容加密:采用混合加密技术,客户端使用对称加密生成的密钥加密传输的数据,使用非对称加密的公钥加密对称加密的密钥,所以网络上传输的数据是被秘钥加密的密文和用公钥加密后的秘密秘钥
  • 验证身份:通过CA证书认证客户端访问的是自己的服务器
  • 保护数据完整性:采用数字签名的方式验证,防止传输的内容被中间人冒充或者篡改

加密的握手过程:

  1. 客户端发送ClientHello,包含:支持的协议版本、加密算法和随机数1
  2. 服务端收到后:
    • 向客户端发送 Server Hello ,发送加密套件(加密协议、加密算法、hash算法)和随机数2
    • 向客户端发送 Certificate 消息,返回数字证书(包含:公钥、网站地址、证书机构和过期日期)。
    • 向客户端发送可选的消息 Certificate Request,验证客户端的证书。
    • 向客户端发送可选的消息Server key exchange,如果来自证书的公钥信息不足以进行密钥交换,则服务器向客户端发送服务器密钥交换消息。例如,在基于 Diffie-Hellman (DH) 的密码套件中,此消息包含服务器的 DH 公钥。
    • 向客户端发送 Server Hello Done 消息,通知服务端已经发送了全部的相关信息
  3. 客户端验证证书的合法性(由客户端SSL/TLS完成)
    • 验证通过后客户端发送Client key exchange,然后生成一个随机数3,对于RSA算法,会将解析证书得到的公钥在随机数3加密生成PreMaster Key返回到服务端,对于DH算法,此消息包含客户端的DH公钥。
    • 发送可选的Certificate verify,如果服务端有要求客户端提供证书,则此处会发送,并且会带上加密散列函数进行数字签名的信息。
    • 向服务端发送Change cipher spec,通知服务端后续采用加密通信。
    • 向服务端发送Finished,通知服务端已准备好加密通信,这是第一条加密的消息。
    • 如果验证不通过,提示证书存在问题
  4. 服务器收到客户端消息后用私钥解析PreMaster Key得到随机数3 ,然后根据随机数1、2、3生成会话密钥K(约定好的算法就可以,比如之前的hash函数)
    • 向客户端发送Change cipher spec,通知客户端后续采用加密通信。
    • 向客户端发送Finished,通知客户端已准备好加密通信,SSL握手结束。

注意:

  • 从握手过程可以看出,HTTPS除了三次握手外,还额外多加了四次握手。
  • 如果每次重连都要重新握手还是比较耗时的,所以可以对握手过程进行优化。从下图里我们看到 Client Hello 消息里还附带了上一次的 Session ID,服务端接收到这个 Session ID 后如果能复用就不再进行后续的握手过程。
  • 上述描述的握手过程是RSA算法进行密钥交互,缺点是会将PreMaster Key在握手过程中传输,另外一种DH加密则可以避免,其不同点在于DH加密客户端和服务端的是交换公钥,后续根据客户端私钥和服务端公钥生成对称加密使用的密钥、服务端根据服务端私钥和客户端公钥生成对称加密使用的密钥进行通信。详情可见知乎上的讨论(DH算法在混合加密中,到底起什么作用

重点在于:

  • 用CA证书保证非对称的公钥PK是绝对安全的。
  • 生成对称加密的密钥k的过程中,不会在通信过程中传输这个密钥,客户端和服务端分别生成,直到生成之后再用非对称加密对其进行加密,保证安全。
  • 使用对称加密通信的数据是因为非对称加密耗费性能。

参考

http发展史
完全吃透 TLS/SSL
SSL/TLS协议运行机制的概述
The TLS 1.2 Protocol

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值