前端网络面试题

210 阅读17分钟

HTTP

什么是 HTTP

超文本传输协议(HTTP)是用于从 Web 服务器传输超文本(HTML)信息到浏览器的协议。

有哪些 HTTP 响应状态码

1xx: 信息类,接收请求并且正在处理

2xx: 成功类 请求正常并处理完毕

  • 200: ok 请求成功
  • 204: no content 返回为空,但是处理了。(MDN 描述为:对于该请求没有内容可发送,但头部字段可能有用,用户代理可能会用此时的请求头部信息来更新原来资源的头部缓存字段)
  • 206: partial content 返回部分数据。(MDN 描述为:当从客户端发送 range 返回标头以只请求资源的一部分时,将用此响应代码)

3xx: 重定向消息类,请求的资源不可用,需要重新请求

  • 301:moved permanently 重定向,永久重定向,表示资源地址发生了变化,在响应中给出了新的 URL。注:get 方法不会变更,其他方法可能会变更为 GET 方法,应用场景:网站重构。
  • 302:found 重定向,表示所请求的资源的 URL 暂时更改,未来可能会对 URL 进行进一步的改变,因此,客户端应当在将来的请求中使用新的 URL。
  • 303: 表示客户端请求的数据在别的服务器被拿到,服务端发送此响应,以指客户端通过一个 GET 请求在另一个 URL 中获取所请求的数据。
  • 304:浏览器缓存相关 - 告诉客户端响应还没被修改,因此客户端可以继续使用相同的缓存版本的响应,而不需要重新发送请求。
  • 308:permanent redirect 重定向,永久重定向,和 301 的区别是用户代理不能更改做使用的 HTTP 方法,如何在第一个请求中使用了 POST 请求,在第二个请求中必须使用 POST 方法。注:方法和消息主体都不发生变化,应用场景:使用非 GET 链接/操作的重组网站。

4xx: 客户端错误类,服务端无法根据当前请求参数返回数据

  • 400: bad request 请求参数错误
  • 401: unauthorized 未授权,需要登录
  • 403: forbidden 禁止访问,权限问题
  • 404: not found 未找到,资源不存在。注:服务器拒绝查询资源,且不想说明原因的时候也会返回404状态码。
  • 405: method not allowed

5xx: 服务器错误类,服务端处理请求出错

  • 500: internal server error 服务器内部错误。
  • 501: not implemented 服务器不支持该功能。
  • 502: bad gateway 服务器作为网关或者代理,从上游服务器接收到了错误响应。
  • 503: service unavailable 服务器暂时不可用,可以等待一段时间再试。

302、303、307 三种重定向的区别?

302、303、307 都是临时重定向。

  1. 302 是 HTTP1.0 中出现的重定向。
  2. HTTP1.1 中,派生出的 303307
  3. 303 GET 请求不会发生变更,其他方法会变成 GET 方法(消息主体丢失)。
  4. 307 当前的重定向会严格按照浏览器的述求方式发送请求,不会改变请求方式。

HTTP 版本能力

HTTP 的主要版本:1.01.12.03.0

HTTP1.0:

  • 协议版本信息现在会随着每个请求发送(HTTP/1.0 被追加到了 GET 行)。
  • 状态码会在响应开始时发送,使浏览器能了解请求执行成功或失败,并相应调整行为(如更新或使用本地缓存)。
  • 引入了 HTTP 标头的概念,无论是对于请求还是响应,允许传输元数据,使协议变得非常灵活,更具扩展性。
  • 在新 HTTP 标头的帮助下,具备了传输除纯文本 HTML 文件以外其他类型文档的能力(凭借 Content-Type 标头)。

HTTP1.1:

  • 连接可以复用,节省了多次打开 TCP 连接加载网页文档资源的时间。
  • 增加管线化技术,允许在第一个应答被完全发送之前就发送第二个请求,以降低通信延迟。
  • 支持响应分块。
  • 引入额外的缓存控制机制。
  • 引入内容协商机制,包括语言、编码、类型等。并允许客户端和服务器之间约定以最合适的内容进行交换。
  • 凭借 Host 标头,能够使不同域名配置在同一个 IP 地址的服务器上。

HTTP2.0:

  • HTTP/2 是二进制协议而不是文本协议。不再可读,也不可无障碍的手动创建,改善的优化技术现在可被实施。
  • 这是一个多路复用协议。并行的请求能在同一个链接中处理,移除了 HTTP/1.x 中顺序和阻塞的约束。
  • 压缩了标头。因为标头在一系列请求中常常是相似的,其移除了重复和传输重复数据的成本。
  • 其允许服务器在客户端缓存中填充数据,通过一个叫服务器推送的机制来提前请求。

HTTP3.0: 使用 QUIC 协议而不是 TCP 协议,QUIC 通过 UDP 协议实现

HTTP1.0 和 HTTP1.1 之间的区别?

连接上:

  • HTTP1.1 引入了长链接 keep-alive,减少建立 TCP 连接(存在三次握手)和关闭 TCP 的损耗

持久性连接:

  • 复用同一个 TCP 连接

资源处理:

整体的资源查询,而是用过 range 来做部分查询

缓存上:

  • 1.0 modified-xx expires 判断是否命中缓存
  • 1.1 etag if-match 缓存策略自动化
http2.0 和 1.1 之间的区别?

二进制协议:

  • 1.1: 头信息:文本存储、数据体:文本二进制存储
  • 2.0: 头信息,数据体都是二进制存储,信息流统称为帧,多路复用的基础

多路复用:

  • 2.0 中一方面复用 TCP 连接,同一个 TCP 连接,支持同时发送多个请求或者响应

头信息压缩:

  • 2.0 头信息压缩:对头信息进行字典化索引以及压缩(MDN:压缩了标头,因为标头在一系列请求中常常是相似的,其移除了重复和传输重复数据的成本。)

服务端推送:

  • 2.0 新增服务端推送:允许服务端主动向客户端未经请求地推送数据 - 资源非动态数据

不同的 HTTP 版本对于 TCP 的建立有何不同?

1.1 中通过 keep-alive 保持一个 TCP 连接一直生效 2.0 中支持多路复用的能力,可以在同一个 TCP 连接上同时发送多次请求,无并发限制

keep-alive

了解什么是 keep-alive 吗?建立过程是什么样的?使用场景?优缺点?

Keep-Alive 是一个通用消息头,允许消息发送者暗示连接的状态,还可以用来设置超时时长和最大请求数。

1.0 中默认是短链接的形式,需要在 Connection 中打开 keep-alive, 在 1.1 中默认是长链接,需要主动关闭才会关闭

建立过程:

  1. 客户端发送报文夹带 keep-alive 的头配置
  2. 服务端接收并处理,并且返回 keep-alive 的返回头
  3. 客户端和服务端维持当前的长链接

断开过程:

  1. 服务端主动断开 => 等客户端请求,等服务端返回请求并且不带 keep-alive,关闭连接
  2. 客户端主动断开 => 客户端请求头带上 close 字段,服务端接手后处理并断开连接,客户端接收资源并断开连接

优点:

  • 服务端来说:对 CPU 和内存的占用下降,
  • 请求和回复复用管道变少,降低堵塞,不受 TCP 的限制(TCP 连接最大数)
  • 减少了后续请求的延时
  • 无需每次异常都关闭 TCP 连接

缺点:

  • 长时间连接可能会导致无效占用

HTTPS 和 WebSocket

了解 HTTPS 吗? 和 HTTP 比起来有哪些区别?

HTTPS 经由 HTTP 进行通信,通过 TLS/SSL 加密数据包,实现了数据交换的可行性和安全性

HTTP 采用明文传输,存在安全性问题 HTTPS 采用 TLS/SSL 利用身份验证 + 信息加密 + 完整性校验

结合了 HTTP 的传输能力同时,对发起的 HTTP 请求数据进行加密操作,对接收到的 HTTP 内容进行解密

结论:HTTPS 相对于 HTTP 在安全上更加稳定,但是建立连接的性能上损耗更大

优化:通过整合请求以及长链接,降低建立连接的开销

SSL/TLS 是什么?作用和工作原理?

SSL/TLS 是一层安全协议,主要依赖是三类基本算法:散列函数 hash,对称加密,非对称加密

  • 散列函数 hash: MD5, SHA1... 单向不可逆,且输入敏感性,输出长度固定 任何数据的修改都会影响他值的改变,验证信息的完整性。
  • 对称加密: 两者公用一种秘钥,同事用其进行加密解密。 存在问题:如何保证密匙的传输安全性。
  • 非对称加密: 利用公钥加密,私钥解密,公钥是公开的,但是私钥是加密的,只能通过公钥解密,可以保证传输的安全性。 存在问题:存在中间截取问题。 解决方式:数字证书(网站提示:安全证书已过期)
WebSocket 基础原理?使用方式特点?

WebSocket 是一种基于 TCP 传输协议的全双工的网络技术,客户端和服务端建立连接后,可以进行双向通信,可以实现实时通信,可复用 HTTP 的通道。具体使用 Api 参考:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket 。

原理: 客户端向服务端发送 notify

特点:实时性较强

DNS 与 网络协议

DNS 作用:

  1. 网站域名地址转化为 IP 地址,客户端会向 DNS 服务器发送查询请求, 服务器返回我们对应的 IP 地址
  • 迭代查询:每次请求都会单词访问不同级别的 DNS 服务器
  • 递归查询:只会像目标服务器发送一次请求,优先自我查找,乡下以及服务器进行进一步查询

网络请求

有哪些网络请求?分别在那些场景使用?

  • GET: GET 方法请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。

  • POST: POST 方法用于将实体提交到指定的资源,通常导致在服务器上的状态变化或副作用。

  • PUT: PUT 方法用有效载荷请求替换目标资源的所有当前表示。

  • DELETE: DELETE 方法删除指定的资源。

  • OPTIONS: OPTIONS 方法用于描述目标资源的通信选项。

  • PATCH: PATCH 方法用于对资源应用部分修改。

GET 和 POST 的区别?

场景区别: GET 请求一般用于获取资源,而不对服务器资源做更改或者提交的操作 POST 请求一般会对服务器资源做更改从而影响数据

状态区别: 浏览器不会对 POST 请求作缓存,业务逻辑也很少会对 POST 请求作缓存

安全性: GET 请求的参数放在 url 中,请求参数会明文发送

参数: POST 请求支持更多种类的参数数据类型, 请求主题由 Content-type 标题指定。

PUT 和 POST 的区别?

PUT 请求一般用于更新资源,不会增加数据的种类,主要用于更新数据 POST 请求不仅可以修改数据,也可以用于创建新的资源包括种类,主要用于创建数据

OPTIONS 请求方法和使用场景

OPTIONS 请求就是预检请求,可用于检测服务器允许的 HTTP 方法。当发起跨域请求时,由于安全原因,触发一定条件时浏览器会在正式请求之前自动先发起 OPTIONS 请求,即 CORS 预检请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。

总结:

  1. 获取当前服务器的状态,性能,特征
  2. 检查服务器访问权限,例如:权限,CORS 报错

了解 HTTP 协议的头部算法?如何降低开销?

HPACK 算法

  1. 服务端和客户端共同维护建立字典,引用索引来标识重复的字符串
  2. 通过编码算法来压缩字符串,从而进一步减小头的大小

客户端,服务端

  1. 利用字典来跟踪和实时存储之前发送的数据值,实时记忆数据,对于相同的数据,不需要每次请求和响应重复发生,从而降低开销

请求头和响应头

了解的请求头和响应头分别有哪些?常见的 content-type 有那些?

请求头:浏览器告知服务端,自己的能力和配置

常用的请求头有:

  • Accept: 指定客户端能够接收的内容类型。如 text/htmlapplication/json
  • Accept-Encoding: 指定浏览器可以支持的 web 服务器返回内容压缩编码类型。如 gzipdeflatebr
  • Accept-Language: 浏览器可接受的语言。如 zh-CN
  • Cache-Control: 缓存控制,值为 no-cacheno-storemax-ages-maxagepublicprivatemust-revalidateproxy-revalidateno-transform
  • Authorization:用于传递认证信息,比如基本认证、Bearer 令牌等。
  • Connection: 连接属性 值为 keep-aliveclose
  • Cookie: 是浏览器用于保存重要信息的,例如登录后的身份信息,就放在 Cookie 里面。
  • Host: 这个值就是请求网址的域名信息。
  • User-Agent:用户代理,如 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36
  • referer: 当前页面的运行身份
响应头:服务端告知浏览器,响应信息和状态

常见的响应头:

  • Content-Type:指示响应体的媒体类型,告诉客户端返回的内容是什么类型的,例如 text/html, application/json 等。
  • Set-Cookie:用于在客户端设置 Cookie,通常用于会话管理或跟踪用户状态。
  • Cache-Control:指示客户端如何缓存响应,控制缓存的行为,例如 publicprivateno-cache 等。
  • Location:用于重定向,指示客户端应该重定向到哪个 URL
  • Access-Control-Allow-Origin:用于 CORS(跨域资源共享)策略,指示允许访问资源的域。

content-type: Content-TypeHTTP 协议中的一个头部字段,用于指示请求或响应中所传输的实体的媒体类型。使用 Content-Type 可以告知接收方如何解析和处理传输的数据,确保数据能够正确地被解析和处理。

常见的 Content-Type 类型包括 text/plaintext/htmlapplication/jsonapplication/xmlimage/jpegaudio/mp3video/mp4 等。

请求的数据很大时,如何优化?

请求头添加 Accept-Encoding: gzip/deflate/br,表示期望返回的内容被压缩,减少网络流量

TCP 和 UDP

TCP

TCP 工作原理

TCP 是一种面向连接的协议,通过三次握手建立可靠的连接。发送端将数据分割成多个 TCP 段,并通过 IP 进行传输。接收端接收到 TCP 段后进行重组,并通过确认机制确保数据的可靠性。TCP 还使用拥塞控制和流量控制机制来保证网络的稳定性。

TCP 特点与优缺点

特点:

  • 可靠性:TCP 保证数据的可靠传输,通过重传机制和确认机制避免数据丢失或损坏。
  • 顺序性:TCP 保证数据按照发送的顺序进行传输,接收端可以按照相同顺序重组数据。
  • 流量控制:TCP 使用滑动窗口机制来控制发送端的数据量,避免接收端缓冲区溢出。

优点:

  • 可靠性高:通过重传和确认机制保证数据的可靠性,适用于对数据完整性要求高的应用场景。
  • 顺序性强:保证数据按照发送顺序传输,适用于需要按序处理的应用场景。

缺点:

  • 开销大:TCP 需要建立连接、维护状态和进行确认,占用较多的网络资源。
  • 传输速度相对较慢:由于提供可靠性保证,TCP 的传输速度相对较慢。
TCP 的应用场景
  • 网页浏览:HTTP 协议使用 TCP 来传输网页内容,保证数据的可靠性和顺序性。
  • 文件传输:FTP 协议使用 TCP 来传输文件,确保文件的完整性和正确性。
  • 邮件传输:SMTP 协议使用 TCP 来传输电子邮件,保证邮件的可靠传输和顺序接收。
  • 远程登录:Telnet 和 SSH 等远程登录协议使用 TCP 来提供安全的登录通道。
  • 数据库访问:MySQL、Oracle 等数据库使用 TCP 来进行数据传输和查询。

UDP

UDP 工作原理

UDP 是一种无连接的协议,数据以数据报的形式独立发送。发送端将数据打包成 UDP 数据报并通过 IP 进行传输,接收端接收到数据报后直接提取数据。UDP 不提供可靠性保证和拥塞控制机制,因此传输速度较快。

UDP 特点与优缺点

特点

  • 无连接:UDP 不需要进行连接的建立和维护,数据报独立发送。
  • 无可靠性保证:UDP 不提供重传和确认机制,数据传输不可靠。
  • 传输速度快:由于无需建立连接和提供可靠性保证,UDP 传输速度较快。

优点:

  • 传输速度快:无需建立连接和提供可靠性保证,适用于实时性要求较高的应用场景。
  • 开销小:UDP 不需要维护连接状态和进行确认,占用较少的网络资源。

缺点:

  • 不可靠性高:由于无重传和确认机制,数据传输可能丢失或损坏。
  • 顺序性差:UDP 数据报独立发送,接收端无法保证数据按照发送顺序接收。
UDP 的应用场景
  • 实时通信:音频、视频会议以及实时游戏等应用利用 UDP 的快速传输特性,实现实时交互。
  • 流媒体:流媒体传输(如音频和视频的实时播放)通常使用 UDP,因为对于丢失少量数据并不敏感,但传输速度至关重要。
  • DNS 解析:域名系统(DNS)使用 UDP 进行域名解析请求和响应,以快速获取域名对应的 IP 地址。
  • 广播和多播:UDP 支持广播和多播传输,用于向多个主机发送数据,如局域网中的视频流广播。

TCP 和 UDP 的区别?

  • TCP 是面向连接的传输。UDP 是无连接的传输
  • TCP 有流量控制、拥塞控制,检验数据数据按序到达,而 UDP 则相反。
  • TCP 的路由选择只发生在建立连接的时候,而 UDP 的每个报文都要进行路由选择
  • TCP 是可靠性传输,他的可靠性是由超时重发机制实现的,而 UDP 则是不可靠传输
  • UDP 因为少了很多控制信息,所以传输速度比 TCP 速度快
  • TCP 适合用于传输大量数据,UDP 适合用于传输小量数据

如何实现一个并发控制器?

class LimitPromise {
  constructor(max) {
    // 并发上限
    this._max = max || 6;
    // 当前执行的任务数量
    this._count = 0;
    // 等待任务队列
    this._queueTasks = [];
    // 单例模式
    this._instance = null;
  }

  run(caller) {
    return new Promise((resolve, reject) => {
      // 创建处理任务
      const task = this._createTask(caller, resolve, reject);
      // 判断是否达到上限
      if (this._count >= this._max) {
        this._queueTasks.push(task);
      } else {
        task();
      }
    });
  }

  _createTask(caller, resolve, reject) {
    return () => {
      caller()
        .then((res) => {
          resolve(res);
        })
        .catch((err) => {
          reject(err);
        })
        .finally(() => {
          this._count--;
          if (this._queueTasks.length > 0) {
            // this._queueTasks.shift()(); //如下
            const task = this._queueTasks.shift();
            task();
          }
        });
      this._count++;
    };
  }

  // 单例模式
  static getInstance(max) {
    if (!this._instance) {
      this._instance = new LimitPromise(max);
    }
    return this._instance;
  }
}

const limitPromise = LimitPromise.getInstance(3);

const asyncTaask = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("111111");
    }, 3000);
  });
};

for (let i = 0; i < 10; i++) {
  limitPromise.run(asyncTaask).then((res) => {
    console.log(res, "res");
  });
}