Skip to content
当前页导航

HTTP

HTTP 常见的状态码

  • 100(继续):客户端继续发送请求,这是临时响应,用来通知客户端部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。

  • 101(切换协议):服务器根据客户端的请求切换协议,主要用于 WebSocket 或 HTTP/2 升级。

  • 200(成功):请求已成功,请求所希望的响应头或数据体将随此响应返回。

  • 201(已创建):请求成功并且服务器创建了新的资源。

  • 204(无内容):服务器成功处理请求,但没有返回任何内容。

  • 301(永久重定向):请求的网页已永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置。

  • 302(临时重定向):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

  • 304(未修改):服务器告诉客户端可以直接使用缓存的版本,无需重新请求。

  • 307(临时重定向):服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。

  • 400(错误请求):服务器不理解请求的语法。

  • 401(未授权):请求要求身份验证。对于需要登录的网页,服务器可能返回此响应。

  • 403(禁止):服务器拒绝请求。

  • 404(未找到):服务器找不到请求的网页。

  • 500(服务器内部错误):服务器遇到错误,无法完成请求。

  • 502(错误网关):服务器作为网关或代理,从上游服务器收到无效响应。

  • 503(服务不可用):服务器目前无法使用(由于超载或停机维护)。

HTTP 1.0 跟 HTTP 1.1 的区别

  • 长连接
    • 1.0 版本规定浏览器和服务器保持短暂的连接,浏览器每次请求都需要和服务器建立一个 TCP 连接,服务器处理完后会立即断开 TCP 连接,服务器也不会记录过去的请求(无状态)。不支持断点续传。
    • 1.1 版本可以通过设置 connection: keep-Alive 保持长连接,避免每次客户端都需要和服务器重复创建关闭 TCP 连接,如果客户端想关闭 HTTP 连接,可以在请求头中设置 connection: close 来告知服务器。支持断点续传。
  • 多并发请求
    • 1.0 版本规定每个请求必须等待前一个请求响应到达后才能发送,假设前一个请求响应一直不到达,则后面的请求会一直阻塞。
    • 1.1 版本支持并行响应请求,它是通过多建立几个 TCP 连接,来支持处理多个请求的。但是它响应请求的顺序还是按照请求发送的顺序。
  • host 域
    • 1.0 版本认为每台服务器都绑定一个唯一的 IP 地址,所以请求中的 URL 不会携带主机名(hostname),没有 host 域。而随着虚拟主机技术的发展,每台服务器上可以存在多个虚拟主机,并且它们共享一个 IP。
    • 1.1 版本的请求和响应消息中都支持 host 域,且如果请求消息中没有传递 host 域,还会报 400 的错误。
  • 缓存处理
    • 1.0 版本主要使用 header 中的 If-Modified-Since,Expires 来作为缓存判断的标准。1.1 版本则增加了强缓存和协商缓存,增加了 Etag 和 cache-control 等字段来控制缓存。
    • 1.1 版本增加了 24 个错误状态响应码。

HTTP 1.1 跟 HTTP 2.0 的区别

  • 二进制协议
    • 1.1 版本它的头信息传递只能是文本格式,数据体可以是文本或者二进制。
    • 2.0 版本则是一个彻底的二进制协议,头信息和数据体都是二进制,二进制的一个好处是,可以定义额外的帧,并且二进制对于解析数据友好。
  • 多路复用
    • 1.1 版本在处理多个并发请求时是通过多建立几个 TCP 连接,来支持处理多个请求的。而且它响应请求的顺序还是按照请求发送的顺序。
    • 2.0 版本则采用多路复用的技术,复用 TCP 连接。它也可以处理多个并发请求,并且不用按照请求顺序,一一对应的进行响应,这样就避免了 "队头堵塞"。
  • 头信息压缩
    • 1.1 版本在发送请求时,头部都是没有经过任何压缩,直接以纯文本进行传输的,随着网站的请求越来越多,导致在头部消耗的流量也越来越多,尤其是每次请求中携带的字段很多都是重复的,像 user agent 和 cookie 等。
    • 2.0 版本则对这进行了优化,它可以对头部信息使用 gzip 或 compress 进行压缩。
  • 服务器推送
    • 1.1 版本客户端在解析网页时,必须等到发现有静态资源时,才会发出静态资源请求。而 2.0 版本可以在服务器预知网页可能会请求静态资源,就主动把这些静态资源和网页一起发给客户端。
    • 2.0 版本中允许服务器未经请求,主动向客户端发送资源。常见场景是客户端在请求一个网页时,这个网页中包含了很多静态资源。正常情况下,
  • 数据流
    • 因为 2.0 版本响应的数据包是不按顺序发送的,所以同一个连接里的数据包可能属于不同的回应,2.0 对数据包进行标记,标明它属于哪个回应。2.0 将每个请求或响应的所有数据包,称为一个数据流(stream)。每个数据流都有一个独一无二的编号,数据包发送的时候,都会标记数据流 ID,用来区分它属于哪个数据流。并且还规定客户端发出的数据流,ID 一律为奇数,服务器发出的,ID 为偶数。客户端还可以指定数据流的优先级。优先级越高,服务器就会越早回应。

