导航:首页 > 编程语言 > nodejswebsocket性能

nodejswebsocket性能

发布时间:2023-03-23 21:34:44

A. 如何对基于node.js 的websocket进行并发访问的性能测试

java、c#、php都有专门的测试工具去测试并发、负载均衡、平肆芹皮首兆滑性、代码裂差覆盖率等的,node这方面还在研究中,空间很大
自己写个模拟客户端测试呗,nodejs也有websocket client的实现
不过性能的话不要报太大希望

B. websocket和nodejs有什么联系

Node.js 是一个 JavaScript 运行环境。
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-plex)通信——允许服务器主动发送信息给客户端。
Node.js可以作为WebSocket的服务端,但WebSocket也可以使用其它的服务端。

C. nodejs-websocket介绍

websocket 是一种网络通信协议,一般用来进行实时通信会使用到

websocket 协议和 http 协议类似,http 协议有一个缺陷,只能由客户方端发起请求,服务端根据请求 url 和传过去的参数返回对应结果

websocket 是双向通信的,只要 websocket 连接建立起来,可以由客户端给服务端发送数据,也可以由服务端主动给客户端发送数据

websocket 适用场景:聊天室

websocket 相关简介,可以看 阮老师的文章

nodejs 可以通过 nodejs-websocket 来实现创建一个 websocket 的服务

nodejs-websocket 用法

文档地址: https://www.npmjs.com/package/nodejs-websocket

node 创建的 websocket 服务,主要包含三个概念

可以通过 server.on('event', (res) => {console.log(res)}) 调用

这次使用 websocket 实现一个基本的聊天室功能,个人感觉还比较简单,只是中间会出现一些由于链接异常断开,导致后端服务抛出异常挂掉的情况
记住前端关闭页面或者刷新页面时,先把连接关掉,每次进入页面时创建连接,然后后端将由于异常关闭导致的出错 try/catch 一下,避免抛出异常,阻塞进程

websocket 对于实现聊天室这样的功能,真的很方便,其实还能扩展到多人合作或者网络游戏等功能

D. nodejs+webSocket

案喊裂例目录:

1.点对点.js

2.瀑郑码闭布模模知式.js

index.html

E. nodejs 承载 多少 websocket

首先是协议的升级,这个比较简单,就简述一下:当在客户端执行new
Websocket("ws://XXX.com/")的时候,客户端就会发起请求报文进行握手申请,报文中有个很重要的key就是Sec-
WebSocket-Key,服务端获取到key,然后将这个key与字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11
相连,对新的字符串通过sha1安全散列算法计算出结果后,再进行base64编码,并且将结果放在请求头的"Sec-WebSocket-
Accept"中返回即可完成握手。具体请看代码:
server.on('upgrade', function (req, socket, upgradeHead) {
var key = req.headers['sec-websocket-key'];
key = crypto.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
var headers = [
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: ' + key
];
socket.setNoDelay(true);
socket.write(headers.join("\r\n") + "\r\n\r\n", 'ascii');
var ws = new WebSocket(socket);
webSocketCollector.push(ws);
callback(ws);
});

upgrade事件其实是http这个模块的封装,再往底层就是net模块的实现,其实都差不多,如果直接用net模块来实现的话,就是监听net.createServer返回的server对象的data事件,接收到的第一份数据就是客户端发来的升级请求报文。

上面那段代码就完成了websocket的握手,然后就可以开始数据传输了。

看数据传输之前,先看看websocket数据帧的定义(因为觉得深入浅出nodejs里的帧定义图最容易理解,所以就贴这张了):

上面的图中,每一列就是一个字节,一个字节总共是8位,每一位就是一个二进制数,不同位的值会对应不同的意义。

