node.js 使用 net 模块模拟 websocket 握手进行数据传递操作示例
本文实例讲述了node.js使用net模块模拟websocket握手进行数据传递操作。分享给大家供大家参考,具体如下:
websocket是一种让浏览器与服务器之间建立持久的连接,并能进行双向数据传输的一种协议。
websocket属性应用层协议,基于tcp传输协议,并复用http的握手通道。
一、如何进行websocket连接。
websocket复用了http的握手通道,客户端通过http请求与服务端进行协商,升级协议。协议升级完后,后面的数据交换则遵照websocket协议。
1、客户端申请协议升级
RequestURL:ws://localhost:8888/ RequestMethod:GET Connection:Upgrade Upgrade:websocket Sec-WebSocket-Version:13 Sec-WebSocket-Key:uR5YP/BMO6M24tAFcmHeXw== Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bits
- Connection:Upgrade表示要升级协议
- Upgrade:websocket表示升级到websocket协议
- Sec-WebSocket-Version:13表示websocket的版本
- Sec-WebSocket-Key表示websocket的验证,防止恶意的连接,与服务端响应的Sec-WebSocket-Accept是配套。
2、服务端响应协议升级
StatusCode:101SwitchingProtocols Connection:Upgrade Sec-WebSocket-Accept:eS92kXpBNI6fWsCkj6WxH6QeoHs= Upgrade:websocket
StatusCode:101表示状态码,协议切换。
Sec-WebSocket-Accept表示服务端响应的校验,与客户端的Sec-WebSocket-Key是配套的。
3、Sec-WebSocket-Accept是如何计算的
将Sec-WebSocket-Key的值与258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。
然后通过sha1计算,再转成base64。
constcrypto=require('crypto'); functiongetSecWebSocketAccept(key){ returncrypto.createHash('sha1') .update(key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11') .digest('base64'); } console.log(getSecWebSocketAccept('uR5YP/BMO6M24tAFcmHeXw=='));
4、协议升级完后,后续的数据传输就需要按websocket协议来走。
websocket客户端与服务端通信的最小单位是帧,由1个或多个帧组成完整的消息。
客户端:将消息切割成多个帧,发送给服务端。
服务端:接收到消息帧,将帧重新组装成完整的消息。
5、数据帧的格式
单位是1个比特位,FIN,PSV1,PSV2,PSV3占1个比特位,opcode占4个比特位。
01234567012345670123456701234567 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R|opcode|M|Payloadlen|Extendedpayloadlength| |I|S|S|S|(4)|A|(7)|(16/64)| |N|V|V|V||S||(ifpayloadlen==126/127)| ||1|2|3||K||| +-+-+-+-+-------+-+-------------+-------------------------------+ |Extendedpayloadlengthcontinued,ifpayloadlen==127| +-------------------------------+-------------------------------+ ||Masking-key,ifMASKsetto1| +-------------------------------+-------------------------------+ |Masking-key(continued)|PayloadData| +-------------------------------+-------------------------------+ |PayloadDatacontinued...| +---------------------------------------------------------------+ |PayloadDatacontinued...| +---------------------------------------------------------------+
FIN 占1位,用来表示该帧是否是最后一帧,1表示是,0表示不是。
RSV1,RSV2,RSV3 分别占1位,一般情况下全为0,扩展使用,值的含义由扩展进行定义。
opcode占4位,表示如何解析后面的数据载荷(PayloadData)。
%x0表示一个延续帧,opcode为0时,表示数据传输采用了数据分片,当前的数据帧只是其中一个数据分片。
%x1表示这是一个文本帧
%x2表示这是一个二进制帧
%x3-7保留的操作代码,用于定义后续的非控制帧。
%x8表示连接断开
%x9表示这是一个ping操作
%xA表示这是一个pong操作
%xB-F保留的操作代码,用于定义后续的控制帧。
MASK占1位,表示是否要对数据载荷进行掩码操作。
客户端向服务端发数据,需要对数据进行掩码操作,服务端向客户端发数据,不需要对数据进行掩码操作。
如果Mask为1,则Masking-key中会定义一个掩码键,通过该掩码键对数据载荷进行反掩码。客户端发送给服务端的数据帧,MASK都是1。
Payloadlen为7位,或7+16位,或7+64位,表示数据载荷的长度,单位字节。
如果Payloadlen=0~125,表示,数据的长度为0~125字节。
如果Payloadlen=126,表示,后续的2个字节代表一个16位的无符号整数,该整数表示数据的长度。
如果Payloadlen=127,表示,后续的8个字节代表一个64位的无符号整数,该整数表示数据的长度。
如果Payloadlen占用多个字节,Payloadlen的二进制表达采用Big-endian。
Masking-key占0或32位,客户端向服务端发送数据帧,数据载荷都进行了掩码操作,Mask为1,且带了4字节的Masking-key。如果Mask为0,则没有Masking-key。
注意数据载荷的长度,不包括Masking-key的长度。
6、掩码的算法
Masking-key掩码键是由客户端生成的32位随机数,掩码操作不会影响数据载荷的长度。
functionunmask(buffer,mask){ constlength=buffer.length; for(vari=0;i7、实现websocket的握手
constcrypto=require('crypto'); constnet=require('net'); //计算websocket校验 functiongetSecWebSocketAccept(key){ returncrypto.createHash('sha1') .update(key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11') .digest('base64'); } //掩码操作 functionunmask(buffer,mask){ constlength=buffer.length; for(vari=0;iindex.html的代码:
Document