讲师中心 微信公众号

工具推荐

视频效率加速
AniShort
AniShort

AI短剧协同制作平台

Seko
Seko

AI短剧一键生成成片

Laper
Laper

剧本策划到成片协作

updream

一键生成高质量推广成片

JavaScript深入探索 websocket和HTTP/2与SSE +如何选择正确的路径!

冬婷大大_4931

冬婷大大_4931

发布时间:2020-12-21 17:33:45

|

3342人浏览过

|

来源于segmentfault

转载

这是专门探索 javascript 及其所构建的组件的系列文章的第5篇。

JavaScript深入探索 websocket和HTTP/2与SSE +如何选择正确的路径!

相关免费学习推荐:javascript(视频)

这一次,我们将深入到通信协议的领域,映射和探讨它们的属性,并在此过程中构建部分组件。快速比较WebSockets和 HTTP/2。最后,我们分享一些关于如何选择网络协议的方法。

简介

如今,功能丰富、动态 ui 的复杂 web 应用程序被认为是理所当然。这并不奇怪——互联网自诞生以来已经走过了漫长的道路。

立即学习“Java免费学习笔记(深入)”;

最初,互联网并不是为了支持这种动态和复杂的 web 应用程序而构建的。它被认为是HTML页面的集合,相互链接形成一个包含信息的 “web” 概念。一切都是围绕 HTTP 的所谓 请求/响应 范式构建的。客户端加载一个页面,然后在用户单击并导航到下一个页面之前什么都不会发生。

大约在2005年,AJAX被引入,很多人开始探索在客户端和服务器之间建立双向连接的可能性。尽管如此,所有HTTP 通信都由客户端引导,客户端需要用户交互或定期轮询以从服务器加载新数据。

让 HTTP 变成“双向”交互

让服务器能够“主动”向客户机发送数据的技术已经出现了相当长的时间。例如“Push”和“Comet”。

最常见的一种黑客攻击方法是让服务器产生一种需要向客户端发送数据的错觉,这称为长轮询。通过长轮询,客户端打开与服务器的 HTTP 连接,使其保持打开状态,直到发送响应为止。 每当服务器有新数据时需要发送时,就会作为响应发送。

看看一个非常简单的长轮询代码片段是什么样的:

(function poll(){
   setTimeout(function(){
      $.ajax({ 
        url: 'https://api.example.com/endpoint', 
        success: function(data) {
          // Do something with `data`
          // ...

          //Setup the next poll recursively
          poll();
        }, 
        dataType: 'json'
      });
  }, 10000);
})();

这基本上是一个自执行函数,第一次立即运行时,它设置了 10 秒间隔,在对服务器的每个异步Ajax调用之后,回调将再次调用Ajax。

其他技术涉及 Flash 或 XHR multipart request 和所谓的 htmlfiles 。

但是,所有这些工作区都有一个相同的问题:它们都带有 HTTP 的开销,这使得它们不适合于低延迟应用程序。想想浏览器中的多人第一人称射击游戏或任何其他带有实时组件的在线游戏。

WebSockets 的引入

WebSocket 规范定义了在 web 浏览器和服务器之间建立“套接字”连接的 API。简单地说:客户机和服务器之间存在长久连接,双方可以随时开始发送数据。

b320d1723a959eaa87eb5254b3e0008.png

客户端通过 WebSocket 握手 过程建立 WebSocket 连接。这个过程从客户机向服务器发送一个常规 HTTP 请求开始,这个请求中包含一个升级头,它通知服务器客户机希望建立一个 WebSocket 连接。

客户端建立 WebSocket 连接方式如下:

// Create a new WebSocket with an encrypted connection.
var socket = new WebSocket('ws://websocket.example.com')
WebSocket url使用 ws 方案。还有 wss 用于安全的 WebSocket 连接,相当于HTTPS。

这个方案只是打开 websocket.example.com 的 WebSocket 连接的开始。

下面是初始请求头的一个简化示例:

ec8b492ac44cd608e4bfe3f908c3a8e.png

如果服务器支持 WebSocke t协议,它将同意升级,并通过响应中的升级头进行通信。

Node.js 的实现方式:

7df8f7d383c17a1b9a6154d8a11cfc1.png