fin:指示这个是消息的最后片段。第一个片段可能也是最后的片段。如果为1即为最后片段,(其实这个位的用途我个人有点疑惑,按照书上以及网上查的资
料,当数据被分片的时候,不同片应该都会有fin位,会根据fin为是不是0来判断是否为最后一帧,但是实际实现中却发现,当数据比较大需要分片时,服务
端收到的数据就只有第一帧是有fin位为1,其他帧则整个帧都是数据段,也就是说,感觉这个fin位似乎用不上,至少我自己写的demo中是通过数据长度
来判断是否到了最后一帧,完全没用到这个fin位是否为1来判断)

rsv1、rsv2、rsv3:各占一个位,用于扩展协商,基本上不怎么需要理,一般都是0

opcode:占四个位,可以表示0~15的十进制,0表示为附加数据帧,1表示为文本数据帧,2表示二进制数据帧,8表示发送一个连接关闭的数据帧,9
表示ping,10表示pong,ping和pong都是用于心跳检测,当一端发送ping时,另一端必须响应pong表示自己仍处于响应状态。

masked:占一个位,表示是否进行掩码处理,客户端发送给服务端时为1,服务端发送给客户端时为0

payload
length:占7位,或者7+16位、或者7+64位。如果第二个字节的后面七个位的十进制值小于或等于125,则直接用这七个位表示数据长度;如果该
值为126,说明
125<数据长度<65535(16个位能描述的最大值,也就是16个1的时候),就用第三个字节及第四个字节即16个位来表示;如果该值为
127,则说明数据长度已经大于65535,16个位也已经不足以描述数据长度了,就用第三到第十个字节这八个字节来描述数据长度。

masking key:当masked为1的时候才存在,用于对我们需要的数据进行解密。

payload data:我们需要的数据,如果masked为1,该数据会被加密,要通过masking key进行异或运算解密才能获取到真实数据。

帧定义解释完了,就可以根据数据来进行解析了,当有data过来的时候,先获取需要的数据信息,下面这段代码将获取到数据在data里的位置,以及数据长度,masking key以及opcode:
WebSocket.prototype.handleDataStat = function (data) {
if (!this.stat) {
var dataIndex = 2; //数据索引,因为第一个字节和第二个字节肯定不为数据,所以初始值为2
var secondByte = data[1]; //代表masked位和可能是payloadLength位的第二个字节
var hasMask = secondByte >= 128; //如果大于或等于128,说明masked位为1
secondByte -= hasMask ? 128 : 0; //如果有掩码,需要将掩码那一位去掉
var dataLength, maskedData;
//如果为126,则后面16位长的数据为数据长度,如果为127,则后面64位长的数据为数据长度
if (secondByte == 126) {
dataIndex += 2;
dataLength = data.readUInt16BE(2);
} else if (secondByte == 127) {
dataIndex += 8;
dataLength = data.readUInt32BE(2) + data.readUInt32BE(6);
} else {
dataLength = secondByte;
}
//如果有掩码,则获取32位的二进制masking key,同时更新index
if (hasMask) {
maskedData = data.slice(dataIndex, dataIndex + 4);
dataIndex += 4;
}
//数据量最大为10kb
if (dataLength > 10240) {
this.send("Warning : data limit 10kb");
} else {
//计算到此处时,dataIndex为数据位的起始位置,dataLength为数据长度,maskedData为二进制的解密数据
this.stat = {
index: dataIndex,
totalLength: dataLength,
length: dataLength,
maskedData: maskedData,
opcode: parseInt(data[0].toString(16).split("")[1] , 16) //获取第一个字节的opcode位
};
}
} else {
this.stat.index = 0;
}
};

代码中均有注释,理解起来应该不难,直接看下一步,获取到数据信息后,就要对数据进行实际解析了:

经过上面handleDataStat方法的处理,stat中已经有了data的相关数据,先判断opcode,如果为9说明是客户端发起的
ping心跳检测,直接返回pong响应,如果为10则为服务端发起的心跳检测。如果有masking
key,则遍历数据段,对每个字节都与masking
key的字节进行异或运算(网上看到一个说法很形象:就是轮流发生X关系),^符号就是进行异或运算啦。如果没有masking
key则直接通过slice方法把数据截取下来。

