请求

常见的 HTTP 请求方法

  • GET: 向服务器获取数据;
  • POST:将实体提交到指定的资源,通常会造成服务器资源的修改
  • PUT:上传文件,更新数据(语义约定);
  • DELETE删除服务器上的对象(语义约定);
  • HEAD获取报文首部,与 GET 相比,不返回报文主体部分,多数由JS发起;
  • OPTIONS询问支持的请求方法,用来跨域请求,一般用于调试;
  • CONNECT:要求在与代理服务器通信时建立隧道,使用隧道进行 TCP 通信,多用于HTTPS和WebsSocket;
  • TRACE: 回显服务器收到的请求,主要⽤于测试或诊断。

GET 和 POST 的请求的区别

  • 缓存的角度,GET 请求会被浏览器主动缓存下来,留下历史记录,POST 不会
  • 编码的角度,GET 只能进行 URL 编码,只能接收 ASCll 字符,而 POST 没有限制
  • 参数的角度,GET 一般放在 URL 中,POST 放在请求体中,更适合传输敏感信息
  • 幂等性的角度,GET 是幂等的,而 POST 不是。(幂等表示执行相同的操作,结果也相同)
  • TCP 的角度,GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发送 header 部分,如果服务器响应 100 (continue),然后发送 body 部分。(火狐浏览器除外,它的 POST 请求只发送一个 TCP 包)

