9. 浏览器/网络

41 min

介绍下 DNS 查找规则

  1. 浏览器里查找 chrome://net-internals/#dns, 如果有,直接用
  2. 操作系统里找 etc 目录下,本机 host 文件,如果有,直接用
  3. 本地域名服务器,如果有,直接用
  4. 向域名服务器发送请求
    1. 查询 根域名服务器 . 得到顶级域名服务器 IP
    2. 查询 顶级域名服务器 com. 如果有,直接用
    3. 两种情况
      • 如果配置了 CDN,交给 CNAME 指向的 CDN 专用的 DNS 服务器
      • 如果没配置 CDN,交给权威域名服务器 baidu.com.

本机和域名服务器一般都会有高速缓存,以减少查询次数和时间。

键入 URL 后,网络世界发生了什么

  • URL 解析:判断输入的是一个合法的 URL 还是一个待搜索的关键词,并且根据你输入的内容进行自动完成、字符编码等操作
  • 发起请求:接着发起真正的 URL 请求
  • 查找缓存:如果浏览器本地缓存了这个资源,则会直接将数据转发给浏览器进程,如果没有缓存,则会查询 DNS 解析域名
  • DNS 查找:首先先找浏览器有没有 DNS 缓存(之前有访问记录),如果有则 返回 ip
    • 如果没有,则寻找本地的 host 文件,看看有没有域名记录,如果有则返回 ip
    • 如果本地 host 没有则直接向 DNS 服务器请求,如果还是没有,继续向上 DNS 服务器请求,直至返回,拿到 ip 地址
  • 三次握手:向服务器发送 http 请求之前,先要和服务器建立 tcp 连接,其实就是三次握手
  • 发送 http 请求:连接建立成功后,就可以发送 http 请求数据了
  • 浏览器渲染浏览器渲染

介绍下三次握手

  • seq:序列号,随机生成
  • ack:确认号,seq+1
  • ACK:确认序列号有效
  • SYN:发起新连接 主要流程:在 HTTP 请求之前,可以看到三次 tcp 连接
  1. 客户端向服务端,seq=clientid,SYN;
  2. 服务端向客户端,seq=serverid,ack=clientid+1,SYN,ACK
  3. 客户端向服务端,seq=clientid+1,ack=serverid+1,ACK 为什么第三次也要发 seq 呢?答案是每个 tcp 连接都要有序列号,以保证顺序控制和流量控制

TCP 有个重要的点就是可靠性,发送出一条消息,一定要等到对面说我收到了,才算发成功;

为什么需要握手?需要同步序列号,TCP 三次握手可以确保双方都具备发送和接收数据的能力。

为什么挥手是四次,握手是三次?因为握手的时候不需要发数据,挥手的时候可能还有数据正在发送(半连接状态);但握手的时候是不允许有所谓半建立的状态的,这是 TCP 的规范,所以握手的第二次中,serve 在回 ACK 的时候必须把 SYN 发过来;

介绍下四次挥手

  • seq:序列号,随机生成
  • ack:确认号,seq+1
  • ACK:确认序列号有效
  • SYN:发起新连接
  • FIN:完成 主要流程:(假设是客户端发起的
  1. 客户端向服务端:seq=clientid,FIN,客户端进入 FIN_WAIT_1,
  2. 服务端收到后,向客户端:ack=clientid+1,ACK,服务端进入 CLOSE_WAIT,
  3. 客户端收到后,客户端进入 FIN_WAIT_2(如果有未完成的请求或者别的响应,在这个阶段要完成,直到所有都处理完后,发起第三次挥手)
  4. 服务端向客户端:seq=serverid,ack=clientid+1,ACK,FIN,服务端进入 LAST_ACK
  5. 客户端收到后,向服务端发送:seq=clientid+1,ack=serverid+1,ACK,之后进入 TIME_WAIT 状态
  6. 服务端收到后,服务端进入 close
  7. 客户端在 2msl 后 自动进入 close

MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。 2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。

为什么需要四次?

关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。

服务端收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接;

浏览器是如何渲染页面的?

当浏览器的网络线程收到 HTML 文档后,会产生一个渲染任务,并将其传递给渲染主线程的消息队列。

在事件循环的机制下,渲染主线程取出消息队列中的渲染任务,开启渲染流程 整个渲染流程分为多个阶段分别是:

  1. 解析 Html => DOM 树,CSSOM 树
  2. 样式计算 => 带有样式的 DOM 树
  3. 布局 => lauoyt tree
  4. 分层
  5. 绘制,生成绘制指令
  6. 分块
  7. 光栅化

每个阶段都有明确的输入输出,这样渲染流程就形成了一套组织严密的生产流水线

  1. 渲染的第一步是 解析 HTML 解析过程中遇到 CSS 就解析 CSS,遇到 JS 执行 JS。为了提高效率,浏览器在开始解析前,会启动一个 预解析线程,率先下载 HTML 中外部 CSS 和 JS 文件。

    如果主线程解析到 link,此时外部的 CSS 文件还没有下载解析好,主线程不会等待,继续解析后续的 HTML。这是因为下载和解析 CSS 的工作是在 预解析线程 中进行的。这就是 CSS 不会阻塞 HTML 解析的根本原因。

    如果主线程解析到 script,会停止解析 HTML,转而等待 JS 文件下载好,并将全局代码解析执行完成后,才能继续解析 HTML,这是因为 JS 代码的执行过程会可能会修改当前的 DOM 树,所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 的根本原因。

    第一步完成后,会得到 DOM 树和 CSSOM 树,浏览器的默认样式,内部样式,外部样式,行内样式均会包含在 CSSOM 中。

  2. 渲染的下一步是 样式计算

    主线程遍历得到的 DOM,依次为树中的每个节点计算出它最终的样式,称之为样式计算。 在这一步,很多预设值会变成绝对值,比如 red 会变成 rgba(255,0,0); 相对单位会变成绝对单位,比如 em 会变成 px

    这一步结束后,会得到一颗带有样式的 DOM 树

  3. 接下来是 布局,布局完成后会得到布局树

    布局阶段会依次遍历 DOM 树的每一个节点,计算每个节点的几何信息,例如节点的宽高,相对包含块的位置。大部分时候,DOM 树和布局树并非一一对应。

  4. 下一步是 分层

    主线程会使用一套复杂的策略对整个布局树进行分层。

    分层的好处在于,将来某一层改变后,仅会对该层进行后续处理,从而提升效率。

    滚动条,堆叠上下文,transform,等样式或多或少的影响分层结果,也可以通过 will-change 属性更大程度影响分层效果。

  5. 再下一步是 绘制

    主线程会为每个层单独产生一套绘制指令,用于描述这一层的内容该如何画出来。

    完成绘制后,主线程会将每个图层的绘制信息提交给合成线程。剩余工作由合成线程完成。

  6. 再下一步是 分块

    合成线程首先对每个图层进行分块,将其划分为更多的小区域。(分多个线程来完成分块工作)

  7. 分块完成后,进入 光栅化 阶段 合成线程会将块信息交给 GPU 进程,以极高的速度完成光栅化。

    GPU 进程会开启多个线程来完成光栅化,并且优先处理靠近视口的块。

    光栅化的结果就是一块一块的位图。

  8. 最后一个阶段就是

    合成线程拿到每一层,每个块的位图后,生成一个个指引(quad)

    指引为表示出每个位图应该画到屏幕的哪个位置,以及会考虑到旋转,缩放等变形。

    变形发生在合成线程,与渲染主线程无关,这就是 _transform 效率高的本质原因

    合成线程会把 quad 交给 GPU 进程,由 GPU 进程产生系统调用,提交给 GPU 硬件,完成最终的屏幕成像。

介绍下 http 缓存

浏览器缓存策略分为两种:强缓存和协商缓存。

强缓存这个”强”实际形容得不太恰当,强缓存也称为本地缓存(local cache),意味着浏览器在一定时间内直接从本地缓存中读取资源,而不去服务器请求;

协商缓存,顾名思义就是浏览器和服务端有商有量,每次请求资源时,浏览器都会与服务端进行通信,根据服务器的响应决定是否使用本地缓存(强缓存)

  1. 浏览器在加载资源时,根据 request header 的 Expires(http1.0) 和 Cache-Control(http1.1) 判断是否命中强缓存,是则直接从本地缓存读取资源,返回 200 from memory/disk cache,不会发请求到服务器。
  2. 如果没有命中强缓存,浏览器一定会发送一个请求到服务器,通过 Last-Modified(http1.0) 和 ETag(http1.1) 验证资源是否命中协商缓存,如果命中,则返回 304 读取缓存资源
  3. 如果前面两者都没有命中,直接从服务器请求加载资源

强缓存通过 expired(少用)和 cache-control 控制 由于 expired 服务器的时间和客户端的时间不一样的情况下,所以 HTTP1.1 提出 cache-control

res.setHeader("Cache-Control", "no-store, max-age=60"); max-age 单位是秒

  • public 表示可以被浏览器和代理服务器缓存,存在 Authorization 头时默认为 private
  • private 只让客户端可以缓存该资源;代理服务器不缓存
  • no-cache 跳过设置强缓存,但是不妨碍设置协商缓
  • no-store 禁止使用缓存,每一次都要重新请求数据

协商缓存是利用的是 Last-Modified/If-Modified-Since 和 ETag/If-None-Match 这两对标识来管理的 Last-Modified/If-Modified-Since 的局限性是单位只到秒,

项目中有配置到缓存吗

强缓存适用于静态资源(如 JS、CSS、图片),结合文件名哈希确保更新。 协商缓存适用于动态资源(比如接口数据),通过 ETag 或 Last-Modified 减少服务器压力。

用户行为对浏览器缓存的影响

  • 打开网页,地址栏输入地址: 查找 disk cache 中是否有匹配。如有则使用;如没有则发送网络请求。
  • 普通刷新 (F5):因为 TAB 并没有关闭,因此 memory cache 是可用的,会被优先使用(如果匹配的话)。其次才是 disk cache。
  • 强制刷新 (Ctrl + F5):浏览器不使用缓存,因此发送的请求头部均带有 Cache-control:no-cache(为了兼容,还带了 Pragma:no-cache), 服务器直接返回 200 和最新内容。

更新强缓存有哪几种方式

  • 修改文件名
  • 修改 url 参数,版本号或者时间戳
  • service worker???

CDN 回源是什么

CDN 回源包括回源地址和加速域名。常规的 CDN 都是回源的。即当有用户访问某一个 URL 的时候,如果被解析到的那个 CDN 节点没有缓存响应的内容,或者是缓存已经到期,就会回源站去获取。如果没有人访问,那么 CDN 节点不会主动去源站拿的。

CDN 的优势

异地容灾,负载均衡

跨域是什么,如何解决

跨域是指浏览器的同源策略限制了从一个源(协议、域名、端口)加载或访问另一个源的资源。

如何解决跨域问题?

  1. CORS(跨域资源共享)
  2. JSONP
  3. 代理服务器
  4. Nginx 反向代理
  5. PostMessage

什么是 CORS?如何工作?

  • CORS (cross-origin resource sharing)是一种跨域资源共享机制,允许浏览器向不同源的服务器发起请求。CORS 是基于 http1.1 的一种跨域解决方案。
  1. 浏览器发送请求。
  2. 服务端返回 Access-Control-Allow-* 响应头。
  3. 浏览器根据响应头决定是否允许跨域访问。

JSONP 的原理是什么?

  • JSONP 利用 <script> 标签不受同源策略限制的特点,通过动态创建 <script> 标签加载资源。
  • 服务端返回一个包含回调函数的 JSON 数据。
  • 优点:兼容性好,简单易用。
  • 缺点:只支持 GET 请求,存在安全风险。

为什么开发环境不会跨域?**

浏览器向本地开发服务器(如 http://localhost:3000)发送请求。 本地开发服务器将请求转发到目标服务器(如 https://api.example.com)。 由于代理服务器和目标服务器之间没有同源限制,跨域问题被绕过。

  • 前端设置
    fetch('https://example.com/api', {
        credentials: 'include'
    });
  • 服务端设置
    res.setHeader('Access-Control-Allow-Origin', 'https://yourdomain.com');
    res.setHeader('Access-Control-Allow-Credentials', 'true');

跨域时如何处理 WebSocket?

  • WebSocket 不受同源策略限制,但需要服务端支持。
  • Nginx 配置
    location /ws/ {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
    }

HTTP 常见状态码

  • 1xx 信息响应:表示请求已被接受,需要继续处理。比如
    • 100(Continue) 表示到目前为止一切正常,客户端可继续请求。
    • 101(Switching Protocol) 表示服务器同意客户端的协议切换
  • 2xx 成功:表示请求已成功接收、理解、并处理。
    • 200Ok 表示请求成功,服务器返回请求的数据;
    • 201Created 请求成功且资源已被创建,常用于 POST/PUT;
    • 202Accepted 请求已接受,但尚未处理;
    • 204 表示请求成功但没有内容返回,通常用于 Delete 请求;
  • 3xx 重定向:表示要完成请求,需要进行进一步操作。比如
    • 301Move Permanently 表示请求的资源已被永久移动到新位置,301 和 302 的 response header 都有 location 字段,浏览器会自动跳转;
    • 302Found 请求的资源暂时被移动到新的位置,响应中也会包含新的 URL;
    • 304NOT Modified 表示资源没修改,客户端可以使用缓存的版本
  • 4xx 客户端错误:表示请求包含语法错误或无法完成。比如
    • 400Bad Request 表示服务器无法理解请求的数据格式;
    • 401Unauthorized 表示请求未授权,需要进行身份验证;
    • 403Forbidden 表示理解请求但拒绝执行;
    • 404Not Found 表示请求的资源不存在;
    • 405Method Not Allowed 表示请求方法不被服务器允许;
    • 408Request Timeout 表示服务器去等待客户端发送请求超时;
    • 409Conflict 表示请求与资源的当前状态冲突;
    • 410Gone 表示请求的资源永久删除了;
  • 5xx 服务器错误:表示服务器在处理请求时发生错误。比如
    • 500Internal Server Error 表示服务器内部错误;
    • 501Not Implemented 表示服务器不支持请求的功能;
    • 502Bad Gateway 表示服务器作为网关或代理,从上游服务器收到了无效响应;
    • 503Service unavailable 表示服务器暂不可用,通常是由于维护或过载;
    • 504Gateway Timeout 表示服务器作为网关或者代理,未能及时从上游服务器接收响应

说一下 http 演变

HTTP1: 问题:无法复用 TCP 连接;连接的建立和销毁都会占用服务器和客户端的资源,造成内存资源的浪费

HTTP1.1: 改进:

  • 长连接 connect: keep-alive, 多次请求响应可以共享同一个 TCP 连接,这不仅减少了 TCP 的握手和挥手时间,同时可以充分利用 TCP「慢启动」的特点,有效的利用带宽。 如何关闭连接:
    • 客户端在某一次请求中设置了 Connection:close
    • 在没有请求时,客户端会不断对服务器进行心跳检测(一般每隔 1 秒)。一旦心跳检测停止,服务器立即关闭 TCP
    • 当客户端长时间没有新的请求到达服务器,服务器会主动关闭 TCP。运维人员可以设置该时间
  • 管道传输:(不常用),只要第一个请求发出去了,不必等其回来,就可发第二个请求。
  • 缓存处理:新增响应头 cache-control,用于实现客户端缓存。
  • 断点传输:在上传/下载资源时,如果资源过大,将其分割为多个部分,分别上传/下载,如果遇到网络故障,可以从已经上传/下载好的地方继续请求,不用从头开始,提高效率

问题:

  • 队头阻塞并没有解决(发生在服务器), 由于多个请求使用的是同一个 TCP 连接,服务器必须按照请求到达的顺序进行响应
  • header 很大,浪费
  • 请求只能从客户端开始,服务器只能被动响应

所以 HTTP1.1 在优化手段上,我们一般建议

  • 减少文件数量,从而减少队头阻塞的几率;
  • 通过开辟多个 TCP 连接,实现真正的、有缺陷的并行传输,浏览器会根据情况,为打开的页面自动开启 TCP 连接,对于同一个域名的连接最多 6 个,如果要突破这个限制,就需要把资源放到不同的域中

HTTP2.0: 基于 https 上;

改进:

  • 二进制分帧:每个传输单元称之为帧,而每一个请求或响应的完整数据称之为流,每个流有自己的编号,每个帧会记录所属的流。
    • 多路复用:基于二进制分帧,在同一域名下所有访问都是从同一个 tcp 连接中走,并且不再有队头阻塞问题,也无须遵守响应顺序
  • 头部压缩:
    • 对 header 编号,存入静态表,传输编号即可
    • 静态表里没有的,还是直接发送,但会添加到动态表中
    • 两张表都没有的,会进行 Huffman 编码压缩后再传输,同时添加到动态表中
  • 服务端主动推送

为什么 HTTP1.1 不能实现多路复用(腾讯)

HTTP/1.1 的传输单元是整个响应文本,因此接收方必须按序接收完所有的内容后才能接收下一个传输单元,否则就会造成混乱。而 HTTP2.0 的传输单元更小,是一个二进制帧,而且每个帧有针对所属流的编号,这样即便是不同的流交替传输,也可以很容易区分出每个帧是属于哪个流的。

简单讲解一下 http2 的多路复用(网易)

在 HTTP/2 中,有两个非常重要的概念,分别是帧(frame)和流(stream)。 帧代表着最小的数据单位,每个帧会标识出该帧属于哪个流,流也就是多个帧组成的数据流。 多路复用,就是在一个 TCP 连接中可以存在多条流。换句话说,也就是可以发送多个请求,对端可以通过帧中的标识知道属于哪个请求。通过这个技术,可以避免 HTTP 旧版本中的队头阻塞问题,极大的提高传输性能。

http1.1 是如何复用 tcp 连接的?(网易)

客户端请求服务器时,通过请求行告诉服务器使用的协议是 http1.1,同时在请求头中附带 connection:keep-alive(为保持兼容),告诉服务器这是一个长连接,后续请求可以重复使用这一次的 TCP 连接。

这样做的好处是减少了三次握手和四次挥手的次数,一定程度上提升了网络利用率。但由于 http1.1 不支持多路复用,响应顺序必须按照请求顺序抵达客户端,不能真正实现并行传输,因此在 http2.0 出现之前,实际项目中往往把静态资源,比如图片,分发到不同域名下的资源服务器,以便实现真正的并行传输。

https

HTTPS 无非就是 HTTP + SSL/TLS,而 SSL/TLS 本质上在解决如何协商出安全的对称加密密钥这一问题,以利用此密钥进行后续的通讯;

如何协商出安全可信任的对称加密密钥🔑? 答案是 server 和 client 之间,使用非对称加密来传输对称加密密钥;

那么如何传输非对称加密的公钥给客户端呢?答案是用 Ca 证书

  • 浏览器告知加密方式
  • 服务器选择加密方式并返回 CA 证书
  • 浏览器收到证书后使用系统内置 CA 证书中的公钥来解密得到的摘要 B,hash CA 明文的其他信息得到 摘要 A,如果 摘要 A === 摘要 B,就认为校验通过,可以认为这个 CA 证书里服务器的公钥是安全的
  • 用这个公钥加密了一个对称加密的密钥出来给到服务器
  • 服务器用私钥解密,就得到了对称加密的密钥
  • 此后就用这一密钥进行数据传输

详情

HTTPS 如何防止重放攻击?

加随机数/时间戳等

介绍下 Https 的中间人攻击

针对 HTTPS 攻击主要有 SSL 劫持攻击和 SSL 剥离攻击两种。

SSL 劫持攻击是指攻击者劫持了客户端和服务器之间的连接,将服务器的合法证书替换为伪造的证书,从而获取客户端和服务器之间传递的信息。这种方式一般容易被用户发现,浏览器会明确的提示证书错误,但某些用户安全意识不强,可能会点击继续浏览,从而达到攻击目的。

SSL 剥离攻击是指攻击者劫持了客户端和服务器之间的连接,攻击者保持自己和服务器之间的 HTTPS 连接,但发送给客户端普通的 HTTP 连接,由于 HTTP 连接是明文传输的,即可获取客户端传输的所有明文数据。

Cookie 是一种存储在浏览器中的小文件,用户存储网站的一些信息。通过 Cookie,服务器可以识别用户并保持会话状态,实现会话保持。

解决问题: Cookie 诞生的主要目的是为了解决 HTTP 协议的无状态性问题。HTTP 协议是一种无状态的协议,即服务器无法识别不同的用户或跟踪用户的状态

OSI 七层模型 应表会 传 网 数 物

OSI 算是一个参考的理论模型,TCP/IP 四层是实施的模型;

从上到下分别为:应用层、传输层、网络层、数据链路层、物理层。在发送消息时,消息从上到下进行打包,每一层会在上一层基础上加包,而接受消息时,从下到上进行解包,最终得到原始信息。

  • 应用层:HTTP,FTP,DNS,这一层叫报文
  • 表示层:JEPG,ASCII,可以理解为翻译官,这一层叫报文
  • 会话层:SSL/TLS,这一层叫报文
  • 传输层:TCP,UDP,主要定义端口号,这一层叫数据段
  • 网络层:IP,ICMP,主要做寻址和路由,这一层叫数据包
  • 数据链路层:进行硬件地址的寻址,将比特组合成字节进而组合成帧,这一层叫数据帧
  • 物理层:电/光/无限波,这一层叫比特流

介绍下 SSE

Server Sent Event,基于 HTTP 的,服务器主动向客户端推送数据的技术。

客户端发起请求,服务端可以实时的向客户端发送消息;相比于 websocket ,SSE 算是个 单工通讯,客户端只能发一次,之后都由服务端推送,websocket 是双工的,两端都可以发消息。

后端设置接口的 content-type 为 text/event-stream,前端 new EventSource 开启一次 SSE,然后监听 message(默认的,后端可修改)事件即可拿到数据

chatGTP 网页就利用了 SSE 技术,打字效果;

GET vs POST

从 http 协议的角度来说,GET 和 POST 它们都只是请求行中的第一个单词,除了语义不同,其实没有本质的区别。

之所以在实际开发中会产生各种区别,主要是因为浏览器的默认行为造成的。

受浏览器的影响,在实际开发中,GET 和 POST 有以下区别:

  • 浏览器在发送 GET 请求时,不会附带请求体
  • GET 请求的传递信息量有限,适合传递少量数据;POST 请求的传递信息量是没有限制的,适合传输大量数据。
  • GET 请求只能传递 ASCII 数据,遇到非 ASCII 数据需要进行编码;POST 请求没有限制
  • 大部分 GET 请求传递的数据都附带在 path 参数中,能够通过分享地址完整的重现页面,但同时也暴露了数据,若有敏感数据传递,不应该使用 GET 请求,至少不应该放到 path 中
  • 刷新页面时,若当前的页面是通过 POST 请求得到的,则浏览器会提示用户是否重新提交。若是 GET 请求得到的页面则没有提示。
  • GET 请求的地址可以被保存为浏览器书签,POST 不可以

websocket 是什么

单个 tcp 连接上的全双工通信的协议,常用于聊天,多人协作啊, 后端可用 ws 库,创建一个 ws 服务,然后监听 Connection 事件,获得是否连接成功的信息,以及所对应的 socket ,给 socket 添加 message 监听事件,就可以收到来自客户端的消息推送,按需将消息用广播出去,可以通过 ws.client 得到已连接的所有客户端, 前端直接用 new WebSocket 实例化一个 ws 对象,然后添加事件监听,比如 message 事件等;

websocket 握手

首先,客户端若要发起 websocket 连接,首先必须向服务器发送 http 请求以完成握手,请求行中的 path 需要使用 ws: 开头的地址,请求头中要分别加入

  • upgrade、
  • connection、
  • Sec-WebSocket-Key、
  • Sec-WebSocket-Version 标记

然后,服务器收到请求后,发现这是一个 websocket 协议的握手请求,于是响应行中包含 Switching Protocols,同时响应头中包含

  • upgrade、
  • connection、
  • Sec-WebSocket-Accept 标记

当客户端收到响应后即可完成握手,随后使用建立的 TCP 连接直接发送和接收消息

心跳监测

由于 socket 长时间不使用,或者因为网络波动,弱网模式,是有可能断开的,心跳检测可用来进行保活。

一般是由客户端每隔大概 5s 向服务器发起 ping 包,服务器回应 pong,如果在一段时间内未收到服务端的 pong,则断开连接或者尝试重连,服务端也是同理,如果长时间未收到客户端的 ping,可用主动关闭连接

除了 ajax,fetch,sse,websocket jsonp 之外,sendBeacon 也可以发送网络请求;使用的是 h5 新增的 ping 请求

  • 心跳检测
  • 埋点
  • 发送用户反馈

优点:不受页面卸载的影响

缺点:受限于广告屏蔽插件,支持的数据类型有限,只有 text,blob,formdata 等,只能发送 post 请求,64kb 上限的数据

TCP vs UDP

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

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

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

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

TCP 报文首部有 20 个字节,额外开销大;

区别如下:

  • TCP 是面向连接的协议,建立连接 3 次握手、断开连接四次挥手,UDP 是面向无连接,数据传输前后不连接连接,发送端只负责将数据发送到网络,接收端从消息队列读取

  • TCP 提供可靠的服务,传输过程采用流量控制、编号与确认、计时器等手段确保数据无差错,不丢失。UDP 则尽可能传递数据,但不保证传递交付给对方

  • TCP 面向字节流,将应用层报文看成一串无结构的字节流,分解为多个 TCP 报文段传输后,在目的站重新装配。UDP 协议面向报文,不拆分应用层报文,只保留报文边界,一次发送一个报文,接收方去除报文首部后,原封不动将报文交给上层应用

  • TCP 只能点对点全双工通信。UDP 支持一对一、一对多、多对一和多对多的交互通信

HTTP3.0 改用 QUIC(基于 UDP 的),QUIC 自己实现了一个层,它提供了数据包重传、拥塞控制、调整传输节奏(pacing)以及其他一些 TCP 中存在的特性。从而保证准确性与可靠性

介绍下 XSS (Cross-Site Scripting)

跨站脚本攻击

  • 反射型 比如电子邮件中,诱使用户去访问一个包含恶意代码的 URL,可能包含恶意代码,通常出现在网站的搜索栏、用户登录口等地方,常用来窃取客户端 Cookies 或进行钓鱼欺骗。
  • 存储型 常见于由社区驱动的内容网站,比如博客评论,留言板等
  • DOM 型 innerHtml,eval,v-html,location

如何预防:

  • 用 xss 漏洞扫描工具提前发现漏洞
  • 输入过滤
  • 输出转义
  • CSP(Content Security Policy)
    • default-src
    • script-src
    • style-src <meta http-equiv="Content-Security-Policy" content="default-src 'self' cdn.example.com; script-src 'self'; style-src 'self' fonts.google.com" />

介绍下 CSRF (Cross-Site Request Forgery)

跨站请求伪造:CSRF 是利用浏览器自动携带 Cookie 特性,攻击者诱导用户发起伪造请求,造成非法操作。 预防措施包括:

  • 使用 CSRF Token,验证请求中的 Token 是否匹配;
  • 设置 Cookie 的 SameSite 属性;
  • 验证 Referer 或 Origin 请求头;
  • 双重提交 Cookie 等方式,从而有效防止 CSRF 攻击。

介绍下 JWT (JSON Web Token)

由三部分组成:用 点 分割开

  • 头部:类型和签名算法,base64
  • payload:用户信息(不能包含敏感信息),base64
  • 签名:加密上述两个数据得到的,用于验证完整性和真实性,加密后再 base64

介绍下 Google reCAPTCHA

reCAPTCHA 会分析用户的行为(如鼠标移动、点击模式等)来判断是否为人类。如果行为可疑,会触发验证挑战(如选择图片、输入验证码等),reCAPTCHA v3 会为每个请求生成一个评分(0.0 到 1.0),表示用户行为的可信度

介绍下加密

密钥

密钥是一种参数,它是在明文转换为密文或将密文转换为明文的算法中输入的参数。密钥分为对称密钥与非对称密钥,分别应用在对称加密和非对称加密上。

对称加密:又叫做私钥加密,即信息的发送方和接收方使用同一个密钥去加密和解密数据。对称加密的特点是算法公开、加密和解密速度快,适合于对大数据量进行加密,常见的对称加密算法有 DES、3DES、TDEA、Blowfish、RC5 和 IDEA。

非对称加密:也叫做公钥加密。非对称加密与对称加密相比,其安全性更好。非对称加密使用一对密钥,即公钥和私钥,且二者成对出现。私钥被自己保存,不能对外泄露。公钥指的是公共的密钥,任何人都可以获得该密钥。用公钥或私钥中的任何一个进行加密,用另一个进行解密。

摘要:摘要算法又称哈希/散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用 16 进制的字符串表示)。算法不可逆。