「Web应用架构」WebSocket介绍和WebSocket API

简介: 「Web应用架构」WebSocket介绍和WebSocket API


WebSocket支持在客户端和服务器之间双向的、面向消息的文本和二进制数据流。它是浏览器中最接近原始网络套接字的API。除了WebSocket连接也不仅仅是一个网络套接字,因为浏览器在一个简单的API背后抽象了所有的复杂性,并提供了一些额外的服务:

  • 连接协商和同源策略的实施
  • 与现有HTTP基础设施的互操作性
  • 面向消息的通信和有效的消息框架
  • 子协议协商和可扩展性

WebSocket是浏览器中最通用、最灵活的传输方式之一。简单和最小的API使我们能够以流方式在客户机和服务器之间分层和交付任意的应用程序协议——从简单的JSON有效负载到定制的二进制消息格式的任何内容——其中任何一方都可以随时发送数据。

但是,定制协议的代价是它们是定制的。应用程序必须考虑到缺少状态管理、压缩、缓存和其他由浏览器提供的服务。设计约束和性能权衡总是存在的,利用WebSocket也不例外。简而言之,WebSocket不是HTTP、XHR或SSE的替代品,为了获得最佳性能,充分利用每种传输的优势至关重要。

WebSocket是由多个标准组成的:WebSocket API由W3C定义,WebSocket协议(RFC 6455)及其扩展由HyBi工作组(IETF)定义。

WebSocket API

浏览器提供的WebSocket API非常小和简单。同样,连接管理和消息处理的所有低级细节都由浏览器处理。为了启动一个新的连接,我们需要一个WebSocket资源的URL和一些应用程序回调:

var ws = new WebSocket('wss://example.com/socket');
ws.onerror = function (error) { ... }
ws.onclose = function () { ... }
ws.onopen = function () {
  ws.send("Connection established. Hello server!");
}
ws.onmessage = function(msg) {
  if(msg.data instanceof Blob) {
    processBlob(msg.data);
  } else {
    processText(msg.data);
  }
}


  1. 打开一个新的安全WebSocket连接(wss)
  2. 可选回调,在发生连接错误时调用
  3. 可选回调,在连接终止时调用
  4. 可选回调,在建立WebSocket连接时调用
  5. 客户端发起的消息到服务器
  6. 为来自服务器的每个新消息调用的回调函数
  7. 为接收到的消息调用二进制或文本处理逻辑

API不言自明。实际上,它应该与我们在前一章看到的EventSource API非常相似。这是有意为之,因为WebSocket提供了类似的扩展功能。话虽如此,两者之间也有一些重要的区别。让我们一个一个来看看。

效仿WebSocket

WebSocket协议已经经历了许多修订、实现回滚和安全调查。然而,好消息是RFC6455定义的最新版本(v13)现在被所有现代浏览器支持。唯一值得注意的遗漏是Android浏览器。有关最新状态,请参阅http://caniuse.com/websockets

类似于SSE的polyfill策略(用定制的JavaScript模拟EventSource), WebSocket浏览器API可以通过一个可选的JavaScript库来模拟。然而,模拟WebSockets的难点不是API,而是传输!因此,polyfill库及其回退传输(XHR轮询、EventSource、iframe轮询等)的选择将对模拟WebSocket会话的性能产生重大影响。

为了简化跨浏览器的部署,流行的库(如SockJS)在浏览器中提供了类似WebSocket的对象的实现,而且更进一步地提供了一个定制服务器来实现对WebSocket的支持和各种替代传输。定制服务器和客户机的结合能够实现“无缝回退”:性能下降,但应用程序API保持不变。

其他库,比如Socket。除了多传输回退功能外,IO还实现了其他特性,如心跳、超时、支持自动重新连接等。

当考虑一个polyfill 库或“实时框架”,Socket.IO,密切关注客户端和服务器的底层实现和配置:始终利用本地WebSocket接口获得最佳性能,并确保回退传输满足您的性能目标。

WS和WSS URL方案

