嗨~铁汁儿,确定不来搞一下HTTP嘛?

2,951 阅读50分钟

不积跬步无以至千里,只有扎实的基础你才有可能放眼未来。 本篇文章的图片借鉴于网络与图解HTTP,个人绘图水平有限(自己画的太丑了)。正题:总结原则首先知识闭环(即知识的全面性)其次二八原则即重点详写(但是好像重点贼拉多) 最后 如有误,望指教 。感恩!

本篇主要总结的知识点

要开始咯!!!

一:网络基础铺垫篇

​ HTTP协议是构建在TCP/IP协议之上的,它依靠网络层ip协议实现寻址和路由、依靠传输层的tcp协议实现可靠的字节流服务(即提供可靠的数据传输)、依靠应用层的DNS协议进行域名解析

​ 故为了更好了解HTTP,咱们才来回忆一下TCP/IP,DNS这些与HTTP息息相关的东西

​ 注意这里的TCP/IP不是单指这两种协议,这里是指一系列网络通信协议的总称。当然既然是以TCP/IP这样的命名那么这两种协议当然是核心中核心

​ 小黑:为啥?

​ 我:一个负责对方地址的寻找一个负责数据的传输这对于两者通信而言还不核心?

1.1 来看TCP/IP协议族的四层架构

同OSI七层模型一一对比,可以看到本身没什么区别的。如图:

​ 主要看一下TCP/IP四层中分别做了什么

  • 应用层:决定了向用户提供应用服务时通信的活动,如FTP、DNS、HTTP等
  • 传输层:向应用层提供处于网络连接中的两台计算机之间的数据传输功能,TCP、UDP
  • 网络层:用于处理在网络上流动的数据包,这层规定了通过一个什么样的路径把数据交给对方计算机 ,IP
  • 链路层:这一层用于处理连接网络的硬件部分,包括控制操作系统、硬件设备驱动、NIC以及光纤等物理可见部分。可以说硬件上的范畴均在链路层的作用范围之内

嗯...其实还是比较好理解的吧。就是比如使用一个web程序,我想通信肯定先得发一个http请求吧,然后传输层的保证数据的传输、网络层的保证我数据能发给对方、链路层就后勤保证呗

1.2 通信数据流

这里简单理解,首先客户端发了一个http请求,这个请求数据会向下走。走到传输层(加了啥工这里不管了)加了一个TCP首部(标记序号、端口号),再往下走,走到网络层再给加一个IP首部(MAC地址)这时就可以发出去了。

数据在服务处就可以从下往上走,过了网络层去掉IP首部,过了传输层去掉TCP首部,到达应用层完成任务

1.3 TCP如何保证可靠传输

小黑:三次握手呗,是个人就知道。

NO!!! 我偏要先说一下TCP说是提供可靠的字节流服务,那么什么是字节流服务?

嘿嘿,所谓字节流服务是指,为方便传输将大块数据分割成以报文段为单位的数据包进行管理

开始握手吧

为了保证可靠性,TCP采用三次握手策略。握手过程中使用了TCP的标志:SYN和ACK

小黑:标志?什么玩意啊,有啥意思啊?

我:来看一下TCP报文中比较重要的字段吧

  • 序号:Seq, 用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记

  • 确认号: Ack序号,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。

  • 标记位: 共6个,即URG、ACK、PSH、RST、SYN、FIN等。具体含义如下:

    • URG:紧急指针(urgent pointer)有效。
    • ACK:确认序号有效。
    • PSH:接收方应该尽快将这个报文交给应用层。
    • RST:重置连接。
    • SYN:发起一个新连接。
    • FIN:释放一个连接。

    注意:确认方Ack=发送方Seq+1,表示两端配对。不要把确认号的Ack和标记位的Ack搞混了

    接下来看一下下面的的图是不是一目了然了呢

小黑:那他为啥要握三次手嘞,谁规定的

我:嗯...,没人规定。三次是最最最合适的次数。因为要保证可靠,故发送和接收双方都需要保证自己和对方的收发功能是正常的。

首先,发送方发送完成之后被接收方第一次接收。此时接收方能够得到结论,发送方的方式功能是正常的,我自己的接收功能是正常的。

然后,第二次握手。此时发送方收到了接收方的回信。它能得到结论,它自己的发送功能是正常的(否则哪来的回信),它自己的接收功能是正常的,对方的发送功能是正常的,对方的接收是正常的

最后,第三次握手,信息给到接收方之后,接收方又得到结论,自己发送功能没问题,对方接收功能没问题

既然都没问题,那就放心的开始数据传输了

1.4 DNS域名解析

TCP/IP协议中使用IP地址标识计算机,但是一串数字计算机是比较容易处理,但是对于人类来说却不好记,于是域名就出现

同时为了更好的标记不同国家和组织,域名又被设计成一个有层次的结构。有.分割,越往右级别越高。例如.com这类的顶级域名

域名解析要做的事情就是将域名解析回IP地址

世界上一个有13组根DNS服务器,我们肯定不能每次访问一个网站前就是访问一下这13组根服务器吧。全球那么用户就是某一时刻的人数奇数也可怕的很啊,不把它们整爆了

事实上当我比如在地址栏输入掘金的域名时,会先找我们的.host文件看看里面有没有记录其对应的IP地址呢。没有找本地的DNS服务器,还是没有找距离自己比较近的DNS(比如按打官司上访开始找省一级的了)还没有再往上(国家级的)直到最后的根DNS

1.5 URI&URL

还是URI与URL傻傻分不清吗? URI:是统一资源标示符,可以唯一标识一个资源。 URL:统一资源定位符,可以提供找到该资源的路径 关系:URL是URI的一个子集

小黑:别人都这么说,你能不能别讲废话了。给个例子看看

我: 总之它两都是对一个资源的标识,我在知乎看到一遍回答给的栗子我觉得蛮形象的 拿现实举例: URI好比一个人的身份证号,即对于一个人来说我们可以用这个身份证号来标识它 URL又是如何标识这个人的呢,它的形式为 协议://中国/北京/海淀区/魏公庄/xx大学/xx宿舍/宫小白 这样区分应该蛮清楚的吧URL不仅可以表示标识这个人还定位到这个人的位置