建立连接后,服务器通过升级头部中内容时行响应:

5708a76c890ad6fec01f21fc38d9ada.png

一旦建立连接,open 事件将在客户端 WebSocket 实例上被触发:

var socket = new WebSocket('ws://websocket.example.com');

// Show a connected message when the WebSocket is opened.
socket.onopen = function(event) {
  console.log('WebSocket is connected.');
};

现在握手已经完成,初始 HTTP 连接被使用相同底层 TCP/IP 连接的 WebSocket 连接替换。此时,双方都可以开始发送数据。

使用 WebSockets,可以传输任意数量的数据,而不会产生与传统 HTTP 请求相关的开销。数据作为消息通过 WebSocket 传输,每个消息由一个或多个帧组成,其中包含正在发送的数据(有效负载)。为了确保消息在到达客户端时能够正确地进行重构,每一帧都以负载的4-12字节数据为前缀, 使用这种基于帧的消息传递系统有助于减少传输的非有效负载数据量,从而大大的减少延迟。

注意:值得注意的是,只有在接收到所有帧并重构了原始消息负载之后,客户机才会收到关于新消息的通知。

WebSocket URLs

之前简要提到过 WebSockets 引入了一个新的URL方案。实际上,他们引入了两个新的方案:ws:// 和wss://。