HTTP 2.0 跟 HTTP 3.0 的区别

  • 3.0 版本基于 UDP 协议实现,抛弃了 TCP 协议。
  • 2.0 版本的多路复用只有一个 TCP 连接,所以还是会有阻塞问题,3.0 版本则实现了在同一连接上可以有多个独立的逻辑数据流,实现了数据流的单独传输。
  • 3.0 版本集成了 TLS 加密
  • 3.0 版本基于 UDP 协议,使用 0-RTT 或 1-RTT 来建立连接,实现了快速握手

HTTP 和 HTTPS 的区别

基本概念:

HTTP,又叫超文本传输协议,是一种用于分布式、协作式和超媒体信息系统的应用层协议。

HTTPS,又叫超文本传输安全协议,是一种透过计算机网络进行安全通信的传输协议。

区别:

  • HTTP 默认的端口是 80 端口,HTTPS 的默认端口是 443.
  • HTTP 协议发送内容是明文传,不提供任何方式进行加密,所以如果攻击者截取了传输报文,就可以直接知道内容。HTTPS 则是在 HTTP 的通信基础上,利用 SSL/TLS 来对数据进行加密,能够保证数据的隐私。它需要到 CA 去申请证书。
  • HTTPS 客户端与服务端通信时,会经过证书的解析步骤,下面详细展开说一下:
md
## HTTPS 与服务器通信时的步骤

第一步: 客户端发起 HTTPS 请求

第二步: 服务端接收到请求之后,会把网站的证书信息,即公钥传送给客户端

第三步: 客户端去解析证书,验证公钥的有限期,颁发机构等信息,如果证书有问题,则会弹出警告;如果没问题,则会生成一个随机密钥 ,
并用公钥对随机密钥进行加密

第四步: 客户端把加密后的随机密钥发送给服务器,目的是让服务器以后使用这个随机密钥进行加密解密

第五步: 服务器接收到随机密钥后,利用私钥去进行解密

第六步: 服务端将要传递的内容使用随机密钥进行加密,然后返回给客户端

第七步: 客户端使用之前的随机密钥进行解密信息

https

GET 和 POST 的区别,何时使用 POST

  • get 是将参数拼接到 url 中,用户可以直接看到;post 是将参数放到 http 请求体中,用户无法直接看到
  • get 传输的数据量较小,一般小于 2KB ,post 传输的数据量较大,一般在 80KB 左右。
  • get 的安全性低,post 的安全性较高。但是 get 比 post 执行效率更好。
  • 建议在包含敏感信息和数据量较大的情况下使用 post 请求,做数据分页查询的时候建议使用 get ,其他数据的添加、修改等建议用 post

浏览器输入 URL 的过程

  • 域名( DNS )解析: 将域名解析成 IP 地址

    它查找 DNS 记录的顺序为,查看浏览器缓存 -- 操作系统缓存 -- 路由缓存 -- ISP(互联网服务提供商)的 DNS 服务器 -- 根服务器

  • TCP 连接:TCP 三次握手

md
## HTTP 三次握手的过程

第一次握手: 客户端尝试连接服务器,向服务器发送一个 SYN 包,SYN = 1,客户端进入等待状态
第二次握手: 服务器接收到客户端的 SYN 包后,回传一个带有 SYN 和 ACK 标志的数据包来传达确认收到信息。
第三次握手: 客户端接收到 SYN 和 ACK 标志的数据包后,再回传一个 ACK 标志的数据包给服务端,此时握手结束。
若在握手过程中某个阶段莫名中断,TCP 协议会再次以相同的顺序发送相同的数据包。

## 为什么要三次握手,而不是两次

为了防止服务器端开启一些无用的连接增加服务器开销,以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。就比如:

在传输过程中,客户端发起了请求,这时处于第一次握手,然后在第二次握手的时候,服务器端把数据直接返回给客户端,由于网络等原因,一直
卡着,客户端没有接受到,等到了超时时间后,就关闭了连接请求,再重新创建一个新的请求。所以如果没有三次握手的话,服务器端是不知道客户端
有没有接受到服务器返回的数据的。 而第三次握手的作用就是告诉服务器是否接受到了第二次传递的数据,接收到后,服务器就建立正常的 TCP 连接,
否则服务器会关闭连接端口,从而减少服务器开销和接收到失效的请求产生的错误
  • 发送 HTTP 请求
  • 服务器处理请求并返回 HTTP 报文
  • 浏览器解析渲染页面 https
  • 断开连接:TCP 四次挥手
md
## 四次挥手