故一个我们在地址栏里输入的都是URL,因为我们最终的目的是获取这个被标识的资源,肯定需要够定位到它的位置吧

二: http初始篇

2.1 什么是HTTP

HTTP => 超文本传输协议。即拆分理解超文本传输协议

HTTP首先是一个协议,协议即规则。它使用计算机能够理解的语言确立了一种计算机之间的交流通信的规范,以及相关的各种控制和错误处理方式

再者接着了理解传输,传输即a->b之间的有关某一种东西的传送。当然在HTTP中a与b之间的传输是双向的

最后再来看超文本,所谓超文本就是超越了普通文本之上的文本,它是文字、图片、yinshipin等的混合体且可以含有超链接,可以从一个超文本转到另一个超文本。当然这里我们熟悉的超文本便是HTTML了

总结:HTTP是一个在计算机世界里专门在两点之间传输 文字图片等超文本数据的一种约定规范

2.2 HTTP中的报文头

对于HTTP报文的基本结构我们应该是很熟悉了。即它有三部分组成:起始行、头部、实体。(注意头部和实体之间必须有空行)

起始行的信息相对比较简单

请求行:

由三部分组成

  • 请求方法,如get或post等
  • 请求目标,一般是一个URI,如/index.php
  • 协议版本号

例子:

GET /index.php HTTP/1.1

响应行:在这里它其实叫做状态行

同样由三部分组成

  • 版本号,HTTP的版本
  • 状态码,如200表示成功
  • 原因,它是状态码的补充

例子:

HTTP/1.1 200 ok

接下来主要看报文头吧

在HTTP中所规范的头部字段一共有大几十中,故全部总结是不可能的。。。

报文头的数据格式为{key:value}形式

常用头可以分为以下四类:

  • 通用字段——>即可同时出现在请求头和响应头里面
  • 请求字段——>仅能出现在请求头里
  • 响应字段——>仅能出现在响应头里
  • 实体字段——>它实际上是属于通用字段,专门用于描述body的额外信息

常见

Date

通用字段,描述了报文发送的时间

Connection

通用字段,可指定本次连接的类型。若值为keep-alive。表示一个请求完成之后本次的tcp连接不会关闭,后面可以继续使用;若值为close,则表示一个请求完成之后tcp连接关闭,后面每次请求需要重新进行连接

如:Connection: keep-alive,Connection:close

HOST

请求字段,且一个请求报文中头部必须拥有此字段,否则就是一个错误的报文。它的作用是指定服务器处理此次请求的主机及其端口号

Referer

请求字段,它的作用是告诉服务器此次请求是从哪个页面链接过来的

如从百度页面搜索某一个东东,从这个字段可以看出发出请求的那个页面地址。即https://www.baidu.com/

Accept

请求字段,它的作用是描述了浏览器端可以接受的媒体类型。如 Accept: text/plain

Accept-Charse

请求字段,描述了浏览器端所能接受的字符集。如Accept-Charset: utf-8

Accept-Encodong

请求字段,描述了浏览器端所能接受的编码方式,通常指定压缩方法,是否支持压缩,支持什么压缩方法。如Accept-Encoding: gzip, deflate

Accept-Language

请求字段,描述了浏览端能够接受的语言。如Accept-Language: en-US

User-Agent

请求字段,此次字段描述了客户端的一些信息,如:客户端使用的操作系统、浏览器的名称版本等

Access-Control-Allow-Origin

响应字段,指定哪些网站可以资源共享。这个字段是我们很常见的,一般我们会用它处理跨域

Server

响应字段,服务器的名字。如

Content-Type

实体字段,描述了报文体内对象的媒体类型。如表单的Content-Type: application/x-www-form-urlencoded或者html的Content-Type: text/html

Content-Length

实体字段,指定请求体的长度

👉详细参见此博客

2.3 HTTP中的请求方法

GET

获取资源,可以理解为读取或者下载数据。同时get方法也可以用来提交数据

POST

和get方法类似,一般用来传输实体主体。get的主要目的是获取,而post显然不是

PUT

用来传输文件,要求在请求报文的主体中包含文件的内容,然后保存到URI指定的位置。即此方法可直接将报文主体的数据替换到服务器的资源

值得注意的是在HTTP1.1中put方法不存在验证机制,故任何人均可以上传文件安全问题得不到保证

它和post方法类似,但是put方法是幂等的,而post不是

小知识 幂等于非幂等

HTTP的幂等性指的是这个HTTP方法不管你调用多少次,其结果都是相同的

如一个get请求的接口,无论你调用多少次它的返回结果都是相同的。(服务器资源不能手动改变)

而一个post的请求,我们常用它做的事情就是数据的增添操作。即这个请求接口的每次调用服务器都会有新的资源产生,并且返回不同的结果

再来说这里的put请求,还是对接我们的实际操作。它一般用于更新操作。即调用这个接口一次和多次,其实际效果均和第一次一样

HEAD

同get方法类似,只不过其响应报文中没有主体部分。用于获取报文首部

DELETE

与put方法相返,put是将URI指定的资源替换掉,而delete则是将它删除

OPTIONS

用来查询URI指定的这块资源支持什么方法。如支持get我们可以用get请求支持post就可以用post

TRACE

用于追踪路径。可能会引发XST攻击很少用

CONNECT

此方法可在客户端与一个代理服务器通信时建立隧道,实现隧道协议进行TCP通信

2.4 HTTP中的状态码

客户端来信儿了,咱服务器端答不答应它的请求也得表个态吧

大体分类

  • 1xx:表示接收的请求正在处理,只是一个临时的响应
  • 2xx:表示成功,这一类的状态码表示请求请求报文已经收到并被正确处理
  • 3xx:重定向,表示资源位置发生了变动,需要客户端重新发送请求
  • 4xx:表示客户端错误,即请求报文有错服务器无法理解或处理
  • 5xx:表示服务器错误,即服务器在处理请求时其内部发生了错误

