国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

Websocket協議詳解及簡單實例代碼

2019-11-19 18:32:57
字體:
來源:轉載
供稿:網友

Websocket協議詳解

關于websocket的協議是用來干嘛的,請參考其他文章。

WebSocket關鍵詞

HTML5協議,實時,全雙工通信,長連接

WebSocket比傳統Http的好處

  1. 客戶端與服務端只建立一個TCP連接,可以使用更少的連接
  2. WebSocket的服務端可以將數據推送到客戶端,如實時將證券信息反饋到客戶端(這個很關鍵),實時天氣數據,比http請求響應模式更靈活
  3. 更輕量的協議頭,減少數據傳送量

數據幀格式

下圖為手工打造的數據幀格式

/** * 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 < idx + 4 + length) {//不夠長 4為掩碼字節數    return;  }  var maskBytes = buf.slice(idx, idx + 4);//獲取掩碼數據  idx += 4;//指針前移到真實數據段  var payload = buf.slice(idx, idx + length);  payload = unmask(maskBytes, payload);//解碼真實數據  this._handleFrame(opcode, payload);//處理操作碼  this.buffer = buf.slice(idx + length);//緩存buffer  return true;};/** * 針對不同操作碼進行不同處理 * @param 操作碼 * @param 數據 */WebSocketConnection.prototype._handleFrame = function (opcode, buffer) {  "use strict";  var payload;  switch (opcode) {    case opcodes.TEXT:      payload = buffer.toString('utf8');//如果是文本需要轉化為utf8的編碼      this.emit('data', opcode, payload);//Buffer.toString()默認utf8 這里是故意指示的      break;    case opcodes.BINARY: //二進制文件直接交付      payload = buffer;      this.emit('data', opcode, payload);      break;    case opcodes.PING://發送ping做響應      this._doSend(opcodes.PING, buffer);      break;    case opcodes.PONG: //不做處理      break;    case opcodes.CLOSE://close有很多關閉碼      let code, reason;//用于獲取關閉碼和關閉原因      if (buffer.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

主站蜘蛛池模板:
大足县|
北辰区|
肃南|
滁州市|
余江县|
武定县|
阳谷县|
沭阳县|
于田县|
高青县|
枣庄市|
浑源县|
新竹县|
苏尼特右旗|
平利县|
大同市|
枣强县|
呼图壁县|
西盟|
青州市|
合江县|
建德市|
玉林市|
特克斯县|
高陵县|
北安市|
东城区|
开原市|
宝应县|
安达市|
皮山县|
东莞市|
达州市|
潮安县|
莱阳市|
习水县|
黄骅市|
宁国市|
湖北省|
广宁县|
南江县|