获取到数据后,放进datas里保存,因为有可能数据被分片了,所以再将stat里的长度减去当前数据长度,只有当stat里的长度为0的时候,
说明当前帧为最后一帧,然后通过Buffer.concat将所有数据合并,此时再判断一下opcode,如果opcode为8,则说明客户端发起了一个
关闭请求,而我们获取到的数据则是关闭原因。如果不为8,则这数据就是我们需要的数据。然后再将stat重置为null,datas数组置空即可。至此,
我们的数据解析就完成了。
WebSocket.prototype.dataHandle = function (data) {
this.handleDataStat(data);
var stat;
if (!(stat = this.stat)) return;
//如果opcode为9,则发送pong响应,如果opcode为10则置pingtimes为0
if (stat.opcode === 9 || stat.opcode === 10) {
(stat.opcode === 9) ? (this.sendPong()) : (this.pingTimes = 0);
this.reset();
return;
}
var result;
if (stat.maskedData) {
result = new Buffer(data.length-stat.index);
for (var i = stat.index, j = 0; i < data.length; i++, j++) {
//对每个字节进行异或运算,masked是4个字节,所以%4,借此循环
result[j] = data[i] ^ stat.maskedData[j % 4];
}
} else {
result = data.slice(stat.index, data.length);
}
this.datas.push(result);
stat.length -= (data.length - stat.index);
//当长度为0,说明当前帧为最后帧
if (stat.length == 0) {
var buf = Buffer.concat(this.datas, stat.totalLength);
if (stat.opcode == 8) {
this.close(buf.toString());
} else {
this.emit("message", buf.toString());
}
this.reset();
}
};

完成了客户端发来的数据解析,还需要一个服务端发数据至客户端的方法,也就是按照上面所说的帧定义来组装数据并且发送出去。下面的代码中基本上每一行都有注释,应该还是比较容易理解的。
//数据发送
WebSocket.prototype.send = function (message) {
if(this.state !== "OPEN") return;
message = String(message);
var length = Buffer.byteLength(message);
// 数据的起始位置,如果数据长度16位也无法描述,则用64位,即8字节,如果16位能描述则用2字节,否则用第二个字节描述
var index = 2 + (length > 65535 ? 8 : (length > 125 ? 2 : 0));
// 定义buffer,长度为描述字节长度 + message长度
var buffer = new Buffer(index + length);
// 第一个字节,fin位为1,opcode为1
buffer[0] = 129;
// 因为是由服务端发至客户端,所以无需masked掩码
if (length > 65535) {
buffer[1] = 127;
// 长度超过65535的则由8个字节表示,因为4个字节能表达的长度为4294967295,已经完全够用,因此直接将前面4个字节置0
buffer.writeUInt32BE(0, 2);
buffer.writeUInt32BE(length, 6);
} else if (length > 125) {
buffer[1] = 126;
// 长度超过125的话就由2个字节表示
buffer.writeUInt16BE(length, 2);
} else {
buffer[1] = length;
}
// 写入正文
buffer.write(message, index);
this.socket.write(buffer);
};

最后还要实现一个功能,就是心跳检测:防止服务端长时间不与客户端交互而导致客户端关闭连接,所以每隔十秒都会发送一次ping进行心跳检测
//每隔10秒进行一次心跳检测,若连续发出三次心跳却没收到响应则关闭socket
WebSocket.prototype.checkHeartBeat = function () {
var that = this;
setTimeout(function () {
if (that.state !== "OPEN") return;
if (that.pingTimes >= 3) {
that.close("time out");
return;
}
//记录心跳次数
that.pingTimes++;
that.sendPing();
that.checkHeartBeat();
}, 10000);
};
WebSocket.prototype.sendPing = function () {
this.socket.write(new Buffer(['0x89', '0x0']))
};
WebSocket.prototype.sendPong = function () {
this.socket.write(new Buffer(['0x8A', '0x0']))
};

至此,整个websocket的实现就完成了,此demo只是大概实现了一下websocket而已,在安全之类方面肯定还是有很多问题,若是真正生产环境中还是用socket.io这类成熟的插件比较好。不过这还是很值得一学的。

F. WebSocket 的实现

长连接: 一个链接上可以连续发送多个数据包,在链接期间,如果没有数据包发送,需要双方发链路检查包

TCP/IP: TCP/IP 属于传输层,主要解决网络中的数据传输问题,只管传输数据。但这样对传输的数据没有一个规范的封装、解析等处理。使得传输的数据难前咐以识别,所以才有了应用层协议对数据进行的封装、解析等,如http协议。

HTTP: HTTP协议是慧虚纯应用层协议,用于分装解析传输数据。 从HTTP1.1开始其实就默认开启了长链接,也就是请求头header中可以看到Connection:Keep-alive。但是长连接只是说保持了(服务器可以告诉客户端保持时间Keep-Alive:timeout=20;max=20;)这个TCP通道,并采用服务器和客户端应答模式(Request-Response),不需要再创建一个链接通道,做到一个性能优化。

socket: 与HTTP协议不一样,socket不是协议,他是在程序层面上对传输层协议(像TCP/IP)的接口封装。我们知道传输层的协议,是解决数据在网络中传输的问题的,那么socket(套接字)就是传输通道两端的接口。

Websocket: WebSocket是包装成了一个应用层协议作为socket,从而能够让客户端和远程服务端通过web建立全双工通信。

WebSocket API 是HTML5 推出的东誉闷西。在客户端我们可以通过HTML5 所提供的API 对websocket 进行创建、发送数据、监听信息、监听报错等功能( HTML5 WebSocket )

我们知道WebSocket 是在Socket的基础上实现的,所以我们要做的是对现有的Socket协议进行升级。

步骤: 客户端发送websocket请求-->服务端接受并识别该请求-->对该请求协议进行升级--> 返回给客户端 --> websocket 通道建立 --> 客户端/服务端发送数据

协议升级

在这里需要注意的是头部信息和头部信息中的Sec-Websocket-Accept的值。

该值需要是一个通过base64加密的哈希值(sha1)。 而该加密所用的数据是客户端传过来的sec-websocket-key的值和MAGIC_STRINC内的固定值。 对MAGIC_STRINC的说明

Webscoket 中传输的数据是 数据帧(frame)

数据帧有多种类型 主要有:文本型、二进制数据

数据帧结构

每一列代表一个字节,一个字节8位,每一位又代表一个二进制数。

创建数据帧

解数据帧

心跳检查

由于websocket 不进行交互会关闭通道所以,才有了心跳检查。

websocket与和他http的区别

基于node实现websocket协议

使用nodeJS在HTTP上实现WebSocket

如何让我的服务器返回正确的Sec-WebSocket-Accept标头值

学习WebSocket协议—从顶层到底层的实现原理

websocket 协议帧 解析

nodejs实现Websocket的数据接收发送

阅读全文

与nodejswebsocket性能相关的资料

热点内容
秘密文件一般保存多少年 浏览:952
文件夹隐藏后怎么恢复 浏览:251
文件恢复360 浏览:914
c打开文件夹选择文件 浏览:430
工程变更文件需要多少份 浏览:227
风云三国28文件修改器 浏览:461
哪个app有pets词汇 浏览:926
苹果6s看电影屏幕变暗 浏览:474
怎么注册文件 浏览:600
excel数字怎么自动填入相同数据 浏览:652
上百万条数据用什么软件处理 浏览:680
打开的程序不是全屏 浏览:837
不用u盘传文件 浏览:673
什么少儿编程课网课哪个好 浏览:98
win101706开始界面 浏览:152
java二维数据初始化 浏览:174
listmapjava 浏览:296
利用视频赚钱的网站有哪些 浏览:766
iphone4sios711降级 浏览:246
探岳数据线口在哪里 浏览:654

友情链接