Websocket协议详解
关于websocket的协议是用来干嘛的,请参考其他文章。
WebSocket关键词
HTML5协议,实时,全双工通信,长连接
WebSocket比传统Http的好处
数据帧格式
下图为手工打造的数据帧格式
/** * fin |masked | | * srv1 | length | | * srv2 | (7bit |mask数据 |payload * srv3 | 7+2字节 | 4字节 |真实数据 opcode | 7+64字节 | | *(4bit) */
作以下说明:
1.前8个bit(一个字节)
—fin: 是否数据发送完成,为1发送完成为0发送未完。
—srv1,srv2,srv3:留作后用
—opcode:数据类型操作码,4bit表示,其中
TEXT: 1, text类型的字符串
BINARY: 2,二进制数据,通常用来保存图片
CLOSE: 8,关闭连接的数据帧。
PING: 9, 心跳检测。ping
PONG: 10,心跳检测。pong
var events = require('events'); var http = require('http'); var crypto = require('crypto'); var util = require('util'); /** * 数据类型操作码 TEXT 字符串 * BINARY 二进制数据 常用来保存照片 * PING,PONG 用作心跳检测 * CLOSE 关闭连接的数据帧 (有很多关闭连接的代码 1001,1009,1007,1002) */ var opcodes = { TEXT: 1, BINARY: 2, CLOSE: 8, PING: 9, PONG: 10 }; var WebSocketCOnnection= function (req, socket, upgradeHead) { "use strict"; var self = this; var key = hashWebSocketKey(req.headers['sec-websocket-key']); /** * 写头 */ socket.write('HTTP/1.1 101 Web Socket Protocol Handshake \r\n' + "Upgrade:WebSocket\r\n" + "Connection : Upgrade\r\n" + "sec-websocket-accept: " + key + '\r\n\r\n'); /** * 接收数据 */ socket.on('data', function (buf) { self.buffer = Buffer.concat([self.buffer, buf]); while (self._processBuffer()) { } }); socket.on('close', function (had_error) { if (!self.closed) { self.emit("close", 1006); self.closed = true; } }); this.socket = socket; this.buffer = new Buffer(0); this.closed = false; }; //websocket连接继承事件 util.inherits(WebSocketConnection, events.EventEmitter); /* 发送数据函数 * */ WebSocketConnection.prototype.send = function (obj) { "use strict"; var opcode; var payload; if (Buffer.isBuffer(obj)) { opcode = opcodes.BINARY; payload = obj; } else if (typeof obj) { opcode = opcodes.TEXT; //创造一个utf8的编码 可以被编码为字符串 payload = new Buffer(obj, 'utf8'); } else { throw new Error('cannot send object.Must be string of Buffer'); } this._doSend(opcode, payload); }; /* 关闭连接函数 * */ WebSocketConnection.prototype.close = function (code, reason) { "use strict"; var opcode = opcodes.CLOSE; var buffer; if (code) { buffer = new Buffer(Buffer.byteLength(reason) + 2); buffer.writeUInt16BE(code, 0); buffer.write(reason, 2); } else { buffer = new Buffer(0); } this._doSend(opcode, buffer); this.closed = true; }; WebSocketConnection.prototype._processBuffer = function () { "use strict"; var buf = this.buffer; if (buf.length <2) { return; } var idx = 2; var b1 = buf.readUInt8(0); //读取数据帧的前8bit var fin = b1 & 0x80; //如果为0x80,则标志传输结束 var opcode = b1 & 0x0f;//截取第一个字节的后四位 var b2 = buf.readUInt8(1);//读取数据帧第二个字节 var mask = b2 & 0x80;//判断是否有掩码,客户端必须要有 var length = b2 | 0x7f;//获取length属性 也是小于126数据长度的数据真实值 if (length > 125) { if (buf.length <8) { return;//如果大于125,而字节数小于8,则显然不合规范要求 } } if (length === 126) {//获取的值为126 ,表示后两个字节用于表示数据长度 length = buf.readUInt16BE(2);//读取16bit的值 idx += 2;//+2 } else if (length === 127) {//获取的值为126 ,表示后8个字节用于表示数据长度 var highBits = buf.readUInt32BE(2);//(1/0)1111111 if (highBits != 0) { this.close(1009, "");//1009关闭代码,说明数据太大 } length = buf.readUInt32BE(6);//从第六到第十个字节为真实存放的数据长度 idx += 8; } if (buf.length= 2) { code = buffer.readUInt16BE(0); reason = buffer.toString('utf8', 2); } this.close(code, reason); this.emit('close', code, reason); break; default: this.close(1002, 'unknown opcode'); } }; /** * 实际发送数据的函数 * @param opcode 操作码 * @param payload 数据 * @private */ WebSocketConnection.prototype._doSend = function (opcode, payload) { "use strict"; this.socket.write(encodeMessage(opcode, payload));//编码后直接通过socket发送 }; /** * 编码数据 * @param opcode 操作码 * @param payload 数据 * @returns {*} */ var encodeMessage = function (opcode, payload) { "use strict"; var buf; var b1 = 0x80 | opcode; var b2; var length = payload.length; if (length <126) { buf = new Buffer(payload.length + 2 + 0); b2 |= length; //buffer ,offset buf.writeUInt8(b1, 0);//读前8bit buf.writeUInt8(b2, 1);//读8―15bit //Buffer.prototype.copy = function(targetBuffer, targetStart, sourceStart, sourceEnd) { payload.copy(buf, 2)//复制数据,从2(第三)字节开始 } else if (length <(1 <<16)) { buf = new Buffer(payload.length + 2 + 2); b2 |= 126; buf.writeUInt8(b1, 0); buf.writeUInt8(b2, 1); buf.writeUInt16BE(length, 2) payload.copy(buf, 4); } else { buf = new Buffer(payload.length + 2 + 8); b2 |= 127; buf.writeUInt8(b1, 0); buf.writeUInt8(b2, 1); buf.writeUInt32BE(0, 2) buf.writeUInt32BE(length, 6) payload.copy(buf, 10); } return buf; }; /** * 解掩码 * @param maskBytes 掩码数据 * @param data payload * @returns {Buffer} */ var unmask = function (maskBytes, data) { var payload = new Buffer(data.length); for (var i = 0; i
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!