比较常见的一些状态码

2xx
  • 200:服务器已经成功的处理了请求
  • 202:服务器接受了请求,但尚未处理
  • 206:服务器成功处理了部分get请求
3xx
  • 301:永久移动,表示请求的资源文件已经被永久的移动到一个新的位置,会同时返回一个新的URL,今后的请求就得使用这个新的了
  • 302:临时移动,与301类似,但资源只是被临时移动以后还是用老的URL
4xx
  • 400:客户端请求发生语法错误,服务器不能理解
  • 401:要求用户进行认证
  • 403:服务器能理解客户端的请求,但是拒绝了它的此次请求
  • 404:我们最熟悉的了,没有找到资源
5xx
  • 500:服务器内部发送错误,无法完成此次请求处理
  • 502:充当网关或者代理的服务器时,从远端服务器接收到了一个无效的请求

2.5 Cookie&Session再加上现在的token

由于HTTP的无状态所衍生出来的产物们 Cookie和Seesion估计是每一个搞web的人都去了解过的东西,就再简单复习一下咯

  • Cookie:就是一段文本信息,在浏览器访问服务器时由服务器颁给浏览器。浏览器保存等到下次再访问此服务器时就会带着这个Cookie一同访问,服务器通过cookie中的信息辨认用户
  • Session:cookie是保存在浏览器中的不太安全,于是session来了。session也是记录用户的一种机制,与cookie不同的是session是保存在服务器中的,即浏览器访问服务器如果是第一次访问,服务器就把这个浏览器的一些信息记录下来。等到它下次访问时直接去session里和核对即可

总结:cookie相似于通信证,session相似于一张自己用户的明细表

使用session也有很大的确定,服务器要保存所有人的session Id。像某宝某东这个用户量上亿用户的网站维持这些信息得是多么大的开销

token来了 token:它也是由服务生成的一串字符串,用于当做客户端浏览器的一个令牌。客户端表单验证通过第一次访问时,服务器生成token交予此客户端。后面它再来访问的时候拿着token即可

是不是看起来token和cookie蛮像的,东西都是由服务器授予的且再次访问的时候需要带着。它们的区别是什么呢?

token只是一个有服务器经过一些算法以及加密生成的一个字符串,而cookie却是保存了许多数据。服务器端进行验证的时候对于token来说仅是相当于一个“对暗号”,对于cookie却要仔细查看里面的内容。同时开发时我想码友有了解了像现在前后端分离,肯定存在的就是跨域的问题。还使用cookie是难以处理的,使用token仍然很方便

当然这仅是我理解的一点不同,笔记以后进行整理。感觉跑题了,这里不是HTTP专场吗?

三: http深入篇

3.1 身份认证

怎么证明是“你”在访问服务器呢?

HTTP/1/1中使用的认证方式

  • BASIC认证(基本认证)
  • DIGEST认证(摘要认证)
  • SSL客户端认证
  • FormBase认证(基于表单认证)

BASIC认证

它是从HTTP/1.0就定义的认证方式,是客户端与服务器之间进行的认证方式

BASIC认证基本流程

如图所示:要请求的资源需要BASIC认证,服务器响应字段返回状态码401、原因 Authorization Required ,跟随返回的还有一个WWW-Authenticate的首部。这个字段包含要认证的方式和 Request-URI 安全域字符串

客户端接收之后,发现需要进行BASIC认。会将用户的ID和密码经过Base64编码之后放进 Authorization字段中发送至服务器

服务器再对传过来的认证信息进行验证,如正确返回一条包含 Request-URI 资源的响应

**值得注意的是:**虽然用户ID和密码使用了base64进行了编码但是可没有进行加密处理,这串信息使用base64解码仍是原信息。故BASIC认证是一种不安全的认证方式且想要再进行一次BASIC认证,一般的浏览器也无法实现认证注销。所以BASIC认证不常用

DIGEST认证

因为BASIC认证中的信息传输是以明文的方式,故在HTTP/1.1中便有了DIGEST认证

DIGEST认证使用质询/响应的方式。质询/响应是指:开始一方会先发送认证要求给另一方,接着使用从另一方那接收到的质询码计算生成响应码。最后将响应码返回给对方进行认证的方式

DIGEST认证基本流程

SSL客户端认证

为了解决用人获取了用户的用户ID和密码就能随意冒充的问题 SSL 客户端认证是借由 HTTPS(下面有HTTP与SSL的总结如果不清楚可先跳过) 的客户端证书完成认证的方式。凭 借客户端证书认证,服务器可确认访问是否 来自已登录的客户端

基本步骤:

  • 接收到需要认证资源的请求,服务器会发送 Certificate Request 报文,要求客户端提供客户端证书。
  • 客户端会把客户端证书信息以 Client Certificate 报文方式发送给服务器
  • 服务器进行验证,验证通过后方可获取客户端的公钥,然后开始 HTTPS 加密通信。

FormBase认证

表单认证我想不用再硕了吧。

3.2 长连接&短连接

HTTP协议是基于请求/响应模式的,故只要服务器端给了响应那么本次HTTP请求就结束了

值得注意的是:HTTP的长连接和短连接本质上是指的TCP的长连接和短连接

为什么要长连接:

现在一个页面会向服务器发出炒鸡多的HTTP请求,每一个HTTP请求的创建都会经历一次TCP的三次握手和四次挥手这是十分浪费性能的。故我们希望通过一个HTTP创建的数据传输通道不要在进行了一次数据传输之后就关闭,希望它可以一直开启以便于我们后面的数据传输

3.3 中介

3.3.1 代理

即如图所示,代理服务器就是如同中间件一样被放到客户端与服务端之间。原本是客户端直接与服务端进行通信,由于代理的存在客户端的请求会被代理接收,然后再由代理将请求发至源服务器。响应同理

回想node中的中间件或者axios的拦截器,这种"第三人"的出现肯定是为了做某些事情,它能做什么呢?

  • 过滤器,过滤掉某些IP地址的访问
  • 文档访问器,对一些文档资源做限制
  • 安全防火墙
  • web缓存
  • 反向代理
  • 负载均衡
  • ...

代理最主要的特点是它的“欺上瞒下”,即客户端以为它是服务器、服务器以为它是客户端。故

3.3.2 网关

网关同样是一种特殊的服务器,同样是作为中间件使用。

网关与代理的工作机制十分类似,但是网关又被称为协议转换器。像上面的代理服务器它两边的协议仍是HTTP,网关可不是这样。网关要求其两边使用不同的协议,如左边使用HTTP右边使用FTP。

小黑:那有什么常见的网关类型不?

答:

  • HTTP/*:服务器端Web网关
  • HTTP/HTTPS:服务器端安全网关
  • HTTPS/HTTP:客户端安全加速器网关

小黑:什么玩意客户端服务器端网关?你在说锤子呢?

答:嗯...,因为web网关两端所使用的协议不同,故一般以这<客户端协议>/<服务器端协议>这形式表示。像HTTP/这种网关使用HTTP协议与客户端通信使用其他协议和服务器端通信被称为服务器端网关;像/HTTP这种通过其他协议与客户端通信,使用HTTP协议与服务器端通信的被称为客户端网关

3.4 缓存

主要来看一下客户端缓存

先来看一下拥有缓存的请求流程

  1. 浏览器请求一个资源,先去查看缓存里面看有没有,没有则向服务器请求资源
  2. 服务器响应,返回资源同时也标记了这个资源的有效期。(即缓存里的资源是有它的存在时间的)
  3. 浏览器将资源缓存起来以备下次使用

再来看一下报文中有关缓存的字段

通用字段 Cache-Control

它有以下常用值:

  • no_store:所有内容均不缓存
  • no_cache: 缓存,但浏览器每次使用缓存资源前,都会去服务器判断这个资源是不是最新的
  • max-age=x:请求缓存后x秒不再发起请求(注意:生存时间的计算起点是从响应报文的创建时刻,即包含了在链路中传输所需要的时间)
  • s-maxage=x:代理服务器请求源站缓存后x秒后不再发起请求,只对CDN缓存有效
  • public:资源在客户端与代理服务器(CDN)都可缓存
  • private:只有客户端才可以缓存

瞅一眼掘金中的资源吧

值得注意

虽然Cache-Control是一个通用字段,客户端也可以使用。但是浏览器使用Cache-Control只能是数据刷新,如页面中我们强制刷新时实际是在请求头里加上Cache-Control:no-cache即使是普通刷新也会在请求头里加上Cache-Control: maxage=0。即它们均表示请求最新的资源

那么它是怎么实现使用缓存里面的数据呢?

HTTP协议中定义了一系列if打头的字段,专门用来验证资源是否过期。其中最常用的两个请求字段就是if-Modified-Sinceif-None-Match。它们分别于下面的响应字段是一对

响应字段 Expires说起

表示资源的过期时间,是HTTP1.0的属性,在与max-age共存的时候优先级要低于它。

即客户端向服务器请求资源时,服务器给与响应资源时还会带上一个Expires字段。即和客户端约定了一个文件资源的过期时间,表示在此时间内你去你缓存里找不要再来向我要了

但是这样有一个缺点:即过了这个约定时间其实服务器中的资源仍然没有变动还是客户端浏览器缓存里的那个数据。这样该怎么办呢?

下面的两对字段就派上了用场

响应字段 Last-Modified与请求字段if-Modified-Since

在文件资源约定过期时间的基础上,加上了一个资源最新修改时间的对比

即客户端向服务器请求资源时服务器这时返回的是资源+约定时间+资源最新修改时间(放到Last-Modified字段)

那么后面就存在了多种情况

  • 约定时间没有到期直接使用缓存
  • 约定时间到期了,客户端发送请求带上if-Modified-Since字段(里面存的是开始服务器响应给的资源最新修改时间)。用if-Modified-Since字段中的数据与此时服务器中的资源最新修改时间做对比,判断资源文件是否发生了变动。无变动仍然使用缓存,变动了则拿最新资源

小黑:看你下面还有一对字段,这里是不是也向上面一样存在不足?

我:你还真聪明嘞,没错Last-Modified只能精确到秒。很可能出现这种情况,开始客户端请求数据时,服务器返回了资源+约定时间+资源最新修改时间,但是服务器中就在这个响应报文刚发出去,其资源又发生了变动(1s内发生的),那么就会导致本次修改后的资源客户端是永远拿不到的

响应字段 ETag与请求字段if-None-Match

为了改进上面那对字段的缺点,增加了一个文件标识ETag

ETag:资源的唯一标识,主要用于解决根据修改时间无法区分文件是否变化的问题。也即资源一发生变动,这个标识符就会跟着变动

这时的客户端向服务器发起请求,服务器响应回:资源+约定时间(max-age=x或Expires)+最新修改时间+资源标识符

后面还是存在多种情况

  • 约定时间没有到,客户端继续使用缓存
  • 约定时间到了,客户端发起请求,带上if-None-Match(放的是开始响应回的ETag)和if-Modified-Since(开始响应回的最新修改时间)。这时服务会先行比较if-None-Match中的标识符与此时服务器里的资源标识符是否一致(标识符判断的优先级要高于资源最新修改时间的判断,其实这里的if-Modified-Since没什么卵用)。一致服务器返302即客户端继续使用缓存,不一致服务器返回最新的资源再带上新的标识符等字段

但是此时还存在一个问题,即在约定时间内资源发生了改变我们怎么去获取呢?

再来了解一下服务器端缓存

服务器端的缓存功能主要是由代理服务器来实现的,其实也是十分类似于客户端的缓存。代理服务器处于中间层即它可即充当客户端也可充当服务器端,在它充当客户端向源服务器请求数据时,请求回来的数据除了转发给它的下游客户端也可自己缓存一份。

故这时客户端再去请求数据,便可直接获取代理中缓存的了

3.5 内容协商机制

内容协商机制指的是客户端和服务器端就响应的资源内容进行交涉,然后服务器端响应给客户端最为合适的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的基准

说白了就是某一资源,在服务器上有多个版本。然后由客户端根据自己的喜好选择一个最为合适的

内容协商技术的三种实现方案

  • 客户端驱动:客户端发起请求,服务器给与一个可选列表,客户端做出选择后发送第二次请求
  • 服务器驱动:服务器检查请求报文的首部以判断发送哪个版本的资源
  • 透明协商:某个中间设备(通常是缓存代理也即具有缓存功能的代理服务器)代表客户端进行协商

主要来看一下服务器驱动中的请求报文吧

其实上面也已经总结过了

如 :

  • Accept:告知服务器发送哪种媒体类型
  • Accept-Language:告知服务器发送哪种语言
  • Accept-Charset:告知服务器发送何种字符集
  • Accept-Encoding:告知服务器采用哪种编码
  • ...

再来了解一下q值的使用

q值其实就是一种优先级的表示,它的取值为[0.0,1.0],其中1.0表示优先级最高

看这个栗子

Accept-Language: en;q=1.0, fr;q=0.0, nl;q=0.8, tr;q=0.0

表示客户端接受的语言最好是en,没有en的话nl也可以。不接受fr和tr

3.6 断点续传与多线程下载

断点续传:当我们下载某一个比较大的资源时因为网络原因下载中断,当网络重新连接时我们通常可不是将这个资源从头再次下载一遍,而是从上次中断的节点继续下载。那这是怎么实现的呢?

这里则是主要关注两个字段,请求头里的Range响应头里的Content-Range

  • Range:这个请求字段表示我客户端要某一资源的哪一个范围区间。它的格式是这样的Range:(从哪开始,从哪结束]。栗子Range:bytes=0-199表示我要这个资源从0字节开始到199字节内的这一段资源。其范围还有以下表示形式如bytes=-1023表示要最后1k的数据;bytes=500-`表示要的是从500到最后的资源片段
  • Content—Range:在接收到请求之后服务器除了返回其所要片段的资源,还会在Content-Range中写上相应的数据。如客户端要的是0-199范围内的资源片段,则响应报文中Content-Range会写上此次响应回的资源片段范围也即是0-199,同时还会写入这个资源的总大小

一个完整的断点续传过程

  1. 客户端下载一个1k大小的资源,已经下载了512k了,发生了网络中断
  2. 网络重新连接,发送续传请求。其中请求头中带上Range:bytes=512000-
  3. 服务器处理请求,从资源的512k处响应资源。并在响应头中添上Content-Pange:bytes 512000-/1024000

值得注意由断点续传返回的状态码不是200而是206

四: HTTPS了解篇

4.1 先来看HTTP中存在的不足

  • 通信使用明文,内容存在窃听风险
  • 无法证明报文的完整性,即报文可能在中途就已经被修改
  • 不验证通信方身份,因此有可能遭遇伪装

4.2 HTTPS核心内容

HTTPS的出现也就是为了弥补上面所总结的不足

那么HTTPS=HTTP+加密+认证+完整性保护=HTTP+TLS

TLS:一种安全通信协议。注意TLS原来叫做SSL,故可不要把它两当成两种协议。

TLS处于应用层的下层,数据传输层的上层。

也即HTTP不会再直接与数据传输层的TCP进行通信,而是先与TLS。

HTTP中存在的那些不足就交给TLS来做了

4.2.1 TLS中的对称加密与非对称加密

对称加密

所谓对称加密就是加密解密都共用一个秘钥,也即通信双方在进行数据交流时因为此时数据已经加密为使对方能理解自己发出的数据(保证对方能对数据进行解密)还要把这个秘钥一起发送过去

那么这里就有一个很大的漏洞,即只要有人在中间将通信双方的传输数据窃取下来。因为数据中包含秘钥,那么这些信息和明文也没有多大的区别吧

非对称加密

为了解决上面对称加密存在的问题,非对称加密使用了两种秘钥。一个为私有秘钥(只能放在本地计算机中),一个为公开秘钥(可随意给出)

它的工作方式是:发送秘文的一方使用对方传过来的公开秘钥进行加密处理,对方收到之后再用自己的私有秘钥进行解密。也即加密和解密使用两种不相同的秘钥,故中间的数据及时被窃取由于攻击者没有私有秘钥仍然看不懂数据中写的是啥玩意

混合加密

任何东西都不是完美的,非对称加密这么优秀是否在HTTPS中的加密方式就是一直使用的它吗

要想知道是不是,首先我们先的了解一下对称与非对称加密算法

  • 对称加密算法目前常用的为AESChaCha20
  • 非对称加密算法中最著名的一个为RSA,还有后面的ECC

我们要知道大部分的密码学的东西都是使用的一些很复杂的数学计算,那么在这里HTTPS传输报文时总要经过加密解密这对CPU的计算是有一定的负担的,计算也要付出时间的成本,在web中时间可就是一切

但是为了保证报文不在网络里进行裸奔,我们就有必要好好的比较一下这两种加密了

首先非对称秘钥的算法均是基于复杂的数学难题,其运算及其消耗时间,即使ECC也要比AES差上不止一个等级。如果报文传输时全部使用非对称加密,那么我们打开一个页面的时候就先跟屏幕眼瞪眼一会吧

再回想一下对称加密,对称加密的缺点在于它的秘钥不能够保证安全的传输个对方。

那么解决方案不是就来了吗,我们可以使用非对称加密的方式将对称加密中的秘钥传过去,后面不就可以放心的使用对称加密了吗

4.2.2 摘要算法

4.2.1中的加密操作仅是保证了报文不是在网络中裸奔,但是还没有保证数据的完整性。这时攻击者窃取了数据,虽然它看不懂但是它可以对其进行修改啊,虽然可以它也不知道这个玩意最后被解密出来个啥玩意,但是这已经破坏了此次的通信,我们也不清楚对方会响应回来个什么玩意

实现完整性的手段便是摘要算法(散列函数、哈希表)。使用它可以把数据压缩成一个固定长度且独一无二的字符串,也可以理解为这段数据的一个指纹或者说这个字符串就是这段数据的唯一标识。(当然会存在像哈希函数那样经过两个不同的字符串经过转换得到的是同一个东西不过这不是我们现在需要考虑的玩意)

有个这个数据的“指纹”,那么我们就可以在传输数据的时候带上它的数据指纹。由接收方去验证,如果数据被篡改了,那么就和它的指纹无法匹配。由此就保证了数据的完整性

当然这里的所有数据肯定也得是基于秘文传输之上的,否则攻击者在修改了数据之后连指纹也给你重新搞了一个怎么办。。。

4.2.3 数字签名与证书

前两个问题已经基本解决了,下面开始看第三点了。即你怎么证明你访问的是一个正规的站点,可能你本打算访问某宝了中间被诱导进了一个和某宝长的一毛一样的站点,这时你在这个站点输入的所有个人信息可都白瞎了啊。

开始证明数据是目标服务器端发回来的。

映射到现实世界,一个文章证明是你的只需要你在末尾签一下名或者盖一下章即可。这里也是一样

这时候还是用到了非对称中的公钥和私钥,值得注意的是私钥可是自己私有的,故可使用它来进行“签名”。然后再由其相对应的公钥进行“验签”

这时解决了验证的问题了吗?

小黑:你这么问肯定没有,快点往下说唠叨什么

我:...,嗯此时仍然是没有解决不了问题。公钥是可以随意转发的,但是你这个公钥必须要指明的的来源身份啊。比如我接收方收到了你这个公钥我就可以准确的知道你就是某宝

这时就要引入第三方来帮忙咯,CA(证书认证机构):类似于网络中的公安局、公正中心具有很高的可信度

这时的流程就变成了这样:

  • 服务器运营人员向数字证书认证结构提出公开秘钥的申请,数字证书认证机构判明身份之后,对这个已申请的公开秘钥做数字签名(相当于盖上认证结构的章)
  • 分配这个已签名的公开秘钥
  • 服务器就把传统的公钥替换为这个公钥证书了
  • 客户端验证真实性

注意客户端之所以能够对这个认证机构做的公钥签名进行验签是因为客户端的操作系统和浏览器都事先内置了各大CA的根证书

这时已经基本的实现了认证机制,但是万事就没有完美的。所谓道高一尺,魔高一丈

如果CA因为事务或者被欺骗再或者CA和黑客攻陷怎么办?

故又出现了下面的补救措施

  • CRL 证书吊销列表
  • OCSP:在线证书状态协议,以便及时废止有问题的证书

4.2.3 HTTPS/TLS握手

请注意这里才是HTTPS的核心

下面来比较详细的走一下TLS握手的步骤,注意它是发生在TCP连接的后面,也就先进行的是TCP的三次握手。在TCP建立了连接之后便开始TLS的握手流程了。

还是看图解HTTP中的这个图吧

  1. 首先浏览器发送一个"ClientHello"的消息。这段消息里面同时含有客户端的版本号、支持的密码套件和一个随机数random1(用于后面生成对称加密的秘钥)
  2. 服务端向客户发送"sever Hello",同时这段消息也包含了一些信息。比如从客户端传递的密码套件选择一份(即决定了后面加密和生成摘要时使用哪种算法),还有一个随机数random2(也是用于后面生成对称加密的秘钥)
  3. 服务端向客户端发送"Certificate",这一步是服务端将自己的证书发给客户端,以便客户端验证自己身份。同时我们前面知道了服务器的公钥也是存在于证书里面,故浏览器此时也拿到了服务器的公钥
  4. 服务器端发送"Server Hello Done",这一步是通知浏览器Server Hello 过程结束
  5. 浏览器端发送"ClientKeyExchange",这一步客户端又生成了一个随机数random3,然后根据服务器传过来的公钥生成PreMaster key然后将它再传给服务器。服务器拿自己的私钥解密得到浏览器端的random3 。这个时候无论是浏览器端还是服务器端都有了三个随机数:random1+random2+random3.然后两边就可是使用相同的算法生成对称加密的密钥
  6. 浏览器端发送"changeCipherSpec",这是一条事件消息,这一步浏览器端通知服务器端后面发送的消息就要是之前生成的那个对称加密密钥进行加密了
  7. 服务器本次工作完成,相当于试探性验证消息。 客户端将前面的握手消息生成摘要再用协商好的秘钥加密,这是客户端发出的第一条加密消息。服务端接收后会用秘钥解密,能解出来说明前面协商出来的秘钥是一致的
  8. 服务器同样发送"changeCipherSpec",同样是一条事件消息,表示告知浏览器后面再发送消息就要加密了
  9. 服务器本次工作完成, 相当于试探性验证消息。服务端也会将握手过程的消息生成摘要再用秘钥加密,这是服务端发出的第一条加密消息。客户端接收后会用秘钥解密,能解出来说明协商的秘钥是一致的。
  10. 均正常,开始正常通信了

4.3 HTTPS的使用成本

HTTPS这么优秀为啥到现在还不是所有的站点都迁移到HTTPS上呢。

主要有以下原因

  • (以前)证书的申请和维护成本太高,小型网站难以承担。
  • 速度问题,无论是多出来的TLS握手还是后面的不断加密解密以及包含一些证书的验证等操作使它要更加的消耗cpu,增加了时延。(现在计算机的性能越来越高,再加上有许多对它的优化方案故此时这里也不是问题了)
  • HTTPS涉及的知识点多而杂,有比较高的技术门槛不便于很快的上手

故现在的主要问题就是步骤3,关于HTTPS的技术方面的问题了。

下面的东西就不是我们现在要了解的了,毕竟咱只是一个弱小而无助的小前端。不能现在就抢了运营大大的活你说是不。。。

五 : http 2.0、http3.0 展望篇

5.1 先来了解一下webSocket吧

在来看一下HTTP中的不足,HTTP的通信是基于请求/响应。即你客户端不请求我服务器端永远不会将最新的数据给你

但是我们有许多业务功能就是希望服务器能够将它此时最新的资源数据自动推送给客户端

基于这种情况,出现一种解决方案:

轮询

轮询又包含两种

  • AJAX轮询:即没隔一段时间客户端向服务器端发送一个请求,看看有无最新资源
  • Long Poll:即每次客户端发起资源的请求,服务器端会先判断你这次请求的资源和我上次返回的是不是同一个(也即判断资源是不是最新的)。是同一个即表示此时资源和你上次请求的是一样的那么就进行等待,先不进行响应等到资源变动了再将这个最新的资源发送过去。如果一开始判断就能确定此时的资源是最新的就直接发给客户端

Long Poll的缺陷比较显而易见,即若资源没有更新那么这次HTTP请求就不会中断。要是客户端请求多个资源呢?这些个资源都没有更新就存在这么多的HTTP请求在等待服务端的响应,这消耗服务器端性能不说,对于一些限制HTTP请求条数的服务器来说相当于就被阻塞了,此时任何别的请求服务器均不在受理那该怎么办?

小黑:快点介绍WebSocket就你墨迹

我:

好吧下面来看WebSocket

来看一下来自菜鸟教程的介绍笔者感觉描述的很nice

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

即由于它是建立在HTTP基础之上的协议,故发起连接的仍是客户端,但是一旦确立了WebSocket通信连接,以后无论是客户端还是服务器端都可以主动的发送信息

接下来看一下webSocket中的握手

  • 要进行WebSocket通信,首先客户端发送请求的报文中头部需要加上Upgrade字段。告知服务器通信协议发生了变化,同时此报文首部还需要加上几个有关字段:Sec-WebSocket-Key一个一base64编码的16字节随机数,用来作为一个简单的认证密钥;Sec-WebSocket-Protocol记录着使用的一些子协议;Sec-WebSocket-Version版本号
  • 服务器响应,状态码101 。同样还有几个字段。Sec-WebSocket-Accept,里面的值由请求中Sec-WebSocket-Key内的值生成的;同样含有Sec-WebSocket-Protocol记录着使用的一些子协议
  • 成功握手后,通信便使用WebSocket独立的数据帧

js中有关WebSocket的API

构造函数 WebSocket(url[,protocols]),一参为地址url,二参可选,指定可接受的子协议

栗子:

const socket=new WebSocket('ws://localhost:8080')
socket.onopen=function(){
      socket.send('Hello Server!');
}
socket.onmessage=function(e){
      console.log('this is from serve',e.data)
}

详细见:

👉MDN WebSocket

5.2 开整 HTTP 2.0

HTTPS解决了HTTP安全方面的问题,但其性能却是个很大的问题,且其优化方案也只是优化了握手与加密的过程

故在HTTPS逐渐成熟之后就得向着解决它性能问题的方向跨步了

再来看一下此时HTTP存在的缺陷
  • 头部冗余
  • 单路连接、请求低效
  • 只允许客户端主动发起请求

此时来看一下HTTP 1.x 、HTTPS、 HTTP 2.0的协议栈,可以发现HTTP 2.0 也是建立在TLS协议之上的,且对其TLS版本就明确要求。TLS我们已经知道不了它是为了保证安全的,那么TLS与HTTP中新添加的HPack、Stream又是啥玩意呢?(简单解释->Hpack用于压缩首部,同时HTTP2.0使用虚拟的流传输消息用于解决队头阻塞和实现多路复用,详细见下)

先来看HTTP 2.0进行的改进
  • 头部压缩

  • 二进制分帧

  • 多路复用

头部压缩

在HTTP 1.x 版本中虽然报文主体可以经过gzip压缩,但是首部却是没进行处理。首部所占的流量也是不容忽视呢,故HTTP 2.0 的改进性能之一就是拿首部问题开刀

首先为了保证向下兼容,HTTP 2.0 与HTTP 1.x的语义是没有变化的,故其报文的结构还是“Header+Body”。但是在请求发送的时候Header必须要用HPACK来压缩一下

在介绍HPACK的压缩流程之前,我想有必要了解的东西

HTTP 2.0 为了方便对首部的管理和压缩,废弃了起始行。把它们也放到了首部字段的统筹中,同时为了与真正的首部字段做区分又给它们起了一个新的名字“伪首部字段”

同时在写法上为了区分伪首部字段,与真首部字段,伪首部字段的key值前会加上:",如:method

到现在整个报文的首部就都是key-value的形式了,既然都是key-value的形式那字典这种数据结构显然就派上了用场

在Hpack中有两个表用于充当字典的角色,这个字典通信双方均拥有

  • 静态表,静态表比较简单它记录了一些已知首部字段,但是其内部又可以被划分为两种

    • key和value均是确定的,如:method:get。这种情况只需查表看其对应的值即可,即:method:get以后仅需用一个2表示
    • value 不能确定,比如cookie。这种情况就要用到动态表了
    • 静态表截图
  • 动态表,它会被添加在静态表的后面,与静态表结构相同,且在编码解码时会随时更新。目的还是让以后某一个字段可以仅用一个数字进行表示

接下来Hpack算法的流程就简单的多了

  • 首先通信双方共同维护了一个静态表和一个动态表, 支持基于静态哈夫曼码表的哈夫曼编码 (用来减小体积)
  • 假设此时客户端刚有cookie,那么它请求服务器时首先用2/3表示自己的请求方法(其他省略...)再告知服务器,将cookie:xxx添加到动态表中,那么cookie以后也可以只用一个字符来表示的,类似的服务器也可以更新对方客户端的动态表

你可能注意过,使用了头部压缩之后首次请求其首部大小可能会减少为原来的一般,下一次请求可能其首部大小又减少为刚才的一半。为啥两次压缩的大小量还不相同呢?

还是静态表与动态表的问,静态表首次就可以进行使用。像上面的cookie栗子,第一次请求肯定需要把这个字段完整的传到服务器,但是当动态表更新之后下次再来这个cookie字段不管它上次占了多次字节我一个字符就搞定,一个字节有8位,即一个字节表示的范围就是1-2滴8次方(符号没打出来不打了),完全够用

👉想深入了解,可参考

二进制分帧

HTTP 2.0 把TCP协议的部分特性搞到了应用层,把原来的Header+Body的消息大散为数个小片的二进制帧,使用HEADERS帧存放首部数据、DATA帧存放实体数据

也可这样理解:HTTP 2.0 中数据通信的最小单位不再是以前的那种报文,而是换成了帧。一个消息由一个或多个帧组成

多路复用

先来了解流是什么?

流是存在于连接中的一个虚拟通道。它可以承载双向消息且每一个流都有一个唯一的整数ID

它有以下特点

  • 可并发,一个HTTP 2.0 的连接可以同时发出多个流传输数据即一次连接创建可整多个这个虚拟通道。一个HTTP 2.0 中的流对应HTTP 1.x的一次请求-响应,也即一个连接并发多条请求(多路复用)
  • 流是双向的,即一个帧可以由客户端发送可由服务器端发送
  • 不同流之间彼此独立,但一个流内的帧的传输顺序有着严格顺序
  • 不止客户端,服务器也可创建流。
  • 流可设置优先级,让服务器优先处理,如先传输HTML数据后传输图片
  • 0号流特殊,不可关闭不能发送数据帧,只可发送控制帧用于流量控制

那么到这多路复用我们就明白了吧

所谓多路复用指的是:所有的请求均是通过一个TCP连接并发完成

怎么解决的队头阻塞

通过上面对流的了解,我们知道一次连接可以有多个流,且一个流里面的数据帧是有着严格顺序的。注意这是我们站在流的角度去思考的,此时的数据传输像这样

但是我们要知道,这些个东西本身可就是在一个连接上的。站在连接的角度它们就没有这里井然有序了,或者说看起来乱七八糟

但是就因为此时多个请求-响应之间没有了顺序关系,故不需要排队等待,也即不会出现队头阻塞的问题

值得注意这里解决的只用应用层面的队头阻塞问题,数据传输层仍然存在

HTTP 2.0 的特性还有很多很多,这里仅是总结了一下最常见的。

详细见👉透视HTTP协议

5.3 了解WebDAV

这玩意是什么?

先看一下百度百科的解释:

WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 👉HTTP 1.1协议的通信协议。它扩展了HTTP 1.1,在👉GET👉POST👉HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的👉版本控制

拿个比较像的应用来举栗子:像百度网盘那种,我们可以直接对服务器的东西进行增删改查

WebDAV在这里我没有把它当做一个重点知识,故接下来借助图解对它的讲解简单进行一下扫盲吧

额发现也没有什么可扫的,那就不总结它了吧

好吧。。。

WebDAV除了增删改的基本功能,还具有文件创建者管理、文件编辑过程中加锁即避免多人同时编辑同一文件的问题

然后WebDAV又新增了一些概念,为方便使用又在HTTP的基础上新增了一些方法及其对应的状态码

5.4 展望QUIC&HTTP 3.0

HTTP 2.0 刚问世不就,HTTP 3.0就已经出来了。HTTP 2.0这么优秀HTTP 3.0又能改进些什么东西呢

首先上面说HTTP 2.0 解决队头阻塞问题时,我有说过HTTP 2.0仅是在应用层解决了这个问题。但是其在TCP连接时因为TCP的可靠连接特性在这里还是存在阻塞。

并且TCP本身就存在问题,就连接来说它因为要保证可靠的传输故其需要握手三次。即至少需要两个RTT

同在数据传输层的UDP可没有这么麻烦,故HTTP 3.0将传输层的协议由TCP换成了UDP

并且因为UDP的不可靠传输使在数据传输层解决了队头阻塞问题,TCP在数据传输过程如果丢失了一个数据包那么它的接收方就会一直等待发送方重传。UDP就简单的多了,有数据了直接扔过去,管你有没有丢包呢

但是又因为UDP不能保证可靠的数据传输,则有必要加上一个东西帮它来做这件事情

此时的各协议栈对比

5.4.1 从了解QUIC开始

QUIC是基于UDP的传输层协议,它提供了像TCP一样的可靠性。同时它实现了数据传输时延0 RTT

同时它还提供了数据传输的安全保障、以及像HTTP 2一样的应用数据二进制分帧、流和多路复用

当然还有其他重要特性:

  • 前向纠错:QUIC协议传输的每个数据包中除了含有它本身的数据之外还会带有其他包的数据,即可实现在有少量丢包的情况下,可以使用其他包的数据而不用重传

  • 快速重启会话:普通基于tcp的连接,是基于两端的ip和端口和协议来建立的。如下面场景手机用户从使用4G切换到了使用wifi,会改变本身的ip,这就导致tcp连接必须重新创建。而QUIC协议使用特有的UUID来标记每一次连接,在网络环境发生变化的时候,只要UUID不变,就能不需要握手,继续传输数据。

注意看协议栈的图:QUIC并不是在建立在保障安全的协议TLS之上,可以说是QUIC内部包含了TLS。且TLS1.3优化了TLS之前的握手

5.4.2 HTTP 3.0

HTTP 3.0 这时候再看就简单多了。数据传输层的QUIC把活都干的差不多了,应用层的仅需调用下层接口即可(专门的QUIC函数)

剩下的还是和HTTP 2.0 一样还是使用流来发送请求响应,因为HTTP 3.0 流的管理是由QUIC来做个HTTP 3.0里的帧结构比2.0更为简单

写到最后:发表感言——无敌打工仔谷歌牛皮

参考致谢:

本文使用 👉mdnice 排版