POST 和 PUT 请求的区别

  • PUT 请求是向服务器端发送数据,从而修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次 PUT 操作,其结果并没有不同。(可以理解为是更新数据
  • POST 请求是向服务器端发送数据,该请求会改变数据的种类等资源,它会创建新的内容。(可以理解为是创建数据

OPTIONS 请求方法及使用场景

客户端可以在采取具体资源请求之前,决定对该资源采取何种必要措施,或者了解服务器的性能。该请求方法的响应不能缓存。

OPTIONS 请求方法的主要用途有两个:

  1. 获取服务器支持的所有 HTTP 请求方法;
  2. 用来检查访问权限。例如:在进行 CORS 跨域资源共享时,对于复杂请求,就是使用 OPTIONS 方法先去探测,以判断是否有对指定资源的访问权限。

    复杂请求:CORS 时需要发送预检的请求

URI 和 URL 的区别

URI:统一资源标识符(Uniform Resource Identifier),一个用于标识某一互联网资源名称的字符串
URL:统一资源定位符(Uniform Resource Locator),一个用于标识和定位某一互联网资源名称的字符串(主要通过定位去标识),是 URI 的子集

URL 的构成:Scheme:[subScheme]://[username:password@]host:port/path?query#fregment

  • Scheme: 通信协议,比如 http、https
  • Host:服务器的域名主机或 IP 地址
  • Port:端口号
  • Path:目录,由”/“隔开
  • Query:查询,由此项开始可以给网页动态传递参数,用&隔开,每个参数的名和值用=隔开
  • Fragment:信息片段,用于指定网络资源中的某片段,#后面的内容
  • subscheme:可选,子协议,常用于区分数据库
  • username:password@:可选,用户名密码

从输入 URL 到 展示页面的全过程

  1. 首先浏览器进程中的 UI 线程会进行处理
    1. 如果是 URI,则会发起网络请求来获取内容
    2. 如果不是,则进入搜索引擎
  2. 检查缓存,如果开启了强制缓存,就直接使用本地缓存
  3. 如果需要发起网络请求,请求过程由网络线程来完成。
  4. 浏览器会检查自带的“预加载 HSTS(HTTP 严格传输安全)”列表,这个列表里包含了那些请求浏览器只能使用 HTTPS 进行连接的网站
  5. 进行 DNS 域名解析(此处涉及 DNS 的寻址过程),找到网页的存放服务器
  6. 浏览器与服务器建立 TCP 连接
  7. 如果是 HTTPS 这里还会有 TLS 的握手过程
  8. 服务器响应 HTTP 请求,返回内容
  9. 如果不是 HTML 文件,则意味着下载请求,此时会将数据传递到下载管理器;如果请求是 HTML 内容,则将数据传递到渲染器进程,此时浏览器应导航到请求站点,网络线程便通知 UI 线程数据准备就绪
  10. 接下来,UI 线程会寻找一个渲染器进程来进行网页渲染。当数据和渲染器进程都准备好后,HTML 数据通过 IPC 从浏览器进程传递到渲染器进程
  11. 渲染器进程接收 HTML 数据后,将开始加载资源并渲染页面
  12. 浏览器解析 HTML 代码,并请求 HTML 代码中的资源(比如 JavaScript、CSS、图片等,此处可能涉及 HTTP 缓存)
  13. 渲染器进程完成渲染后,通过 IPC 通知浏览器进程页面已加载
  14. 最终页面呈现给用户

TCP——传输控制协议(Transmisson Control Protocol)

  • 可靠的、按顺序地传送数据
    • 确认和重传
  • 流量控制
    • 发送方不会淹没接收方
  • 拥塞控制
    • 当网络拥塞时,发送方降低发送速率

UDP——用户数据报协议(User Datagram Protocol)

  • 无连接
  • 不可靠数据传输
  • 无流量控制
  • 无拥塞控制

使用 TCP 的应用:HTTP(Web)、FTP(文件传送)、Telnet(远程登录)、SMTP(email)

使用 UDP 的应用:流媒体、远程会议、DNS、Internet 电话

DNS

DNS是应用层的协议,是因特网的一项核心服务,是用于实现域名和 IP 地址相互映射的一个分布式数据库。因为域名的形式更符合人类的记忆习惯,而计算机更擅长处理数字,DNS就是为了解决这个问题。

DNS 同时使用 TCP 和 UDP 协议?

DNS 占用 53 号端口,同时使用 TCP 和 UDP 协议。
(1)在区域传输的时候使用 TCP 协议

  • 辅域名服务器会定时(一般 3 小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。区域传送使用 TCP 而不是 UDP,因为数据同步传送的数据量比一个请求应答的数据量要多得多。
  • TCP 是一种可靠连接,保证了数据的准确性。

(2)在域名解析的时候使用 UDP 协议

  • 客户端向 DNS 服务器查询域名,一般返回的内容都不超过 512 字节,用 UDP 传输即可。不用经过三次握手,这样 DNS 服务器负载更低,响应更快。理论上说,客户端也可以指定向 DNS 服务器查询时用 TCP,但事实上,很多 DNS 服务器进行配置的时候,仅支持 UDP 查询包。

DNS 的解析过程

DNS的查询有两种方式:递归和迭代。DNS客户端设置使用的DNS服务器一般是递归服务器,它负责全权处理客户端的DNS查询请求,直到返回最终结果。DNS服务器之间一般采用迭代查询方式
在浏览器输入 www.test.com 的解析过程如下:
(1) 检查浏览器缓存
(2)检查操作系统缓存,常见的如 hosts 文件
(3)检查路由器缓存
(4)如果前几步都没没找到,会向 ISP(网络服务提供商)的 LDNS 服务器查询
(5)如果 LDNS 服务器没找到,会向根域名服务器(Root Server)请求解析,分为以下几步:

  • 根服务器返回顶级域名(TLD)服务器如.com.cn.org等的地址,该例子中会返回.com的地址
  • 接着向顶级域名服务器发送请求,然后会返回次级域名(SLD)服务器的地址,本例子会返回.test的地址
  • 接着向二级域名服务器发送请求,然后会返回通过域名查询到的目标 IP,本例子会返回www.test.com的地址
  • Local DNS Server 会缓存结果,并返回给用户,缓存在系统中

DNS 优化

  • 减少 DNS 请求次数
  • 对于图片资源,可以 DNS 预获取:DOM 还没开始,浏览器开始解析地址;把解析好的地址放在本地缓存中,DOM 树生成完,要加载图片类资源时发现 DNS 解析好了
  • DNS 缓存。DNS 查询过程经历了很多步骤,我们应该尽早地返回真实地 IP 地址,减少查询过程。一般会缓存到浏览器地缓存中,或本地的 DNS 缓存服务器
  • DNS 负载均衡,云服务商处理的问题
  • 减少主机名

TCP

TCP 和 UDP 的特点和区别?

  • UDP(用户数据报协议)
    无连接的,最大可能交付,没有拥塞控制,面向报文传输(对于应用程序传下来的报文不合并也不拆分,直接添加到 UDP 的首部),支持一对一,一对多,多对一,多对多的交互通信。

    这里可以提到 QUIC 协议,是一种通用的、安全、多路复用的新型传输层协议。目的是代替 TCP。他有许多和 TCP 类似的特性,同时还有 TLS,将它们置于 UDP 传输之上的应用层协议。

  • TCP (传输控制协议)
    面向连接的,提供可靠交付,有流量控制拥塞控制,支持全双工通信,面向字节流的传输(把传下来的报文看成字节流,把字节流分成数据大小不等的块),每一条 TCP 只能是点对点的。

    这里就可能问到流量控制是什么,拥塞控制是什么

UDP 和 TCP 的首部格式

  • UDP
    首部字段只有 8 个字节,包括了源端口,目的端口,长度,检验和。
  • TCP
    • 序号:用于对字节流进行编号,假如序号是 301,说明第一个字节的编号是 301,如果携带的数据长度是 100 字节,那么下一个报文段的序号为 401
    • 确认号:期望收到的下一个报文段的序号。
    • 数据偏移:首部的长度。
    • 控制位
      • ACK:设置为 1,确认应答的字段有效。TCP 规定除了最初建立连接时的 SYN 包之外该为必须设为 1
      • RST:设置为 1,表示 TCP 连接出现问题时强制断开连接。
      • SYN:设置为 1,表示希望建立连接。并在其序列号的字段设置初始初始序列号
      • FIN:设置为 1,表示希望断开连接。
    • 窗口:窗口值作为接收方让发送方设置其发送窗口的依据。因为接收方数据缓存空间有限

TCP 三次握手

计算机网络 - 图1

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。

  • 第一次握手,客户端将 TCP 报文同步标志位 SYN 设置为 1,随机产生一个序列号 seq = J,保存在 TCP 的首部的序列号( Sequence Number )字段里,指明客户端打算连接的端口号,并发送数据包给服务端,发送完毕后,此时客户端进入 SYN_SENT 状态。
  • 第二次握手,服务端由报文标志位 SYN = 1 知道客户端要建立连接,所以把同步标志位 SYN和 确认标志位 ACK用于标志 TCP 是否进行了确认操作,) 置为 1,ack (头部的确认号 Ackownledge Number) = J + 1,随机产生一个序列号 K,并把该数据包发送给客户端,此时服务端进入 SYN_RCVD 状态。
  • 第三次握手,客户端收到确认后,检查 ack 是否为 J + 1, ACK 是否为 1。如果正确就将 ACK 置为 1,ack 置为 k + 1,发送给服务端。服务端收到后检查 ACK 是否为 1,ack 是否为 K + 1。如果正确就建立连接。客户端和服务端进入 ESTABLISH 状态,完成三次握手。

SYN=1 的报文段不能携带数据, 但要消耗掉一个序号 ACK 报文段可以携带数据,不携带数据则不消耗序号

那为什么要三次握手呢?两次不行吗?

根本原因:无法确认客户端的 接收能力
第三次握手的作用是客户端对服务器端的初始序号的确认。如果只使用两次握手,那么服务器就没有办法知道自己的序号是否已被确认。同时这样也是为了防止失效的请求报文段被服务器接收,而出现错误的情况。
我们假设 client 发出的第一个连接请求字段并没有丢失,而是在某个网络结点滞留了,以至于延误到释放连接后才到达服务端。
本来这是一个早就失效了的报文段。但是当它发送到服务端的时候,就会误以为是客户端在重新建立连接,于是就向客户端发送确认报文段,表示同意建立连接。假设是采用的两次握手,那么新的连接就建立了。但是客户端没有发送建立连接的请求,就不会理会服务端的确认,也不会向服务端发送数据。服务端就会等着,这样就会浪费掉服务器的很多资源。

TCP 四次挥手

计算机网络 - 图2

  • 第一次挥手:client 端发送关闭连接请求,向 server 端发送的报文标志位是 FIN 字段,设置序列号为 seq,此时 client 进入 FIN_WAIT1 状态,表示 client 端没有数据向 server 端发送了。
  • 第二次挥手:server 端收到 FIN 标志位之后,设置标志位 ACK = 1, ack = seq + 1,client 端进入 FIN_WAIT2 状态,server 端告诉 client ,我确认同意你的关闭请求。
  • 第三次挥手:server 端向 client 发送 FIN 标志位,seq 设置为 L,表示请求关闭连接,同时 server 端进入 LAST_ACK 状态。
  • 第四次挥手:client 端收到 server 端的 FIN 字段后,发送 ACK 的报文段,ack 设置为 L + 1。然后进入 TIME_WAIT 状态。服务端收到客户端的 ACK 报文后,关闭连接。客户端等待 2MSL(报文最大存活时间)后仍然没有收到回复,就会关闭连接。

为什么需要四次挥手?

由于 TCP 是一种面向连接,双向的,基于字节流的传输层通信协议。
这就意味着,关闭连接时,当客户端发送 FIN 报文段后,只是表示客户端没有数据发送给服务端了,服务端收到 FIN 报文段后,只是它知道客户端要关闭连接了,但是服务端还是可以发送数据的。直到服务端把数据都发送完。当服务端发送 FIN 报文段之后,这个时候才表示服务端没有数据发送给客户端了,才会关闭 TCP 连接。

为什么要等待 2MSL?

  • 保证 TCP 的连接能够可靠关闭
    由于 IP 协议的不可靠性或者其他网络原因,导致了服务端没有收到客户端的 ACK 报文,那么服务端在超时之后进行重新发送 FIN。如果此时客户端进入了 CLOSE 状态,那么重发的 FIN 就会找不到连接,就会发生连接错乱。 所以,客户端发送完最后的 ACK 之后不能直接进入 CLOSE 状态,而是保持 TIME_WAIT 状态,当再次收到 FIN 时,能够确保对方收到 ACK,正确的关闭连接
  • 保证这次连接的重复数据段从网络中消失,使下一个连接不会出现旧的请求报文。

什么是半连接队列?

服务器第一收到客户端的 SYN 之后,就会把处于 SYN_RCVD 状态,此时双方还没有完全建立连接,服务器会把这种状态下请求连接放在一个队列里,这个队列就是 半连接队列

全连接队列——就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列已经满了,就会发生丢包现象。

ISN ( Initial Sequence Number )是固定的吗?

当一端为建立连接而发送它的 SYN,它会选择一个初始序号。ISN 随时间而变化,因此每个连接都将具有不同的 ISN。ISN 可以看作一个 32 比特的计数器,每 4ms 加 1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做出错误的解释。

三次握手的其中一个重要功能就是客户端与服务端交换 ISN。以便让对方知道接下来接收数据的时候如何按序列号组装数据,如果 ISN 是固定的,攻击者很容易猜出后续的确认号。

三次握手可以携带数据吗?

第三次握手可以携带数据,第一次、第二次握手不可以携带数据。

第一次握手不可以携带数据,其中一个简单的原因就是会让服务器更加容易收到攻击。因为如果第一次可以携带数据的话,那么攻击者可以每次都在第一次握手的 SYN 报文中放入大量的数据。这会让服务器花费很多时间,内存空间来接收这些报文。而对于第三次的话,客户端已经进入了 ESTABLISHED 状态。对于客户端来说,它已经建立起连接了,并且也已经知道服务器的接收,发送能力是正常的了。

SYN 攻击是什么?

服务端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易收到 SYN 洪泛攻击。

SYN 攻击就是 client 在短时间内伪造大量不存在 IP 地址,并向 Server 不断发送 SYN 包,Server 则回复确认包,并等待 Client 确认,由于源地址不存在,因此 Server 需要不断重发直至超时,这些伪造的 SYN 包将长时间占用未连接队列,导致正常的 SYN 请求因为队列满而被丢弃。SYN 攻击是一种典型的 DDoS 攻击。

常见的预防手段:

  • 缩短超时时间
  • 增加最大半连接数
  • 过滤网关防护
  • SYN cookies 技术

TCP 的长连接和短连接

  • 短连接:客户端和服务端只会进行一个读写操作
  • 长连接:完成一次读写后,不会立刻关闭连接,下一次的读写操作依然可以使用这个连接。

TCP 的粘包和拆包以及解决办法

什么是粘包和拆包?

第一种情况,接收端正常收到两个数据包,即没有发生拆包和粘包的现象。
第二种情况,接收端只收到一个数据包,但是这一个数据包中包含了发送端发送的两个数据包的信息,这种现象即为粘包。这种情况由于接收端不知道这两个数据包的界限,所以对于接收端来说很难处理。
第三种情况,这种情况有两种表现形式,接收端收到了两个数据包,但是这两个数据包要么是不完整的,要么就是多出来一块,这种情况即发生了拆包和粘包。这两种情况如果不加特殊处理,对于接收端同样是不好处理的。

为什么会发生粘包和拆包?

  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包。
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

粘包、拆包的解决办法

由于 TCP 本身是面向字节流的,无法理解上层的业务数据,所以在底层是无法保证数据包不被拆分和重组的,这个问题只能通过上层的应用协议栈设计来解决,根据业界的主流协议的解决方案

  • 消息定长
  • 设置消息边界
  • 将消息分为消息头和消息体

为什么常说 TCP 有粘包和拆包的问题而不说 UDP ?

因为 UDP 是基于报文传输的,UDP 首部用了 16 bit 来指示 UDP 的数据报文的长度。因此能在应用层把不同的数据报文区分开,从而避免了粘包和拆包的问题。

而 TCP 是基于字节流的,应用层和 TCP 传输层之间的数据交互是大小不等的数据块,但是 TCP 并没有把这些数据块区分边界,仅仅是一连串没有结构的字节流;另外从 TCP 的帧结构也可以看出,在 TCP 的首部没有表示数据长度的字段,基于上面两点,在使用 TCP 传输数据时,才有粘包或者拆包现象发生的可能。

TCP 可靠传输

TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在规定时间内没有收到确认,那么就重传这个报文段。

一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT

加权平均往返时间 RTTs

超时时间 RTO,应该略大于 RTTs

TCP 滑动窗口

窗口是缓存的一部分,用来暂时存放字节流。

发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。

发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。

接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 { 31, 34, 35 },其中 { 31 } 按序到达,而 { 34, 35 } 就不是,因此只对字节 31 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。

TCP 的流量控制

流量控制是为了 控制发送方发送速率,保证接收方来得及接收

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

实际上,为了避免此问题的产生,发送端主机会时不时的发送一个叫做窗口探测的数据段,此数据段仅包含一个字节来获取最新的窗口大小信息。

TCP 的拥塞控制

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。

因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。

TCP 主要通过四个方法来进行拥塞控制:

  • 慢开始
  • 拥塞避免
  • 快重传
  • 快恢复。

发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。

慢开始与拥塞避免

发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …

注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。

如果出现了超时,则令 ssthresh = cwnd / 2,然后重新执行慢开始。

快重传和快恢复

在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 表示 M3 丢失,立即重传 M3。

慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

Cookie 与 Session

Cookie

因为HTTP是无状态协议,之前已认证成功的用户状态无法通过协议层面保存下来,即无法状态管理,因此即使当该用户下一次继续访问,也无法区分他和其他用户。
Cookie技术通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
应用场景

  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)
  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息

创建 cookie

Cookie会根据从服务器端发送的响应报文中的Set-Cookie字段,通知客户端保存Cookie,当下次客户端再往服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送处去
服务端发现客户端发来的Cookie后,会去检查究竟是从哪个客户端发来的连接请求,对比服务器上的记录,最后得到之前的信息。
Set-Cookie 响应头部和 Cookie 请求头部:Set-Cookie: <cookie名>=<cookie值>

第一方cookie 和 第三方cookie

第一方cookie:由使用者当前所浏览的网站所建立,第一方cookie会记录使用者的信息和登录状态,让浏览体验更好。在电商平台将产品加入购物车,资料不会因为导出浏览而丢失。
总的来说,第一方cookie主要用来优化用户体验,让使用者再次访问相同网站时不必重复输入相同的信息

第三方cookie:如果这个网站嵌入了第三方代码,这个代码会产生cookie,浏览器会写入第三方代码所在的域名,这个和使用者浏览的网址域名不同,这就是第三方建立的cookie。主要用来跨网站追踪用户记录,使用者到每一个不同的网站浏览网页的记录,都能透过第三方cookie整合到第三方数据收集平台;
用途:1. 广泛应用于广告等行业,对用户做到精准投放个性化广告的用途。2. 使用第三方SDK进行前端异常或者性能监控,一般他们都需要标识每个用户来方便排查问题或者统计UV数据,所以当你请求这个站点的时候,它们就set一个Cookie,后续所有日志上报请求都会带上这个Cookie

跨域时如何携带 cookie

服务端:

  1. Access-Control-Allow-Origin: 设置域名
  2. Access-Control-Allow-Credentials: true

客户端:

  1. withCredentials = true

fetch 跨域携带 cookie 。
fetch 第二个参数对象可以设置模式,same-origin 表示同源可以进行访问;cors 表示同源和带有 cors 响应头的跨域可以请求成功
在第二个参数对象中设置credentials: 'include'就可以携带 cookie 了,same-origin:同源才能发送 cookie

cookie 属性

  • Domain
    cookie 可以送达的主机名。如果不指定,默认为 origin,不包含子域名。如果指定了 Domain,则一般包含子域名。
  • path
    Path 标识指定了主机下的哪些路径可以接受 Cookie(该 URL 路径必须存在于请求 URL 中)。以字符 %x2F (“/“) 作为路径分隔符,子路径也会被匹配。
  • Expires/Max-Age
    • max-age 是 HTTP1.1 中,指我们 web 中的文件被访问后存活的时间,是个相对值。相对请求时间,优先级比Expires
    • Expires :cookie 的最长有效时间,如果没有设置这个值的话,表示一个会话期 Cookie,一个会话结束语客户端被关闭时
  • Size: 大小限制为 4k
  • HttpOnly
    用于阻止 Javascript 通过Dcoument.cookie属性访问 cookie
    此类 Cookie 仅作用于服务器。例如,持久化服务器端会话的 Cookie 不需要对 JavaScript 可用。此预防措施有助于防范跨站点脚本(XSS)攻击。
  • Secure
    标记为 Secure 的 Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端,因此可以预防中间人攻击。localhost 不受限制
  • SameSite
    SameSite Cookie 允许服务器要求某个 cookie 在跨站请求时不会被发送,(其中 Site (en-US) 由可注册域定义),从而可以阻止跨站请求伪造攻击 CSRF
    • None。浏览器会在同站请求、跨站请求下继续发送 cookie,不区分大小写。
    • Strict:浏览器仅对同一站点的请求发送 cookie。
    • Lax。意味着 cookie 不会在跨站请求中被发送,例如加载图像或 frame 的请求,但用户从外部站点导航至源站时(例如通过链接)除外。 为默认选项。

cookie 作用域

Cookie 有两个很重要的属性: DomainPath,用来指示此 Cookie 的作用域

比如通过访问www.vinceruan.info添加的 Cookie 的域名默认就是www.vinceruan.info,通过访问blog.vinceruan.info所生成的 Cookie 的域名就是blog.vinceruan.info

在父域名上设置 cookie ,其子域名都可以共享获得 cookie
Path 告诉浏览器当前要添加的 Cookie 的路径归属,如果没有明确指明则默认为当前路径

比如通过访问www.vinceruan.info/java/hotspot.html添加的Cookie的默认路径就是/java/,通过blog.vinceruan.info/java/hotspot.html生成的Cookie的路径也是/java/

一般在实现 单点登录 的时候会用到这个 Domain 属性,因为可以通过在父级域设置 cookie,然后在各个子级域拿到存在父级域中的 cookie 值。

cookie 的安全问题防范

通过上面我们可以知道,可以设置 httponly、same-site、secure、domain、path等属性防止一些安全问题

cookie 的缺陷

  1. 数量受到限制。很多浏览器对能创建的 cookie 数量有限制,并且每个不能超过 4KB,每个 web 站点能设置的 cookie 总数不能超过 20 个。
  2. 安全性无法得到保障。通常跨站点脚本攻击往往利用网站漏洞在网站页面中植入脚本代码或网站页面引用第三方脚本代码,均存在跨站点脚本攻击的可能。在受到跨站点脚本攻击时,脚本指令会读取当前站点的所有 cookie 内容( 已不存在 cookie 作用域限制),然后通过某种方式将 cookie 内容提交到指定的服务器。
  3. 浏览器可以禁用 cookie

Session

cookie在web应用中经常承担请求方身份的作用,所以web应用在cookie的基础上封装了session的概念,专门做用户身份识别。
称为 “会话控制”。Session 对象存储特定用户会话所需的属性以及配置信息。
当用户访问服务器否个网页的时候,服务器会开辟一块内存,这块内存就叫做 session 空间。

Session 的认证流程

  1. 用户第一次请求服务器时,服务器根据用户提交的相关信息,创建对应的 session
  2. 请求返回时将此 session 的唯一标识session id返回给浏览器
  3. 浏览器收到服务器返回的session id信息后,会将此信息存入到 cookie 中,同时 cookie 记录此 session id 属于哪个域名。
  4. 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 cookie 信息。

    session 的缺陷

    因为 session 是存储在服务器当中的,所以 session 过多,会对服务器产生压力。

    cookie 和 session 比较

    大多数应用都是用 cookie 来实现 session 跟踪的。
    如果浏览器禁用了 cookie,可以使用 URL 重写的技术来进行会话跟踪,即每次 HTTP 交互,URL 后面都会被附加上一个类似 sid=xxxx 这样的参数,服务端根据这个来识别用户。

总结:

session 是服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。
cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现 session 的一种方式。

HTTP

HTTP 的作用是什么?

HTTP 协议就是超文本传输协议,位于应用层
作用就是用于客户端和服务器之间的通信
用HTTP协议能够明确区分哪端是客户端,哪端是服务器端
在 Web 开发中,页面缓存控制,数据传递,文档语言参数设定等等。都离不开 HTTP 协议。

HTTP 无状态、无连接

无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。

无状态是指HTTP协议不对请求和响应之间的通信状态进行保存。也就是说在HTTP协议这个级别,协议对于发送过的请求响应都不做持久化处理
原因:为了更快地处理大量事务,确保协议地可伸缩性,特意设计成如此简单的

HTTP 请求头和响应头

HTTP头可以看作一个键值对,他也是一种数据,我们可以自定义

HTTP Request Header 常见的请求头:

  • Accept: 浏览器能够处理的内容类型
  • Accept-Charset: 浏览器能够显示的字符集
  • Accept-Encoding:浏览器端接收的编码方式
  • Accept-Language:浏览器端接受的语言,用于服务端判断多语言
  • Connection:连接方式,如果是 keep-alive,且服务端支持,则会复用连接
  • Cookie:客户端存储的 cookie 字符串
  • Host:发出请求的页面所在的域
  • Referer:发出请求的页面的 URL
  • User-Agent:客户端标识
  • If-Modified-Since:上次访问时的更改时间,没有更新会返回 304
  • If-None-Match:次访问时使用的 E-Tag,通常是页面的信息摘要,这个比更改时间更准确一些
  • Cache-Control:控制 HTTP 缓存
    • max-age
      • 缓存存储的最大周期,相对于请求的时间
    • max-stale
      • 表示客户端愿意接收一个过期的资源,可以设置过期后还能接受的时间
    • min-fresh
      • 客户端希望一个能在指定秒数内保持其最新状态的响应
    • no-cache
      • 强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)
    • no-store
      • 缓存不应该存储请求和响应的内容,即不使用任何缓存
    • no-transform
      • 代理不能对资源进行转换,比如 content-range、content-encoding、content-type 等 http 头

HTTP Response Header 常见的响应头:

  • Date:当前的服务器日期
  • server: 服务端软件的类型
  • Set-Cookie:设置cookie,可以存在多个
  • Via:服务端的请求链路,对一些调试场景至关重要的一个头
  • Content-Encoding:内容编码方式,通常是 gzip
  • Content-Length:内容的长度,有利于浏览器判断内容是否已经结束
  • Content-Type: 表示后面的文档属于什么 MIME 类型,所有请求网页的都是 text/html
  • Connection:连接方式,keep-alive 会复用连接
  • ETag:(缓存相关)页面的信息摘要,用于判断是否需要重新到服务端取回页面
  • Expires:(缓存相关)过期时间,用于判断下次请求是否需要到服务端取回页面
  • Keep-Alive:保持连接不断时需要的一些信息,如timeout=5,max=100(发送请求的最大值)
  • Last-Modified:页面上次修改的时间
  • Cache-Control:控制 HTTP 缓存
    • must-revalidate
      • 一旦资源过期,在成功向原始服务器验证之前,缓存不能用该资源响应后续请求
    • no-cache
      • 强制要求缓存把请求提交给原始服务器进行验证(协商缓存验证)
    • no-store
      • 缓存不应该存储请求和响应的内容,即不使用任何缓存
    • no-transform
      • 代理不能对资源进行转换,比如 content-range、content-encoding、content-type 等 http 头
    • public
      • 表示响应可以被任何对象缓存,即使是通常不能被缓存的内容(post、没有 max-age/expires 头)
    • private
      • 响应只能被单个用户缓存,不能作为共享缓存(代理服务器不能缓存它)
    • proxy-revalidate
      • 仅适用于共享缓存
    • max-age
      • 缓存存储的最大周期,与 Expires 相反,时间是相对于请求的时间
    • s-maxage
      • 仅适用于共享缓存,覆盖 max-age/expires 头
  • Access-Control-Allow-Credentials:

常见的 Content-Type 属性值有以下四种:
(1)application/x-www-form-urlencoded:浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。该种方式提交的数据放在 body 里面,数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。
(2)multipart/form-data:该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。
(3)application/json:服务器消息主体是序列化后的 JSON 字符串。
(4)text/xml:该种方式主要用来提交 XML 格式的数据。

HTTP 状态码

HTTP状态码负责表示客户端HTTP请求的返回结果、标记服务器端的处理是否正常、通知出现的错误等工作
状态码的分类:


类别 原因短语
1xx 信息性状态码 接收的请求正在处理
2xx 成功状态码 请求正常处理完毕
3xx 重定向状态码 需要进行附加操作以完成请求
4xx 客户端错误状态码 服务器无法处理请求
5xx 服务端错误状态码 服务器处理请求错误

mdn 状态码

常见状态码

  • 100:服务器已经接受到了请求头,客户端应该继续发送请求主体
  • 101:服务器已经理解了客户端的请求,并通过upgrade消息头通知客户端采用不同的协议来完成这个请求
  • 103:用来在最终的HTTP消息之前返回一些响应头,允许用户在服务器还在准备响应数据的时候加载一些资源。
  • 200 OK:表示从客户端发来的请求在服务端被正常处理了
  • 204 No Content:表示服务器接收的请求已成功处理,但是在返回的响应报文中不含实体的主体部分,也不允许返回任何实体的主体。浏览器显示的页面不发生更新。一般在只需要从客户端往服务器发送信息,而对服务端不需要发送新内容的情况下使用
  • 206 Partial Content:表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容
  • 301 Moved Permanently:永久性重定向,该状态码表示请求的资源已被分配了新的 URI,以后应该使用资源现在所指的 URI。如果已经把资源对应的 URI 保存为书签了,这时应该按 Location 首部字段提示的 URI 重新保存
  • 302 Found:临时重定向,表示请求的资源已被分配了的新的 URI,希望用户本次能使用新的 URI 访问,如果用户把 URI 保存成书签,会仍然保留返回 302 状态码的页面对应的 URI
  • 303 See Other:表示请求的资源存在着另一个 URI ,应使用 GET 方法定向获取请求的资源。303 和302 功能相同,但是 303 明确表示客户端应该使用 GET 方法获取资源
  • 304 Not Modified:表示客户端发送附带条件的请求(采用 GET 方法的请求报文中包含 If-Match、If-Modified-Since、If-None-Match、If-Range、If-Unmodified-Since中任一首部)时,服务端允许请求访问资源,但是未满足条件的情况。返回时不包含任何响应的主体部分,主要用于缓存。和重定向没有关系
  • 307 Temporary Redirect:临时重定向,和 302 有相同的含义,虽然 302 标准禁止将 POST 变换成GET,但是实际大家都不遵守,307 不会从 POST 变为 GET
  • 400 Bad Request:表示请求报文中存在语法错误。当错误发生时,需修改请求的内容再次发送请求。浏览器会像 200 OK 一样对待该状态码
  • 401 Unauthorized:表示发送的请求需要通过 HTTP 认证的认证信息,如果之前以进行过一次请求,则表示认证失败
  • 403 Forbidden:表示请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由,当然也可以在实体的主体部分对原因进行描述。未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源IP地址试图访问)等都可能发生403
  • 404 Not Found:表示服务器上无法找到请求的资源。也可以在服务端拒绝请求并且不想说明理由时使用
  • 500 Internal Server Error:服务器端在执行请求时发生了错误,也可能是 Web 应用存在的 bug 或某些临时的故障
  • 503 Service Unavaliable:表示服务器暂时处于超负载或正在停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入RetryAfter首部字段再返回给客户端