url 具有特定方案的语法。WebSocket url 的特殊之处在于它们不支持锚点(#sample_anchor)。

同样的规则适用于 WebSocket 风格的url和 HTTP 风格的 url。ws 是未加密的,默认端口为80,而 wss 需要TLS加密,默认端口为 443。

帧协议

更深入地了解帧协议,这是 RFC 为我们提供的:

在RFC 指定的 WebSocket 版本中,每个包前面只有一个报头。然而,这是一个相当复杂的报头。以下是它的构建模块:

adc4b556635b7985aa89aa11dd9d048.png

HISUI 前端框架开发指南(IRIS版)
HISUI 前端框架开发指南(IRIS版)

HISUI 前端框架基础开发指南。HISUI 是基于 easyui 二次开发的 jQuery UI 组件库,专为医疗信息系统设计。当涉及 HISUI 组件开发、jQuery easyui 表格/表单/布局/窗口、医疗系统前端页面编写时使用此 skill。 涵盖 40 个组件的官方文档参考,支持声明式与编程式两种初始化方式,提供 $cm/$m 数据访问方法。

下载
  • FIN :1bit ,表示是消息的最后一帧,如果消息只有一帧那么第一帧也就是最后一帧,Firefox 在 32K 之后创建了第二个帧。

  • RSV1,RSV2,RSV3:每个1bit,必须是0,除非扩展定义为非零。如果接受到的是非零值但是扩展没有定义,则需要关闭连接。

  • Opcode:4bit,解释 Payload 数据,规定有以下不同的状态,如果是未知的,接收方必须马上关闭连接。状态如下:

    • 0x00: 附加数据帧
    • 0x01:文本数据帧  
    • 0x02:二进制数据帧    
    • 0x3-7:保留为之后非控制帧使用
    • 0x8:关闭连接帧
    • 0x9:ping
    • 0xA:pong
    • 0xB-F(保留为后面的控制帧使用)      
  • Mask:1bit,掩码,定义payload数据是否进行了掩码处理,如果是1表示进行了掩码处理。

  • Masking-key:域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。

  • Payload_len:7位,7 + 16位,7+64位,payload数据的长度,如果是0-125,就是真实的payload长度,如果是126,那么接着后面的2个字节对应的16位无符号整数就是payload数据长度;如果是127,那么接着后面的8个字节对应的64位无符号整数就是payload数据的长度。

  • Masking-key:0到4字节,如果MASK位设为1则有4个字节的掩码解密密钥,否则就没有。

  • Payload data:任意长度数据。包含有扩展定义数据和应用数据,如果没有定义扩展则没有此项,仅含有应用数据。

为什么 WebSocket 是基于帧而不是基于流?我不知道,就像你一样,我很想了解更多,所以如果你有想法,请随时在下面的回复中添加评论和资源。另外,关于这个主题的讨论可以在 HackerNews 上找到。

帧数据

如上所述,数据可以被分割成多个帧。 传输数据的第一帧有一个操作码,表示正在传输什么类型的数据。 这是必要的,因为 JavaScript 在开始规范时几乎不存在对二进制数据的支持。 0x01 表示 utf-8 编码的文本数据,0x02 是二进制数据。大多数人会发送 JSON ,在这种情况下,你可能要选择文本操作码。 当你发送二进制数据时,它将在浏览器特定的 Blob 中表示。

通过 WebSocket 发送数据的API非常简单:

var socket = new WebSocket('ws://websocket.example.com');
socket.onopen = function(event) {
  socket.send('Some message'); // Sends data to server.
};

当 WebSocket 接收数据时(在客户端),会触发一个消息事件。此事件包括一个名为data的属性,可用于访问消息的内容。

// Handle messages sent by the server.
socket.onmessage = function(event) {
  var message = event.data;
  console.log(message);
};

在Chrome开发工具:可以很容易地观察 WebSocket 连接中每个帧中的数据:

d81e2d9a7030e93d726d66a476848b3.png

消息分片

有效载荷数据可以分成多个单独的帧。接收端应该对它们进行缓冲,直到设置好 fin 位。因此,可以将字符串“Hello World”发送到11个包中,每个包的长度为6(报头长度)+ 1字节。控件包不允许分片。但是,规范希望能够处理交错的控制帧。这是TCP包以任意顺序到达的情况。

连接帧的逻辑大致如下:

  • 接收第一帧
  • 记住操作码
  • 将帧有效负载连接在一起,直到 fin 位被设置
  • 断言每个包的操作码是零

分片目的是发送长度未知的消息。如果不分片发送,即一帧,就需要缓存整个消息,计算其长度,构建frame并发送;使用分片的话,可使用一个大小合适的buffer,用消息内容填充buffer,填满即发送出去。

什么是跳动检测?

主要目的是保障客户端 websocket 与服务端连接状态,该程序有心跳检测及自动重连机制,当网络断开或者后端服务问题造成客户端websocket断开,程序会自动尝试重新连接直到再次连接成功。

在使用原生websocket的时候,如果设备网络断开,不会触发任何函数,前端程序无法得知当前连接已经断开。这个时候如果调用 websocket.send 方法,浏览器就会发现消息发不出去,便会立刻或者一定短时间后(不同浏览器或者浏览器版本可能表现不同)触发 onclose 函数。

后端 websocket 服务也可能出现异常,连接断开后前端也并没有收到通知,因此需要前端定时发送心跳消息 ping,后端收到 ping 类型的消息,立马返回 pong 消息,告知前端连接正常。如果一定时间没收到pong消息,就说明连接不正常,前端便会执行重连。

为了解决以上两个问题,以前端作为主动方,定时发送 ping 消息,用于检测网络和前后端连接问题。一旦发现异常,前端持续执行重连逻辑,直到重连成功。

错误处理

以通过监听 error 事件来处理所有错误:

var socket = new WebSocket('ws://websocket.example.com');

// Handle any error that occurs.
socket.onerror = function(error) {
  console.log('WebSocket Error: ' + error);
};

关闭连接

要关闭连接,客户机或服务器都应该发送包含操作码0x8的数据的控制帧。当接收到这样一个帧时,另一个对等点发送一个关闭帧作为响应,然后第一个对等点关闭连接,关闭连接后接收到的任何其他数据都将被丢弃:

// Close if the connection is open.
if (socket.readyState === WebSocket.OPEN) {
    socket.close();
}

另外,为了在完成关闭之后执行其他清理,可以将事件侦听器附加到关闭事件:

// Do necessary clean up.
socket.onclose = function(event) {
  console.log('Disconnected from WebSocket.');
};

服务器必须监听关闭事件以便在需要时处理它:

connection.on('close', function(reasonCode, description) {
    // The connection is getting closed.
});

WebSockets和HTTP/2 比较

虽然HTTP/2提供了很多功能,但它并没有完全满足对现有推送/流技术的需求。

关于 HTTP/2 的第一个重要的事情是它并不能替代所有的 HTTP 。verb、状态码和大部分头信息将保持与目前版本一致。HTTP/2 是意在提升数据在线路上传输的效率。

比较HTTP/2和WebSocket,可以看到很多相似之处:

正如我们在上面看到的,HTTP/2引入了 Server Push,它使服务器能够主动地将资源发送到客户机缓存。但是,它不允许将数据下推到客户机应用程序本身,服务器推送只由浏览器处理,不会在应用程序代码中弹出,这意味着应用程序没有API来获取这些事件的通知。

这就是服务器发送事件(SSE)变得非常有用的地方。SSE 是一种机制,它允许服务器在建立客户机-服务器连接之后异步地将数据推送到客户机。然后,只要有新的“数据块”可用,服务器就可以决定发送数据。它可以看作是单向发布-订阅模式。它还提供了一个名为 EventSource API 的标准JavaScript,作为W3C HTML5标准的一部分,在大多数现代浏览器中实现。不支持 EventSource API 的浏览器可以轻松地使用 polyfilled 方案来解决。

由于 SSE 基于 HTTP ,因此它与 HTTP/2 非常合适,可以结合使用以实现最佳效果:HTTP/2 处理基于多路复用流的高效传输层,SSE 将 API 提供给应用以启用数据推送。

为了理解 Streams  和 Multiplexing 是什么,首先看一下`IETF定义:“stream”是在HTTP/2 连接中客户机和服务器之间交换的独立的、双向的帧序列。它的一个主要特征是,一个HTTP/2 连接可以包含多个并发打开的流,任何一个端点都可以从多个流中交错帧。

1c9e7d1fc0ead428f859c3a8a7e501d.png

SSE 是基于 HTTP 的,这说明在 HTTP/2 中,不仅可以将多个 SSE 流交织到单个 TCP 连接上,而且还可以通过多个 SSE 流(服务器到客户端的推送)和多个客户端请求(客户端到服务器)。因为有 HTTP/2 和 SSE 的存在,现在有一个纯粹的 HTTP 双向连接和一个简单的 API 就可以让应用程序代码注册到服务器推送服务上。在比较 SSE 和 WebSocket 时,缺乏双向能力往往被认为是一个主要的缺陷。有了 HTTP/2,不再有这种情况。这样就可以跳过 WebSocket ,而坚持使用基于 HTTP 的信号机制。

如何选择WebSocket和HTTP/2?

WebSockets 会在 HTTP/2 + SSE 的领域中生存下来,主要是因为它是一种已经被很好地应用的技术,并且在非常具体的使用情况下,它比 HTTP/2 更具优势,因为它已经被构建用于具有较少开销(如报头)的双向功能。

假设建立一个大型多人在线游戏,需要来自连接两端的大量消息。在这种情况下,WebSockets 的性能会好很多。

一般情况下,只要需要客户端和服务器之间的真正低延迟,接近实时的连接,就使用 WebSocket ,这可能需要重新考虑如何构建服务器端应用程序,以及将焦点转移到队列事件等技术上。

使用的方案需要显示实时的市场消息,市场数据,聊天应用程序等,依靠 HTTP/2 + SSE 将为你提供高效的双向通信渠道,同时获得留在 HTTP 领域的各种好处:

  • 当考虑到与现有 Web 基础设施的兼容性时,WebSocket 通常会变成一个痛苦的源头,因为它将 HTTP 连接升级到完全不同于 HTTP 的协议。
  • 规模和安全性:Web 组件(防火墙,入侵检测,负载均衡)是以 HTTP 为基础构建,维护和配置的,这是大型/关键应用程序在弹性,安全性和可伸缩性方面更偏向的环境。

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

相关专题

更多
Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

0

2026.11.12

Go-Zero零基础入门教程
Go-Zero零基础入门教程

本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。

0

2026.11.12

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

120

2026.11.06

Postman请求头Headers怎么设置
Postman请求头Headers怎么设置

围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。

60

2026.11.06

Postman接口请求发送教程
Postman接口请求发送教程

详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。

80

2026.11.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

80

2026.11.05

DBeaver安全防护教程大全
DBeaver安全防护教程大全

DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。

60

2026.11.05

DBeaver连接创建与配置教程
DBeaver连接创建与配置教程

本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。

60

2026.11.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn