Websocket 研究 / Nodejs 模块选型对比

导语 对Websocket的基础原理研究,并在nodejs的WebSocket库中进行选型对比,选出最适合我们的库。本文分为两章,第一张对WebSocket基础原理进行研究,第二章将从Nodejs库中选出最适合的WebSocket库。

第一章:Websocket研究

WebSocket连接本质上是TCP连接,在网页打开后通过http协议握手之后建立长连接。真正实现了Web的实时通信,使B/S模式具备了C/S模式的实时通信能力

WebSocket的生命周期

分为三个阶段:

第一阶段:由客户端发起的握手阶段,握手后建立连接 第二阶段:数据交换,客户端与服务端可以互相主动发送消息 第三阶段:关闭连接,可以由任意一端发起关闭的命令

WebSocket的握手协议

握手请求

GET http://localhost:8181/ HTTP/1.1
Host: localhost:8181
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://tomcltang.kf0309.3g.qq.com
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6
Sec-WebSocket-Key: VCPIDS4ggndDGQmpLfzMLA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
  • HTTP request method 必须是GET,协议应不小于1.1
  • Upgrade,并且其值为 websocket;
  • Connection,并且其值为Upgrade;
  • Sec-WebSocket-Key,其值采用base64编码的随机16字节长的字符序列;
  • Origin,服务器可以从Origin决定是否接受该WebSocket连接;
  • Sec-webSocket-Version,当前值必须是13;握手响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: UFCeZ5AiDPquwYZOEHuNHBnbZ94=
Sec-WebSocket-Extensions: permessage-deflate
  • 首行返回的是HTTP/1.1协议版本和状态码101,表示变换协议(Switching Protocol)
  • Upgrade,其值为 websocket;
  • Connection,其值为Upgrade;
  • Sec-WebSocket-Accept,加密处理后的握手Key消息体组成

WebSocket的消息并非没有额外信息,除了业务数据以外,消息体也包含一些额外信息。只不过相对http的头会小很多,一般只有6个bytes

FIN:1 bit 指示这个是消息的最后片段。第一个片段可能也是最后的片段。

RSV1, RSV2, RSV3: 每个1 bit 必须是0,除非一个扩展协商为非零值定义含义。如果收到一个非零值且没有协商的扩展定义这个非零值的含义,接收端点必须失败WebSokcket连接。

Opcode: 4 bits 定义了“负载数据”的解释。如果收到一个未知的操作码,接收端点必须失败WebSocket连接。定义了以下值。 %x0 代表一个继续帧 %x1 代表一个文本帧 %x2 代表一个二进制帧 %x3-7 保留用于未来的非控制帧 %x8 代表连接关闭 %x9 代表ping %xA 代表pong %xB-F 保留用于未来的控制帧

Mask: 1 bit 定义是否“负载数据”是掩码的。如果设置为1,一个掩码键出现在masking-key,且这个是用于根据5.3节解掩码(unmask)“负载数据”。从客户端发送到服务器的所有帧有这个位设置为1。

Payload length: 7 bits, 7+16 bits, 或者 7+64 bits “负载数据”的长度,以字节为单位:如果0-125,这是负载长度。如果126,之后的两字节解释为一个16位的无符号整数是负载长度。如果127,之后的8字节解释为一个64位的无符号整数(最高有效位必须是0)是负载长度。多字节长度数量以网络字节顺序来表示。注意,在所有情况下,最小数量的字节必须用于编码长度,例如,一个124字节长的字符串的长度不能被编码为序列126,0,124。负载长度是“扩展数据”长度+“应用数据”长度。“扩展数据”长度可能是零,在这种情况下,负载长度是“应用数据”长度。

Masking-key: 0 or 4 bytes 客户端发送到服务器的所有帧通过一个包含在帧中的32位值来掩码。如果mask位设置为1,则该字段存在,如果mask位设置为0,则该字段缺失。详细信息请参见5.3节 客户端到服务器掩码。

Payload data: (x+y) bytes “负载数据”定义为“扩展数据”连接“应用数据”。

Extension data: x bytes “扩展数据”是0字节除非已经协商了一个扩展。任何扩展必须指定“扩展数据”的长度,或长度是如何计算的,以及扩展如何使用必须在打开阶段握手期间协商。 如果存在,“扩展数据”包含在总负载长度中。

Application data: y bytes 任意的“应用数据”,占用“扩展数据”之后帧的剩余部分。“应用数据”的长度等于负载长度减去“扩展数据”长度。

FIN + RSV1 + RSV2 + RSV3 + Opcode + Mask + Payload length + Masking-key = 业务数据以外的消息大小 1bit + 1bit + 1bit + 1bit + 4bit + 1bit + 7bit + 4bytes = 6bytes

与http对比

以发送JSON字符串 {“req”:”123”} 为例,字符串本身13 bytes 通过http发送的话,http消息总大小 523+13 通过WebSocket发送的话,消息总大小是 6+13

第二章:Nodejs 的Websocket模块选型

由于工作原因,主要用Nodejs进行开发,因此只对比Nodejs实现的WebSocket库 GitHub上面,用nodejs实现的WebSocket库非常多,我挑选了几个靠前的库进行对比

本地Windows环境,对比Ajax与WebSocket发送消息的耗时。可以看到WebSocket的耗时远远低于Ajax

本地Windows环境 不同消息大小的耗时对比库对比

本地Windows环境,处理不同消息大小的耗时对比。 测试结果: websocket-node < faye < ws < socket.io

因为本地Windows环境与生产环境并不一样,因此上面的数据仅作Windows环境参考。因为下面在生产环境进行对比后,数据会有较大差异

以下生产环境测试,都是在2G内存、10个ecu环境下进行的测试对比

生产linux环境 不同消息大小的耗时对比库对比

这个测试与上一个Windows测试是一样的,但结果完全不同。ws表现最好 测试结果:ws< socket.io < websocket-node < faye < ajax

生产linux环境 测试内存波动

使用同样大小的消息,对服务发起大量的请求。测试服务的内存消耗。socket.io/ws/websocket-node 表现都不错,比较稳定。faye表现最差,占用内存高。 测试结果:socket.io < ws < websocket-node < faye

生产linux环境 测试CPU波动

使用同样大小的消息,对服务发起大量的请求。测试服务的CPU占用情况。socket.io表现最差,CPU占比很高。 测试结果:websocket-node = faye < ws < socket.io

生产linux环境 测试最大连接数

在2G内存的服务器上,测试各个库的最大连接数。最好的结果也是差异巨大。最好的ws是最差的socket.io的近三倍 测试结果:ws > websocket-node > faye > socket.io

websocket-node 在连接数超过140000的时候,连接速度比较慢。服务器没响应,但之前的连接不会断开 而faye和ws在到极限的时候,会出现异常。所有连接会断开 socket.io 连接在20000左右 的时候,就非常慢了

生产linux环境 测试最大连接数时的内存与CPU波动

测试最大连接数的时候,同时监控了内存和CPU的波动。

内存

在内存方面,ws的增长最为平缓,而socket.io早早的攀升到了极限最后挂掉了 测试结果:ws < websocket-node < faye < socket.io

CPU

在CPU方面,ws同样保持稳定,占用比也非常低。 测试结果:ws < websocket-node < faye < socket.io

总结

按第一得分4,第二得3分,第三得2分,第四得1分计算各个库的得分情况

得分

ws

21

websocket-node

17

faye

11

socket.io

11

ws表现最好简单易用,连接数最大,内存和CPU控制的稳定。缺点是在到达最大连接数极限之后,会断开所有连接

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

唐昌林的专栏

1 篇文章1 人订阅

我来说两句

1 条评论
登录 后参与评论

相关文章

来自专栏算法修养

服务器推技术研究Comet

服务器推技术 最近参与的一个关于股票的项目,有这样一个需求。服务器需要主动推送给客户端消息。这和传统的Web模式不同。传统的Web系统,客户端和服务器的交互是这...

3058
来自专栏崔庆才的专栏

腾讯云上Winpcap网络编程一之前言、目标

由于腾讯云上提供了Windows系统,所以我们这次Winpcap编程选用腾讯云主机实验,让大家简要了解Winpcap的编程目标与编程流程。

2050
来自专栏PHP技术

WebSocket 通信过程与实现

WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。

893
来自专栏流柯技术学院

nmon指标

nmon [ -s < seconds > ] [ -c < count > ] [ -b ] [ -B ] [ -g < filename > ] [ -k ...

582
来自专栏13blog.site

Spring+SpringMVC+MyBatis+easyUI整合进阶篇(十四)Redis缓存正确的使用姿势

作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载。 简介 这是一篇关于...

2755
来自专栏芋道源码1024

注册中心 Eureka 源码解析 —— Eureka-Server 启动(一)之 ServerConfig

本文主要基于 Eureka 1.8.X 版本 1. 概述 2. EurekaServerConfig 2.1 类关系图 2.2 配置属性 2.3 Default...

3395
来自专栏微服务生态

缓存穿透、缓存并发、热点缓存之最佳招式

在之前的一篇缓存穿透、缓存并发、缓存失效之思路变迁文章中介绍了关于缓存穿透、并发的一些常用思路,但是个人感觉文章中没有明确一些思路的使用场景,本文继续将继续深化...

652
来自专栏前端架构

wireshark使用教程及过滤语法总结——血泪史的汇聚

wireshark的官方下载网站: http://www.wireshark.org/

831
来自专栏xingoo, 一个梦想做发明家的程序员

ADX3000二层的负载均衡设计问题

我的想法是 想在现有的局域网内部,利用ADX划分出一个新的局域网,模拟负载均衡。 现在有三台试验机器,拓扑图如下: ? 各个机器IP设置如下图: 我进行了如下的...

1885
来自专栏程序员宝库

一文读懂 WebSocket 通信过程与实现

WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是一种基于 TCP 的一种独立实现。

1066

扫码关注云+社区