HTTP 状态码 304 是多好还是少好

搜索引擎会更加青睐内容源更新频繁的网站。通过特定时间内对网站抓取返回的状态码来调节对该网站的抓取频次。若网站在一定时间内一直处于 304 的状态,那么搜索引擎可能会降低对网站的抓取次数。相反,若网站变化的频率非常之快,每次抓取都能获取新内容,那么日积月累的回访率也会提高。

产生较多 304 状态码的原因:

  • 页面更新周期长或不更新
  • 纯静态页面或强制生成静态 html

304 状态码出现过多会造成以下问题:

  • 网站快照停止
  • 收录减少
  • 权重下降

HTTP 协议的优点和缺点

HTTP 是超文本传输协议,它定义了客户端和服务器之间交换报文的格式和方式,默认使用 80 端口。它使用 TCP 作为传输层协议,保证了数据传输的可靠性。

优点

  • 协议本身很简单,所以被应用在各种场景
  • 无状态:可减少服务器的CPU以及内存资源的消耗

HTTP 协议具有以下缺点

  • 无状态:HTTP 是一个无状态的协议,对发送过的请求和响应不做持久化处理,无发根据之前的状态进行本次的请求处理。
  • 通信使用明文(不加密),内容可能会被窃听;
  • 不验证通信方的身份,因此有可能遭遇伪装;
  • 无法证明报文的完整性,所以有可能已遭篡改;

HTTP 报文结构

image.png

起始行 + HTTP首部字段 + 空行 + 实体

  • 起始行:每两个部分用空格隔开,最后一个部分后面应该接一个换行
    • 请求报文:方法 + 路径 + HTTP 版本 GET /home HTTP/1.1
    • 响应报文:HTTP 版本 + 状态码 + 原因 HTTP/1.1 200 OK ,也叫状态行
  • HTTP首部字段: 字段名不区分大小写,字段名不允许出现空格不可以出现下划线
  • 空行用来区分 头部 和 实体如果在头部中间故意加一个空行,那么空行后面的内容全部被视为实体
  • 实体:具体的数据,也就是 body 部分。请求报文对应请求体,响应报文对应响应体

HTTP1.1

HTTP1.1 解决了 HTTP1.0 最重要的两个问题

  • TCP 连接无法复用:每次请求都需要重新建立 TCP 通道,这就需要重复进行三次握手四次挥手,也就是说每个 TCP 连接只能发送一个请求。
  • 队头阻塞:多个请求并行出发,也只能一个接一个地排队

HTTP1.1 引入了长连接和管线化、Cookie技术

  • 长连接:HTTP1.1 会默认开启 connection:keep-alive,只要任意一端没有明确提出断开连接,则保持TCP连接状态。减少了TCP连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使HTTP请求和响应能更早地结束,网页加载也更快。
  • 管线化:不能等待响应就可以直接发送下一个请求,这样可以做到同时并行发送多个请求。
    但是,管线化只可以使浏览器并行发出请求,并没有从根本上解决队头阻塞问题,因为对请求的响应仍要遵循先进先出的原则,第一个请求的处理结果返回后,第二个请求才会得到响应。同时浏览器供应商很难实现管线化,而且,大多数浏览器默认禁用管线化,有的甚至删除了它。

HTTP1.1 还有一些改进:

  • 增加了与缓存相关的请求头

    If-Modify-Since <-> Last-Modified E-Tag <-> If-None-Match

  • 进行了带宽优化,并能够使用 range 头等来支持断点续传功能,可以返回 206

  • 新增错误类型,并增强了错误和响应码的语义特性
  • 新增 Host 头,如果请求消息中没有 Host 头,会报错。

基于 HTTP1.1 的前后端交互方案

  • HTTP long-polling
  • HTTP streaming
  • websocket

问题

  1. 没有真正解决队头阻塞的问题
  2. 明文传输,具有安全隐患
  3. header 中携带的内容过多,增加了传输成本
  4. 默认开启的 keep-alive 可能会服务端造成性能压力,比如,对比一次性的请求(如图片的 CDN 服务),在文件请求之后还保持了很长时间不必要的连接。

对 keep-alive 的理解

HTTP1.0 中默认是在每次请求/应答,客户端和服务器都要新建一个连接,完成之后立即断开连接,这就是短连接。当使用 Keep-Alive 模式时,Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接,这就是长连接。其使用方法如下:

  • HTTP1.0 版本是默认没有 Keep-alive 的,所以要想连接得到保持,必须手动配置发送Connection: keep-alive字段。若想断开 keep-alive 连接,需发送Connection:close字段;
  • HTTP1.1 规定了默认保持长连接,数据传输完成了保持 TCP 连接不断开,等待在同域名下继续用这个通道传输数据。如果需要关闭,需要客户端发送Connection:close首部字段。

Keep-Alive 的建立过程

  • 客户端向服务器在发送请求报文同时在首部添加发送 Connection 字段
  • 服务器收到请求并处理 Connection 字段
  • 服务器回送 Connection:Keep-Alive 字段给客户端
  • 客户端接收到 Connection 字段
  • Keep-Alive 连接建立成功

服务端自动断开过程(也就是没有 keep-alive)

  • 客户端向服务器只是发送内容报文(不包含 Connection 字段)
  • 服务器收到请求并处理
  • 服务器返回客户端请求的资源并关闭连接
  • 客户端接收资源,发现没有 Connection 字段,断开连接

客户端请求断开连接过程

  • 客户端向服务器发送 Connection:close 字段
  • 服务器收到请求并处理 connection 字段
  • 服务器回送响应资源并断开连接
  • 客户端接收资源并断开连接

开启 Keep-Alive 的优点:

  • 较少的 CPU 和内存的使⽤(由于同时打开的连接的减少了);
  • 允许请求和应答的 HTTP 管线化;
  • 降低拥塞控制 (TCP 连接减少了);
  • 减少了后续请求的延迟(⽆需再进⾏握⼿);
  • 报告错误⽆需关闭 TCP 连接;

开启 Keep-Alive 的缺点

  • 长时间的 Tcp 连接容易导致系统资源无效占用,浪费系统资源。

HTTP2.0

特点

  • 二进制分帧
    二进制分帧将通信传输信息分解为帧,这些帧交织在客户端和服务端之间的双向逻辑流中,使所有通信都可以在单个 TCP 上连接执行,而且该连接在整个对话期间一直处于打开状态。
  • 请求/响应复用
    为每帧分配一个流标识符,可以使它们在同一个 TCP 上进行独立发送,此技术实现了完全双向的请求和响应消息复用,解决了队头阻塞的问题。
    换句话说:一个请求对应一个流并分配一个 id,这样一个连接上可以有多个流,所有流的帧都可以相互混杂在一起,接收方可以根据流的 id 将帧分配到不同的请求中。
    总结一下:所有域名相同的请求都会通过同一个 TCP 连接并发送。同一个 TCP 连接中可以发送多个请求,对端可以通过帧中的标识符知道该帧属于哪一个请求。这样就可以解决队头阻塞的问题,极大提高了传输效率。真正意义上的多路复用
  • 报头压缩
    报头压缩的实现方式要求客户端和服务端都维护之前看见的报头字段列表。在发出第一个请求后,浏览器仅需要发送与之前一个报头的不同之处,而对于相同之处,服务器可以从报头的列表中获取。
  • 流优先化
    消息帧通过流发送。我们可以为每个流分配一个 id,同样也可以为它们分配优先级。这样,服务端就可以根据优先级确定处理它的顺序。
  • 服务端推送
    当一个客户端主动请求资源 K 时,如果服务端知道它很可能也需要资源 M,那么服务端就会主动把资源 M 推送给客户端。当客户端 真的需要 M 时,就可以从缓存中读取。
  • 流控制
    流控制允许接收者主动示意停止发送或减少发送的数据量

缺点

  1. TCP 以及 TCP + TLS 建立连接时的延时。HTTP2 使用 TCP 协议来传输,而如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,在传输数据之前,导致我们需要花掉 3~4 个 RTT.
  2. TCP 的队头阻塞并没有真正解决。在 HTTP2 中,多个请求是跑在一个 TCP 管道中的,但是当 HTTP2 出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP 连接中的所有请求。

HTTP2 的头部压缩算法是怎样的? ✅

HTTP2 的头部压缩是HPACK算法。在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,采用哈夫曼编码来压缩整数和字符串,可以达到 50%~90%的高压缩率。

具体来说:

  • 在客户端和服务器端使用 “首部表” 来跟踪和存储之前发送的键值对,对于相同的数据,不再通过每次请求和响应发送
  • 首部表在 HTTP 2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新
  • 每个新的首部键值对要么被追加到当前表的末尾,要么替换表中之前的值。

例如下图中的两个请求, 请求一发送了所有的头部字段,第二个请求则只需要发送差异数据,这样可以减少冗余数据,降低开销。

计算机网络 - 图4

HTTP3

HTTP/3 基于 UDP 协议实现了类似于 TCP 的多路复用数据流、传输可靠性等功能,这套功能被称为 QUIC 协议。
计算机网络 - 图5

  1. 流量控制、传输可靠性功能:QUIC 在 UDP 的基础上增加了一层来保证数据传输可靠性,它提供了数据包重传、拥塞控制、以及其他一些 TCP 中的特性。
  2. 集成 TLS 加密功能:目前 QUIC 使用 TLS1.3,减少了握手所花费的 RTT 数。
  3. 多路复用:同一物理连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输,解决了 TCP 的队头阻塞问题。

计算机网络 - 图6

  1. 快速握手:由于基于 UDP,可以实现使用 0 ~ 1 个 RTT 来建立连接。

HTTPS

即与SSL组合的HTTP协议,HTTP+加密+认证+完整性保护 = HTTPS
image.png
作用:一是确定请求的目标服务端身份,二是保证传输的数据不会被网络中间节点窃听或者篡改。

HTTP 有以下安全性问题:

  • 使用明文进行通信,内容可能会被窃听;
  • 不验证通信方的身份,有可能遭遇伪装;
  • 无法证明报文的完整性,有可能遭篡改。

HTTPS首先与服务端建立一条加密通道
通过使用 TLS,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。

https 采用混合的加密机制。

  1. 第一阶段使用非对称加密方式
    1. 非对称加密(公开密钥加密):私有密钥不能让其他人知道,公开密钥可以任意发布,所有人都可以获得。发送密钥的一方使用对方的公开密钥进行加密,对方收到加密的信息后,再使用自己的私钥进行解密。处理速度比对称加密慢
    2. 传输密钥 K 是使用的非对称加密
  2. 第二阶段使用对称加密的方式
    1. 对称加密:加密和解密使用同一个密钥
    2. 对称加密是对 https 中内容传输加密,加快传输速度

image.png

非对称加密和对称加密的区别

这两类加密类型的主要区别在于,使用对称加密,可以使用一个密钥对要保护的消息进行加密和解密。非对称加密需要使用两个单独的密钥,即公钥和私钥,公钥加密,私钥解密。
安全层的主要职责就是对发起的 HTTP 请求的数据进行加密操作对接收到的 HTTP 的内容进行解密操作

SSL 握手过程

  1. 客户端通过发送 Client Hello 报文开始SSL/TLS通信,报文中同时包含了它的 SSL 版本加密组件(加密算法等)
  2. 服务器端可以进行 SSL 通信时,会以 Server Hello 报文作为应答,消息中包含了服务器端的 SSL 版本加密组件
  3. 之后服务器发送 Certificate 报文。报文中包含公开密钥证书
  4. 最后服务器发送Server Hello Done 报文通知客户端,最初阶段的SSL握手协商部分结束。
  5. SSL 第一次握手结束后,客户端以 Client Key Exchange 报文作为回应。报文中包含通信加密中使用的一种被称为 Pre-master-secret 的随机字符串的随机密码串。该报文用公钥进行加密
  6. 接着客户端继续发送Change Cipher Spec 报文。该报文会提示服务器,在此报文后的通信使用Pre-master-secret 密钥加密
  7. 客户端发送 Finished 报文。该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够解密该报文作为判定标准。
  8. 服务器同样发送 Change Cipher Spec 报文。
  9. 服务器同样发送 Finished 报文
  10. 服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信收到SSL的保护,从此处开始进行应用层协议的通信,即发送HTTP请求。

接口设计规范

https://docs.microsoft.com/zh-cn/azure/architecture/best-practices/api-design

对 WebSocket 的理解

WebSocket 是 HTML5 提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议。它基于 TCP 传输协议,并复用 HTTP 的握手通道。浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。

WebSocket 的出现就解决了半双工通信的弊端。它最大的特点是:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。

WebSocket 原理:客户端向 WebSocket 服务器通知(notify)一个带有所有接收者 ID(recipients IDs)的事件(event),服务器接收后立即通知所有活跃的(active)客户端,只有 ID 在接收者 ID 序列中的客户端才会处理这个事件。

WebSocket 特点的如下:

  • 支持双向通信,实时性更强
  • 可以发送文本,也可以发送二进制数据‘’
  • 建立在 TCP 协议之上,服务端的实现比较容易
  • 数据格式比较轻量,性能开销小,通信高效
  • 没有同源限制,客户端可以与任意服务器通信
  • 协议标识符是 ws(如果加密,则为 wss),服务器网址就是 URL
  • 与 HTTP 协议有着良好的兼容性。默认端口也是 80 和 443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

Websocket 的使用方法如下:
在客户端中:

  1. // 在index.html中直接写WebSocket,设置服务端的端口号为 9999
  2. let ws = new WebSocket('ws://localhost:9999')
  3. // 在客户端与服务端建立连接后触发
  4. ws.onopen = function () {
  5. console.log('Connection open.')
  6. ws.send('hello')
  7. }
  8. // 在服务端给客户端发来消息的时候触发
  9. ws.onmessage = function (res) {
  10. console.log(res) // 打印的是MessageEvent对象
  11. console.log(res.data) // 打印的是收到的消息
  12. }
  13. // 在客户端与服务端建立关闭后触发
  14. ws.onclose = function (evt) {
  15. console.log('Connection closed.')
  16. }

即时通讯的实现:短轮询、长轮询、SSE 和 WebSocket 间的区别?

短轮询和长轮询的目的都是用于实现客户端和服务器端的一个即时通讯。
短轮询的基本思路: 浏览器每隔一段时间向浏览器发送 http 请求,服务器端在收到请求后,不论是否有数据更新,都直接进行响应。这种方式实现的即时通信,本质上还是浏览器发送请求,服务器接受请求的一个过程,通过让客户端不断的进行请求,使得客户端能够模拟实时地收到服务器端的数据的变化。这种方式的优点是比较简单,易于理解。缺点是这种方式由于需要不断的建立 http 连接,严重浪费了服务器端和客户端的资源。当用户增加时,服务器端的压力就会变大,这是很不合理的。

长轮询的基本思路: 首先由客户端向服务器发起请求,当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制才返回。客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。长轮询和短轮询比起来,它的优点是明显减少了很多不必要的 http 请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。

SSE 的基本思想: 服务器使用流信息向服务器推送信息。严格地说,http 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 http 协议,目前除了 IE/Edge,其他浏览器都支持。它相对于前面两种方式来说,不需要建立过多的 http 请求,相比之下节约了资源。

WebSocket 是 HTML5 定义的一个新协议议,与传统的 http 协议不同,该协议允许由服务器主动的向客户端推送信息。使用 WebSocket 协议的缺点是在服务器端的配置比较复杂。WebSocket 是一个全双工的协议,也就是通信双方是平等的,可以相互发送消息,而 SSE 的方式是单向通信的,只能由服务器端向客户端推送信息,如果客户端需要发送信息就是属于下一个 http 请求了。

上面的四个通信协议,前三个都是基于 HTTP 协议的。

对于这四种即使通信协议,从性能的角度来看:

WebSocket > 长连接(SEE) > 长轮询 > 短轮询

但是,我们如果考虑浏览器的兼容性问题,顺序就恰恰相反了:

短轮询 > 长轮询 > 长连接(SEE) > WebSocket