第一次挥手: 客户端发送一个 FIN 标记的数据包,告诉服务器需要关闭连接,表示自己不需要发送请求了,但是还可以接收数据
第二次挥手: 服务器发送一个 ACK 的确认数据包,告诉客户端接收到关闭请求,但是还没有准备好,客户端接收到后等待服务器关闭连接
第三次挥手: 服务器发送一个 FIN 标记的数据包,告诉客户端准备关闭了,然后服务器等待客户端确认
第四次挥手: 客户端接收到服务器关闭响应后,再发送一个 ACK 的确认数据包,服务器接收到 ACK 数据包后,关闭连接。而客户端在等待一段时间(
两个最大段生命周期)后,还没有接收到服务器的响应,则认为服务器已关闭,然后自己也关闭连接

## 为什么要四次挥手,而不是三次

因为为了保证数据能够完整传输。当是三次的时候,假设在第二次挥手的时候,直接同意关闭的话,服务器可能还有其他请求的数据没有发送完,这样
就会造成数据的丢失,所以增加一个等待的时间是很有必要的。

常见 web 安全及防护原理

  • sql 注入:就是把 sql 命令插入到表单提交或者请求中,达到欺骗服务器的目的。
  • XSS 漏洞,通常指的是网站对用户输入数据未做有效过滤,攻击者可以将恶意脚本注入网站页面中,达到执行恶意代码的目的。前端提交数据的时候,禁止输入一些代码数据,或者对一些数据进行转义。
  • CSRF:跨站请求伪造。它是盗用用户的身份,去发送恶意请求,像修改用户数据,发表文章等。cookie 设置 HttpOnly ,这样就无法读取 cookie。也可以在每次请求中都增加一个 随机 token,有效期设置很短,它可以放在 cookie 或者响应头中,然后客户端发请求的时候,再回传给服务端进行验证。

浏览器缓存

  1. 强缓存(本地缓存):浏览器在有效时间内强制缓存,未超过这个时间(电脑本地时间),会直接读取浏览器的缓存。一般都是针对静态资源的。跟它相关的两个字段是 expirescache-control
  • expires 是设置一个过期时间,utc 格式的时间(Fri, 30 Apr 2024 04:05:46 GMT)。这个方式存在一个漏洞,如果客户端时间被用户主动修改了,那么对缓存的效果和预期会无法相符。
  • cache-control 是设置一个以秒为单位的过期时间,假设设置 cache-control: max-age=10 在设置的 10 秒之内,多次请求都会读取本地缓存,超过 10 秒之后,才会去读取服务器资源。修改客户端时间也没有影响。多个参数以逗号拼接。Cache-Control: no-cache, max-age=3600, public
md
max-age 是设置客户端缓存的过期时长。
s-maxage 是设置代理服务器缓存的过期时长。这个参数只有在设置了 public 才有用。
  • cache-control 还可以设置参数 no-cache 和 no-store 。它俩是互斥的,不能同时设置
md
cache-control 设置 no-store 表示禁止使用任何缓存策略,客户端的每次请求都需要服务器端给予全新的响应。 `Cache-Control: no-store`
cache-control 设置 no-cache 表示设置为协商缓存。即对于每次发起的请求都不会再去判断强制缓存是否过期,而是直接与服务器协商来验证缓存的有效性,若缓存未过期,则会使用本地缓存。`Cache-Control: no-cache`
  • cache-control 还可以设置参数 public 和 private 。它俩是互斥的,不能同时设置。默认值为 private 。
md
cache-control 设置 public 表示资源可以被浏览器缓存,又可以被代理服务器缓存。
cache-control 设置 private 表示资源只能被浏览器缓存。
对于一些静态资源建议设置 public
  • 同时设置的情况下,优先级 cache-control > expires
  1. 弱缓存(协商缓存):和服务器沟通,由服务器告知浏览器是否使用缓存。跟它相关的字段是 last-modifiedETag
  • last-modified 是设置资源的修改时间(utc 格式),要同时设置 Cache-Control: no-cachelast-modified: Thu, 29 Apr 2021 03:09:28 GMT 。当服务器设置好 last-modified 后,客户端第二次请求的时候,会自动带上 If-Modified-Since 字段,服务器去判断 If-Modified-Since 和 资源文件最新的修改时间是否相同,如果相同,则返回 304 状态码,告知浏览器使用本地缓存,如果不一样,则返回新的资源。
md
## last-modified 的不足

首先它只是根据资源最后的修改时间戳进行判断的,虽然请求的文件资源名字进行了编辑,但内容并没有发生任何变化,时间戳也会更新。需要重新进行完整的资源请求,这就会造成网络带宽资源的浪费。

其次标识文件资源修改的时间戳单位是秒,如果文件修改的速度非常快,假设在几百毫秒内完成,那么上述通过时间戳的方式来验证缓存的有效性,是无法识别出该次文件资源的更新的。
  • ETag 是设置成根据文件内容生成一个 hash 值,客户端请求时,会自动带上 If-None-Match 字段,服务器去判断 If-None-Matchetag 的值是否相同,如果相同,则返回 304 状态码,告知浏览器使用本地缓存,如果不一样,则返回新的资源。
md
## ETag 的不足

不像强制缓存中 cache-control 可以完全替代 expires 的功能,在协商缓存中,ETag 并非 last-modified 的替代方案而是一种补充方案,因为它依旧存在一些弊端。