WebSocket资源URL使用自己的定制方案:ws用于明文通信(例如,ws://example.com/socket), wss用于需要加密通道(TCP+TLS)。为什么使用自定义模式,而不是熟悉的http?

WebSocket协议的主要用例是在浏览器和服务器上运行的应用程序之间提供一个优化的双向通信通道。然而,WebSocket有线协议可以在浏览器之外使用,并且可以通过非http交换进行协商。因此,HyBi工作组选择采用自定义URL方案。

尽管自定义方案启用了非http协商选项,但在实践中,还没有用于建立WebSocket会话的替代握手机制的现有标准。

接收文本和二进制数据

WebSocket通信由消息和应用程序代码组成,不需要担心缓冲、解析和重构接收到的数据。例如,如果服务器发送了1 MB的有效负载,应用程序的onmessage回调将只在客户机上整个消息可用时被调用。

此外,WebSocket协议对应用程序的有效负载没有任何假设和限制:文本和二进制数据都是公平的。在内部,该协议只跟踪关于消息的两部分信息:作为可变长度字段的有效负载的长度和区分UTF-8和二进制传输的有效负载类型。

当浏览器接收到新消息时,它会自动转换为DOMString对象(用于基于文本的数据)或Blob对象(用于二进制数据),然后直接传递给应用程序。唯一的另一个选项,作为性能提示和优化客户端,是告诉浏览器转换接收到的二进制数据到ArrayBuffer,而不是Blob:

var ws = new WebSocket('wss://example.com/socket');
ws.binaryType = "arraybuffer";
ws.onmessage = function(msg) {
  if(msg.data instanceof ArrayBuffer) {
    processArrayBuffer(msg.data);
  } else {
    processText(msg.data);
  }
}


  • 当收到二进制消息时,强制进行ArrayBuffer转换

用户代理可以将此作为如何处理传入的二进制数据的提示:如果属性设置为“blob”,则将其缓冲到磁盘是安全的,如果属性设置为“arraybuffer”,则可能更有效地将数据保存在内存中。自然,用户代理被鼓励使用更微妙的启发式来决定是否将传入数据保存在内存中……

WebSocket API, W3C候选推荐

Blob对象表示不变的原始数据的类文件对象。如果您不需要修改数据,也不需要将其分割成更小的块,那么它就是最佳格式——例如。,您可以将整个Blob对象传递给图像标记(请参阅使用XHR下载数据中的示例)。另一方面,如果需要对二进制数据执行额外的处理,那么ArrayBuffer可能更适合。

用JavaScript解码二进制数据

ArrayBuffer是一种通用的、固定长度的二进制数据缓冲区。但是,ArrayBuffer可以用来创建一个或多个ArrayBufferView对象,每个对象可以以特定的格式显示缓冲区的内容。例如,假设我们有以下类似c的二进制数据结构:

struct someStruct {

 charusername[16];

 unsignedshortid;

 floatscores[32];

};

给定这种类型的ArrayBuffer对象,我们可以在同一个缓冲区中创建多个视图,每个视图都有自己的偏移量和数据类型:

var buffer = msg.data;


var usernameView = new Uint8Array(buffer, 0, 16);
var idView = new Uint16Array(buffer, 16, 1);
var scoresView = new Float32Array(buffer, 18, 32);


console.log("ID: " + idView[0] + " username: " + usernameView[0]);
for (var j = 0; j < 32; j++)
{
console.log(scoresView[j])
}

每个视图接受父缓冲区、起始字节偏移量和要处理的元素数量——偏移量是根据前面字段的大小计算的。因此,ArrayBuffer和WebSocket为我们的应用程序提供了所有必要的工具来在浏览器中流处理二进制数据。

发送文本和二进制数据

一旦WebSocket连接建立,客户端就可以随意发送和接收UTF-8和二进制消息。WebSocket提供了一个双向通信通道,它允许在相同的TCP连接上双向传递消息:

var ws = new WebSocket('wss://example.com/socket');
ws.onopen = function () {
  socket.send("Hello server!");
  socket.send(JSON.stringify({'msg': 'payload'}));
  var buffer = new ArrayBuffer(128);
  socket.send(buffer);
  var intview = new Uint32Array(buffer);
  socket.send(intview);
  var blob = new Blob([buffer]);
  socket.send(blob);
}


  1. 发送UTF-8编码的文本消息
  2. 发送一个UTF-8编码的JSON有效负载
  3. 将ArrayBuffer内容作为二进制有效负载发送
  4. 将ArrayBufferView内容作为二进制有效负载发送
  5. 将Blob内容作为二进制有效负载发送

WebSocket API接受一个DOMString对象,该对象在网络上被编码为UTF-8,或者一个用于二进制传输的ArrayBuffer、ArrayBufferView或Blob对象。但是,请注意,后面的二进制选项只是为了API的方便:在网络上,WebSocket框架通过单个位标记为二进制或文本。因此,如果应用程序或服务器需要关于负载的其他内容类型信息,那么它们必须使用额外的机制来通信该数据。

send()方法是异步的:提供的数据由客户机排队,然后函数立即返回。因此,特别是在传输大的有效负载时,不要将快速返回误认为数据已经发送的信号!要监视浏览器排队的数据量,应用程序可以查询套接字上的bufferedAmount属性:

var ws = new WebSocket('wss://example.com/socket');
ws.onopen = function () {
  subscribeToApplicationUpdates(function(evt) {
    if (ws.bufferedAmount == 0)
      ws.send(evt.data);
  });
};
  1. 订阅应用程序更新(例如,游戏状态更改)
  2. 检查客户机上缓冲的数据量
  3. 如果缓冲区为空,则发送下一个更新

前面的示例尝试将应用程序更新发送到服务器,但前提是前面的消息已从客户机的缓冲区中抽取。为什么要费心去做这样的检查呢?所有WebSocket消息都是按照客户端排队的确切顺序传递的。结果,队列消息的大量积压,甚至单个大消息,都将延迟在it后排队的消息的传递——线头阻塞!

为了解决这个问题,应用程序可以将大消息分割成更小的块,仔细监视bufferedAmount值以避免在行头阻塞,甚至为挂起的消息实现自己的优先级队列,而不是盲目地将它们全部排在套接字上。

许多应用程序生成多种类型的消息:高优先级更新,如控制流量;低优先级更新,如后台传输。为了优化传递,应用程序应该密切关注每种类型的消息如何以及何时在套接字上排队!

Subprotocol谈判

WebSocket协议对每个消息的格式不做任何假设:一个位跟踪消息是否包含文本或二进制数据,这样它可以被客户端和服务器有效地解码,但其他消息的内容是不透明的。

此外,与HTTP或XHR请求不同的是,它们通过每个请求和响应的HTTP头来通信额外的元数据,对于WebSocket消息没有这种等效的机制。因此,如果需要关于消息的额外元数据,那么客户端和服务器必须同意实现它们自己的子协议来通信这些数据:

  • 客户端和服务器可以预先就固定的消息格式达成一致。,所有通信都将通过json编码的消息或自定义二进制格式完成,必要的消息元数据将成为编码结构的一部分。
  • 如果客户机和服务器需要传输不同的数据类型,那么它们可以就一致的消息头达成一致,该消息头可用于通信指令来解码剩余的有效负载。
  • 可以使用文本和二进制消息的混合来传递有效负载和元数据信息。时,文本消息可以与等效的HTTP头通信,然后与应用程序有效负载通信二进制消息。

这个列表只是一个可能的策略的小样本。WebSocket消息的灵活性和低开销是以额外的应用逻辑为代价的。但是,消息序列化和元数据管理只是问题的一部分!一旦我们确定了消息的序列化格式,我们如何确保客户机和服务器相互理解,以及如何保持它们同步?

幸运的是,WebSocket提供了一个简单而方便的子协议协商API来解决第二个问题。作为初始连接握手的一部分,客户端可以向服务器发布它支持的协议:

var ws = new WebSocket('wss://example.com/socket',
                       ['appProtocol', 'appProtocol-v2']);
ws.onopen = function () {
  if (ws.protocol == 'appProtocol-v2') {
    ...
  } else {
    ...
  }
}


  1. 在WebSocket握手期间发布的子协议数组
  2. 检查服务器选择的子协议

如前面的示例所示,WebSocket构造函数接受子协议名称的可选数组,它允许客户端发布它理解或愿意为这个连接使用的协议列表。指定的列表被发送到服务器,服务器被允许选择一个由客户机发布的协议。

如果子协议协商成功,那么onopen回调在客户端上被触发,并且应用程序可以查询WebSocket对象上的协议属性来确定所选择的协议。另一方面,如果服务器不支持任何客户端发布的客户端协议,那么WebSocket握手是不完整的:onerror回调被调用,连接被终止。

子协议名称由应用程序定义,并在初始HTTP握手期间按指定的方式发送到服务器。除此之外,指定的子协议对核心WebSocket API没有影响。

相关文章
|
11月前
|
人工智能 自然语言处理 开发工具
统一多模态 Transformer 架构在跨模态表示学习中的应用与优化
本文介绍统一多模态 Transformer(UMT)在跨模态表示学习中的应用与优化,涵盖模型架构、实现细节与实验效果,探讨其在图文检索、图像生成等任务中的卓越性能。
统一多模态 Transformer 架构在跨模态表示学习中的应用与优化
|
10月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1433 3
|
12月前
|
存储 编解码 Serverless
Serverless架构下的OSS应用:函数计算FC自动处理图片/视频转码(演示水印添加+缩略图生成流水线)
本文介绍基于阿里云函数计算(FC)和对象存储(OSS)构建Serverless媒体处理流水线,解决传统方案资源利用率低、运维复杂、成本高等问题。通过事件驱动机制实现图片水印添加、多规格缩略图生成及视频转码优化,支持毫秒级弹性伸缩与精确计费,提升处理效率并降低成本,适用于高并发媒体处理场景。
1458 0
|
12月前
|
安全 测试技术 API
电商API接口开发:基础架构搭建全攻略
本文详细解析了电商API接口从零搭建基础架构的全流程。首先通过需求分析明确业务功能与接口规范,选定数据格式(如JSON)及通信方式(如RESTful)。接着在架构设计阶段选择合适的技术栈、数据库方案,并引入API网关实现统一管理。开发实现部分涵盖认证授权、数据访问、日志记录与异常处理等核心功能。安全防护则强调数据加密、传输安全及速率限制策略。测试优化阶段包括单元测试、集成测试、性能与安全测试,确保接口稳定性。最后通过工具生成清晰的API文档并实施版本控制,为开发者提供便利。整体流程系统化、模块化,助力打造高效、安全的电商API接口。
|
12月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
582 102
|
8月前
|
人工智能 JavaScript 前端开发
GenSX (不一样的AI应用框架)架构学习指南
GenSX 是一个基于 TypeScript 的函数式 AI 工作流框架,以“函数组合替代图编排”为核心理念。它通过纯函数组件、自动追踪与断点恢复等特性,让开发者用自然代码构建可追溯、易测试的 LLM 应用。支持多模型集成与插件化扩展,兼具灵活性与工程化优势。
683 6
|
8月前
|
存储 监控 安全
132_API部署:FastAPI与现代安全架构深度解析与LLM服务化最佳实践
在大语言模型(LLM)部署的最后一公里,API接口的设计与安全性直接决定了模型服务的可用性、稳定性与用户信任度。随着2025年LLM应用的爆炸式增长,如何构建高性能、高安全性的REST API成为开发者面临的核心挑战。FastAPI作为Python生态中最受青睐的Web框架之一,凭借其卓越的性能、强大的类型安全支持和完善的文档生成能力,已成为LLM服务化部署的首选方案。
1318 3
|
9月前
|
人工智能 Cloud Native 中间件
划重点|云栖大会「AI 原生应用架构论坛」看点梳理
本场论坛将系统性阐述 AI 原生应用架构的新范式、演进趋势与技术突破,并分享来自真实生产环境下的一线实践经验与思考。
|
9月前
|
机器学习/深度学习 人工智能 vr&ar
H4H:面向AR/VR应用的NPU-CIM异构系统混合卷积-Transformer架构搜索——论文阅读
H4H是一种面向AR/VR应用的混合卷积-Transformer架构,基于NPU-CIM异构系统,通过神经架构搜索实现高效模型设计。该架构结合卷积神经网络(CNN)的局部特征提取与视觉Transformer(ViT)的全局信息处理能力,提升模型性能与效率。通过两阶段增量训练策略,缓解混合模型训练中的梯度冲突问题,并利用异构计算资源优化推理延迟与能耗。实验表明,H4H在相同准确率下显著降低延迟和功耗,为AR/VR设备上的边缘AI推理提供了高效解决方案。
1488 0
|
8月前
|
机器学习/深度学习 自然语言处理 算法
48_动态架构模型:NAS在LLM中的应用
大型语言模型(LLM)在自然语言处理领域的突破性进展,很大程度上归功于其庞大的参数量和复杂的网络架构。然而,随着模型规模的不断增长,计算资源消耗、推理延迟和部署成本等问题日益凸显。如何在保持模型性能的同时,优化模型架构以提高效率,成为2025年大模型研究的核心方向之一。神经架构搜索(Neural Architecture Search, NAS)作为一种自动化的网络设计方法,正在为这一挑战提供创新性解决方案。本文将深入探讨NAS技术如何应用于LLM的架构优化,特别是在层数与维度调整方面的最新进展,并通过代码实现展示简单的NAS实验。
392 0