服务器对于生成文件资源的 ETag 需要付出额外的计算开销,如果资源的尺寸较大,数量较多且修改比较频繁,那么生成 ETag 的过程就会影响服务器的性能。

另一方面 ETag 字段值的生成分为强验证和弱验证,强验证根据资源内容进行生成,能够保证每个字节都相同;弱验证则根据资源的部分属性值来生成,生成速度快但无法确保每个字节都相同,并且在服务器集群场景下,也会因为不够准确而降低协商缓存有效性验证的成功率,所以恰当的方式是根据具体的资源使用场景选择恰当的缓存校验方式。

缓存决策

  • html 文件:为保证内容变化后能及时更新,建议使用协商缓存 Cache-Control: no-cache,至于 last-modifiedETag ,二者选一即可
  • js、css 文件:可以使用强制缓存,缓存时间可以设置得久一点,并且在文件后面加上 hash 值,这样当发生文件修改后,不同的文件便会有不同的文件指纹,即需要请求的文件 URL 不同了,因此必然会发生对资源的重新请求。
  • 图片、字体文件:因为网站对图片的修改基本都是更换修改,同时考虑到图片文件的数量及大小可能对客户端缓存空间造成不小的开销,所以可采用强制缓存且过期时间不宜过长。
  • 代理服务器(如 cdn)的缓存,建议不要缓存敏感信息资源。
  • 同一个根域名下的缓存是共享的,如 a.com、foo.a.com、bar.a.com 的根域都是 a.com

CDN 缓存

CDN 全称 Content Delivery Network,即内容分发网络,它是构建在现有网络基础上的虚拟智能网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、调度及内容分发等功能模块,使用户在请求所需访问的内容时能够就近获取,以此来降低网络拥塞,提高资源对用户的响应速度。

CDN 网络能够缓存网站资源来提升首次请求的响应速度,但并非能适用于网站所有资源类型,它往往仅被用来存放网站的静态资源文件。所谓静态资源,就是指不需要网站业务服务器参与计算即可得到的资源,包括第三方库的 JavaScript 脚本文件、样式表文件及图片等,这些文件的特点是访问频率高、承载流量大,但更新修改频次低,且不与业务有太多耦合。

CDN 网络的核心功能包括两点:缓存与回源

  • 缓存指的是将所需的静态资源文件复制一份到 CDN 缓存服务器上;
  • 回源指的是如果未在 CDN 缓存服务器上查找到目标资源,或 CDN 缓存服务器上的缓存资源已经过期,则重新追溯到网站根服务器获取相关资源的过程。

优化实践

我们以淘宝示例来进行说明,主站请求的域名为 www.taobao.com,而静态资源请求 CDN 服务器的域名有 g.alicdn.com 和 img.alicdn.com 两种,它们是有意设计成与主站域名不同的,这样做的原因主要有两点:

  • 第一点是避免对静态资源的请求携带不必要的 Cookie 信息
Cookie 的访问遵循同源策略,并且同一域名下的所有请求都会携带全部 Cookie 信息。

虽然 Cookie 的存储空间就算存满也并不是很大,但如果将所有资源请求都放在主站域名下,那么所产生的效果对于任何一个图片、JavaScript 脚本及样式表等静态资源文件的请求,都会携带完整的 Cookie 信息,若这些完全没有必要的开销积少成多,那么它们所产生的流量浪费就会很大,所以将 CDN 服务器的域名和主站域名进行区分是非常有价值的实践。
  • 第二点是考虑浏览器对同一域名下并发请求的限制。
因为浏览器对于同域名下的并发请求存在限制,通常 Chrome 的并发限制数是 6,其他浏览器可能多少会有所差异。这种限制也同时为我们提供了一种解决方案:通过增加类似域名的方式来提高并发请求数,比如对多个图片文件进行并发请求的场景

HTTP2.0 的 PUSH 缓存

post 为什么会发送两次请求

第一次请求其实是由浏览器自动发起的一种 OPTIONS 请求,它叫预检请求。

预检请求是在进行跨域资源共享 CORS 时,由浏览器自动发起的一种 OPTIONS 请求。它的存在是为了保障安全,并允许服务器决定是否允许跨域请求。

跨域请求是指在浏览器中向不同域名、不同端口或不同协议的资源发送请求。出于安全原因,浏览器默认禁止跨域请求,只允许同源策略。而当网页需要进行跨域请求时,浏览器会自动发送一个预检请求,以确定是否服务器允许实际的跨域请求。

预检请求中包含了一些额外的头部信息,如 Origin 和 Access-Control-Request-Method 等,用于告知服务器实际请求的方法和来源。服务器收到预检请求后,可以根据这些头部信息,进行验证和授权判断。如果服务器认可该跨域请求,将返回一个包含 Access-Control-Allow-Origin 等头部信息的响应,浏览器才会继续发送实际的跨域请求。

使用预检请求机制可以有效地防范跨域请求带来的安全风险,保护用户数据和隐私。

跨域的解决方案

什么是跨域

因为浏览器的同源策略,非同源的情况下即会产生跨域现象。会导致跨域的场景有 当 URL 的协议、主域名、子域名、端口中任意一个不相同的时候,就会产生跨域

跨域其实请求发送出去了,并且服务器也进行响应了,但是浏览器拦截了返回结果。

解决方案

jsonp

原理:利用 script 标签没有跨域限制的特性,向服务器发送请求。它只支持 GET 方法。

实现流程:

  • 封装一个函数名为 jsonp 的方法,接收 url、params、callback 三个参数,返回一个 promise 。
  • 内部实现,首先在 window 上创建一个跟传入的回调函数名一样的函数,用于接收服务器返回的数据,并且在接收完后,把动态创建的 script 删除,把 window 上挂载的此方法设为 null
  • 创建一个 script 标签,把传入的 url、参数和回调函数,拼接成 script 的 src
  • 最后服务器处理,并返回一个以传入的回调函数名的方法,并把需要传递给客户端的数据,作为函数的参数。
html
<!-- 客户端 -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <style></style>
  </head>
  <body>
    <button onclick="send()">发送jsonp</button>
    <script>
      function jsonp({ url, params, callback = 'show' }) {
        return new Promise((resolve, reject) => {
          const script = document.createElement('script')
          // 创建回调函数
          window[callback] = function (res) {
            resolve(res)
            document.body.removeChild(script)
            window[callback] = null
          }
          // 处理参数
          params = {
            callback,
            ...params,
          }

          let arr = []
          for (const key in params) {
            arr.push(`${key}=${params[key]}`)
          }
          script.src = `${url}?${arr.join('&')}`
          document.body.appendChild(script)
        })
      }

      function send() {
        jsonp({
          url: 'http://localhost:2000/say',
          params: {
            id: 1,
          },
        }).then((res) => {
          console.log(res) // {id: 1, name: "sky"}
        })
      }
    </script>
  </body>
</html>
js
// 服务端
const express = require('express')

const app = express()

app.get('/say', (req, res) => {
  const { callback, id } = req.query
  if (id === '1') {
    let obj = { id: 1, name: 'sky' }
    res.end(`${callback}(${JSON.stringify(obj)})`)
  } else if (id === '2') {
    let obj = { id: 2, name: 'jeeny' }
    res.end(`${callback}(${JSON.stringify(obj)})`)
  } else {
    res.end(`${callback}('不存在此id')`)
  }
})

app.listen(2000)

CORS(跨源资源共享)

实现 CORS 的关键是后端,只要后端配置就能够实现跨域。服务端设置 Access-Control-Allow-Origin 就可以开启 CORS,该属性表示哪些域名可以访问。

通过 nginx 反向代理

proxy服务器
server {
    listen       81;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
        add_header Access-Control-Allow-Credentials true;
    }
}

postMessage

利用 postMessage 发送消息,然后在页面通过监听 message 方法,接收消息。它可以解决以下 2 种情况:

  • 页面与嵌套的 iframe 传递消息
html
<!-- 父级页面 01.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>我是01.html</h2>
    <button onclick="handleMessage()">发送消息</button>
    <iframe
      src="http://localhost:5000/02.html"
      style="width: 300px; height: 300px"
    ></iframe>

    <script>
      // iframe 地址不能用系统文件地址格式,可以开启一个服务
      function handleMessage() {
        const iframe = document.getElementsByTagName('iframe')[0]
        iframe.contentWindow.postMessage(
          '我是发送到02页面的内容',
          'http://localhost:5000/02.html'
        )
      }
      window.addEventListener('message', (res) => {
        // 接收其他页面传过来的内容
        console.log(res)
      })
    </script>
  </body>
</html>

<!-- 子页面 02.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>我是02.html</h2>
    <button onclick="handleMessage()">发送消息</button>

    <script>
      function handleMessage() {
        window.parent.postMessage('返回给01页面的消息')
      }
      window.addEventListener('message', (res) => {
        // 接收其他页面传过来的内容
        console.log(res)
      })
    </script>
  </body>
</html>
  • 页面与通过 window.open 打开的页面通信,注意:父页面向子页面发送消息时,需子页面已经被 window.open 打开,或者打开的同时,设置一个异步,延迟 100ms 左右再发送消息
html
<!-- 父页面01.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>我是01.html</h2>
    <button onclick="handleMessage()">发送消息</button>

    <script>
      function handleMessage() {
        const iframe = window.open('http://localhost:5000/02.html')
        setTimeout(() => {
          iframe.postMessage('我是发送到02页面的内容')
        }, 100)
      }
      window.addEventListener('message', (res) => {
        // 接收其他页面传过来的内容
        console.log(res)
      })
    </script>
  </body>
</html>

<!-- 子页面 02.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>我是02.html</h2>
    <button onclick="handleMessage()">发送消息</button>

    <script>
      function handleMessage() {
        window.opener.postMessage('返回给01页面的消息')
      }
      window.addEventListener('message', (res) => {
        // 接收其他页面传过来的内容
        console.log(res)
      })
    </script>
  </body>
</html>

通过 node 中间件代理

通过 window.name + iframe,只适用于 iframe

通过 location.hash + iframe,只适用于 iframe

通过 document.domain + iframe

此方式只能用于二级域名,协议、端口相同的情况下,比如 a.baidu.com 和 b.baidu.com,只需要给需要通信的页面加上 document.domain ='baidu.com'

html
<!-- 父页面 01.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h2>我是01.html</h2>
    <button onclick="handleMessage()">获取内容</button>
    <iframe
      src="http://localhost:5000/02.html"
      style="width: 300px; height: 300px"
    ></iframe>

    <script>
      document.domain = 'baidu.com'
      function handleMessage() {
        const iframe = document.getElementsByTagName('iframe')[0]
        iframe.contentWindow.a
      }
    </script>
  </body>
</html>

<!-- 子页面02 -->
<script>
  document.domain = 'baidu.com'
  var a = 3
</script>

说说对 WebSocket 的理解?应用场景?

WebSocket 是什么

WebSocket 是一种网络传输协议,位于 OSI 模型的应用层。它允许在单个 TCP 连接上进行全双工通信,实现了客户端与服务器之间的双向数据传输,从而能更好地节省服务器资源和带宽,并达到实时通信的效果。

WebSocket 的特点

  1. 全双工通信

WebSocket 支持全双工通信,允许数据在两个方向上同时传输,客户端和服务器可以实时地发送和接收数据,达到瞬时同步的效果。

  1. 二进制帧结构

WebSocket 采用二进制帧结构,与 HTTP 完全不兼容。相比起 HTTP/2,WebSocket 更侧重于实时通信,不像 HTTP/2 那样定义流和多路复用等特性,因为 WebSocket 自身已经是全双工通信,不需要这些特性。

  1. 协议名和握手

WebSocket 引入了 ws 和 wss 分别代表明文和密文的 WebSocket 协议,且默认端口使用 80 或 443,与 HTTP 的端口一致。在连接建立时,客户端需要发送协议升级请求并进行握手,服务端返回接受手请求的响应,完成连接的建立。

  1. 优点
  • 较少的控制开销:相对于 HTTP 每次请求都需要携带完整的头部,WebSocket 的数据包头部较小,减少了控制开销。
  • 更强的实时性:相对于 HTTP 请求需要等待客户端发起请求才能响应,WebSocket 实现了持久连接,实时性更好。
  • 保持连接状态:WebSocket 连接建立后,客户端和服务器之间可以保持连接状态,省去了每次请求都要携带身份验证的过程。
  • 更好的二进制支持:WebSocket 定义了二进制帧,更好地处理二进制内容。
  • 支持扩展:用户可以扩展 WebSocket 协议,实现自定义的子协议,增加了灵活性。
  • 更好的压缩效果:在适当的扩展支持下,WebSocket 可以沿用之前内容的上下文,在传递类似的数据时,可以显著提高压缩率。

应用场景

  • 弹幕:在直播或视频网站中,用户可以实时发送弹幕评论,通过 WebSocket 实时将弹幕内容显示在视频画面上。
  • 媒体聊天:实现在线即时通信功能,例如在线聊天室、即时消息应用等。
  • 协同编辑:多个用户可以同时编辑同一个文档,实现实时协作编辑。
  • 基于位置的应用:例如地图应用中,实时显示用户的位置信息。
  • 体育实况更新:在体育比赛进行时,实时更新比分和比赛进展。
  • 股票基金报价实时更新:股票和基金价格实时更新,及时推送最新的行情信息。
html
<!DOCTYPE html>
<html>
  <head>
    <title>WebSocket Client</title>
  </head>
  <body>
    <input type="text" id="messageInput" placeholder="Enter your message" />
    <button onclick="sendMessage()">Send</button>
    <div id="messageArea"></div>

    <script>
      const socket = new WebSocket('ws://your_server_address')

      socket.onopen = function () {
        showMessage('WebSocket connection established.')
      }

      socket.onmessage = function (event) {
        showMessage('Received: ' + event.data)
      }

      function sendMessage() {
        const messageInput = document.getElementById('messageInput')
        const message = messageInput.value
        socket.send(message)
        showMessage('Sent: ' + message)
        messageInput.value = ''
      }

      function showMessage(message) {
        const messageArea = document.getElementById('messageArea')
        messageArea.innerHTML += '<p>' + message + '</p>'
      }
    </script>
  </body>
</html>

如何理解 UDP 和 TCP? 区别? 应用场景?

UDP

UDP(User Datagram Protocol),用户数据包协议,是一个简单的面向数据报的通信协议,即对应用层交下来的报文,不合并,不拆分,只是在其上面加上首部后就交给了下面的网络层

也就是说无论应用层交给 UDP 多长的报文,它统统发送,一次发送一个报文

而对接收方,接到后直接去除首部,交给上面的应用层就完成任务

特点如下:

  • UDP 不提供复杂的控制机制,利用 IP 提供面向无连接的通信服务
  • 传输途中出现丢包,UDP 也不负责重发
  • 当包的到达顺序出现乱序时,UDP 没有纠正的功能。
  • 并且它是将应用程序发来的数据在收到的那一刻,立即按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况,UDP 也无法进行流量控制等避免网络拥塞行为

TCP

TCP(传输控制协议):一种面向连接的、可靠的、基于字节流的传输层通信协议,把上面应用层交下来的数据看成无结构的字节流来发送

可以想象成流水形式的,发送方 TCP 会将数据放入“蓄水池”(缓存区),等到可以发送的时候就发送,不能发送就等着,TCP 会根据当前网络的拥塞状态来确定每个报文段的大小

特点如下:

  • TCP 充分地实现了数据传输时各种控制功能,可以进行丢包时的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在 UDP 中都没有。
  • 此外,TCP 作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。
  • 根据 TCP 的这些机制,在 IP 这种无连接的网络上也能够实现高可靠性的通信( 主要通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现)

区别

  • UDP 与 TCP 两者的都位于传输层
  • TCP 是面向连接的协议,建立连接 3 次握手、断开连接四次挥手,UDP 是面向无连接,数据传输前后不连接连接,发送端只负责将数据发送到网络,接收端从消息队列读取
  • TCP 提供可靠的服务,传输过程采用流量控制、编号与确认、计时器等手段确保数据无差错,不丢失。UDP 则尽可能传递数据,但不保证传递交付给对方
  • TCP 面向字节流,将应用层报文看成一串无结构的字节流,分解为多个 TCP 报文段传输后,在目的站重新装配。UDP 协议面向报文,不拆分应用层报文,只保留报文边界,一次发送一个报文,接收方去除报文首部后,原封不动将报文交给上层应用
  • TCP 只能点对点全双工通信。UDP 支持一对一、一对多、多对一和多对多的交互通信
TCPUDP
可靠性可靠不可靠
连接性面向连接无连接
报文面向字节流面向报文
效率传输效率低传输效率高
双共性全双工一对一、一对多、多对一、多对多
流量控制滑动窗口
拥塞控制慢开始、拥塞避免、快重传、快恢复
传输效率

应用场景

  • TCP 应用场景适用于对效率要求低,对准确性要求高或者要求有链接的场景。如电子邮件、远程终端接入、万维网、文件传输
  • UDP 适用场景为对效率要求高,对准确性要求低的场景。如域名转换、文件传输、网络管理、远程文件服务器

如何理解 TCP/IP 协议

TCP/IP,传输控制协议/网际协议,是指能够在多个不同网络间实现信息传输的协议簇(cù)。

  • TCP(传输控制协议):一种面向连接的、可靠的、基于字节流的传输层通信协议
  • IP(网际协议):用于封包交换数据网络的协议

TCP/IP 协议不仅仅指的是 TCP 和 IP 两个协议,而是指一个由 FTP、SMTP、TCP、UDP、IP 等协议构成的协议簇(cù),只是因为在 TCP/IP 协议中 TCP 协议和 IP 协议最具代表性,所以通称为 TCP/IP 协议

划分

TCP/IP 协议族按层次分别了五层体系或者四层体系

五层体系的协议结构是综合了 OSI 和 TCP/IP 优点的一种协议,包括应用层、传输层、网络层、数据链路层和物理层

五层协议的体系结构只是为介绍网络原理而设计的,实际应用还是 TCP/IP 四层体系结构,包括应用层、传输层、网络层(网际互联层)、网络接口层

sou

五层体系

应用层

TCP/IP 模型将 OSI 参考模型中的会话层、表示层和应用层的功能合并到一个应用层实现,通过不同的应用层协议为不同的应用提供服务

如:FTP、Telnet、DNS、SMTP 等

传输层

该层对应于 OSI 参考模型的传输层,为上层实体提供源端到对端主机的通信功能

传输层定义了两个主要协议:传输控制协议(TCP)和用户数据报协议(UDP)

其中面向连接的 TCP 协议保证了数据的传输可靠性,面向无连接的 UDP 协议能够实现数据包简单、快速地传输

网络层

负责为分组网络中的不同主机提供通信服务,并通过选择合适的路由将数据传递到目标主机

在发送数据时,网络层把运输层产生的报文段或用户数据封装成分组或包进行传送

数据链路层

数据链路层在两个相邻节点传输数据时,将网络层交下来的 IP 数据报组装成帧,在两个相邻节点之间的链路上传送帧

物理层

保数据可以在各种物理媒介上进行传输,为数据的传输提供可靠的环境

四层体系

层次名称单位功 能协 议
网络接口层负责实际数据的传输,对应 OSI 参考模型的下两层HDLC(高级链路控制协议)PPP(点对点协议) SLIP(串行线路接口协议)
网络层数据报负责网络间的寻址数据传输,对应 OSI 参考模型的第三层IP(网际协议) ICMP(网际控制消息协议)ARP(地址解析协议) RARP(反向地址解析协议)
传输层报文段负责提供可靠的传输服务,对应 OSI 参考模型的第四层TCP(控制传输协议) UDP(用户数据报协议)
应用层负责实现一切与应用程序相关的功能,对应 OSI 参考模型的上三层FTP(文件传输协议) HTTP(超文本传输协议) DNS(域名服务器协议)SMTP(简单邮件传输协议)NFS(网络文件系统协议)

如何理解 OSI 七层模型

OSI (Open System Interconnect)模型全称为开放式通信系统互连参考模型,是国际标准化组织 ( ISO ) 提出的一个试图使各种计算机在世界范围内互连为网络的标准框架

OSI 将计算机网络体系结构划分为七层,每一层实现各自的功能和协议,并完成与相邻层的接口通信。即每一层扮演固定的角色,互不打扰

sou

应用层

应用层位于 OSI 参考模型的第七层,其作用是通过应用程序间的交互来完成特定的网络应用

该层协议定义了应用进程之间的交互规则,通过不同的应用层协议为不同的网络应用提供服务。例如域名系统 DNS,支持万维网应用的 HTTP 协议,电子邮件系统采用的 SMTP 协议等

在应用层交互的数据单元我们称之为报文

表示层

表示层的作用是使通信的应用程序能够解释交换数据的含义,其位于 OSI 参考模型的第六层,向上为应用层提供服务,向下接收来自会话层的服务

该层提供的服务主要包括数据压缩,数据加密以及数据描述,使应用程序不必担心在各台计算机中表示和存储的内部格式差异

会话层

会话层就是负责建立、管理和终止表示层实体之间的通信会话

该层提供了数据交换的定界和同步功能,包括了建立检查点和恢复方案的方法

传输层

传输层的主要任务是为两台主机进程之间的通信提供服务,处理数据包错误、数据包次序,以及其他一些关键传输问题

传输层向高层屏蔽了下层数据通信的细节。因此,它是计算机通信体系结构中关键的一层

其中,主要的传输层协议是 TCP 和 UDP

网络层

两台计算机之间传送数据时其通信链路往往不止一条,所传输的信息甚至可能经过很多通信子网

网络层的主要任务就是选择合适的网间路由和交换节点,确保数据按时成功传送

在发送数据时,网络层把传输层产生的报文或用户数据报封装成分组和包,向下传输到数据链路层

在网络层使用的协议是无连接的网际协议(Internet Protocol)和许多路由协议,因此我们通常把该层简单地称为 IP 层

数据链路层

数据链路层通常也叫做链路层,在物理层和网络层之间。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层协议

在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧

每一帧的数据可以分成:报头 head 和数据 data 两部分:

  • head 标明数据发送者、接受者、数据类型,如 MAC 地址
  • data 存储了计算机之间交互的数据

通过控制信息我们可以知道一个帧的起止比特位置,此外,也能使接收端检测出所收到的帧有无差错,如果发现差错,数据链路层能够简单的丢弃掉这个帧,以避免继续占用网络资源

物理层

作为 OSI 参考模型中最低的一层,物理层的作用是实现计算机节点之间比特流的透明传送

该层的主要任务是确定与传输媒体的接口的一些特性(机械特性、电气特性、功能特性,过程特性)

该层主要是和硬件有关,与软件关系不大

数据在各层传输过程

sou

  • 应用层报文被传送到运输层
  • 在最简单的情况下,运输层收取到报文并附上附加信息,该首部将被接收端的运输层使用
  • 应用层报文和运输层首部信息一道构成了运输层报文段。附加的信息可能包括:允许接收端运输层向上向适当的应用程序交付报文的信息以及差错检测位信息。该信息让接收端能够判断报文中的比特是否在途中已被改变
  • 运输层则向网络层传递该报文段,网络层增加了如源和目的端系统地址等网络层首部信息,生成了网络层数据报
  • 网络层数据报接下来被传递给链路层,在数据链路层数据包添加发送端 MAC 地址和接收端 MAC 地址后被封装成数据帧
  • 在物理层数据帧被封装成比特流,之后通过传输介质传送到对端
  • 对端再一步步解开封装,获取到传送的数据

OSI 参考模型与 TCP/IP 参考模型区别

相同点:

  • OSI 参考模型与 TCP/IP 参考模型都采用了层次结构
  • 都能够提供面向连接和无连接两种通信服务机制

不同点:

  • OSI 采用的七层模型; TCP/IP 是四层或五层结构
  • TCP/IP 参考模型没有对网络接口层进行细分,只是一些概念性的描述; OSI 参考模型对服务和协议做了明确的区分
  • OSI 参考模型虽然网络划分为七层,但实现起来较困难。TCP/IP 参考模型作为一种简化的分层结构是可以的
  • TCP/IP 协议去掉表示层和会话层的原因在于会话层、表示层、应用层都是在应用程序内部实现的,最终产出的是一个应用数据包,而应用程序之间是几乎无法实现代码的抽象共享的,这也就造成 OSI 设想中的应用程序维度的分层是无法实现的