欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Websocket協(xié)議詳解及簡單實(shí)例代碼

 更新時(shí)間:2016年12月12日 11:41:59   作者:Newpidian  
這篇文章主要介紹了Websocket協(xié)議詳解及簡單實(shí)例代碼的相關(guān)資料,這里對websocket 協(xié)議進(jìn)行詳細(xì)介紹,并附簡單實(shí)例代碼,需要的朋友可以參考下

Websocket協(xié)議詳解

關(guān)于websocket的協(xié)議是用來干嘛的,請參考其他文章。

WebSocket關(guān)鍵詞

HTML5協(xié)議,實(shí)時(shí),全雙工通信,長連接

WebSocket比傳統(tǒng)Http的好處

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

數(shù)據(jù)幀格式

下圖為手工打造的數(shù)據(jù)幀格式


/**
 * fin  |masked    |      |
 * srv1 |  length   |      |
 * srv2 |  (7bit   |mask數(shù)據(jù)   |payload
 * srv3 |   7+2字節(jié)  | 4字節(jié)    |真實(shí)數(shù)據(jù)
 opcode |   7+64字節(jié) |      |
 *(4bit)
 */

作以下說明:

1.前8個(gè)bit(一個(gè)字節(jié))
—fin: 是否數(shù)據(jù)發(fā)送完成,為1發(fā)送完成為0發(fā)送未完。
—srv1,srv2,srv3:留作后用
—opcode:數(shù)據(jù)類型操作碼,4bit表示,其中
TEXT: 1, text類型的字符串
BINARY: 2,二進(jìn)制數(shù)據(jù),通常用來保存圖片
CLOSE: 8,關(guān)閉連接的數(shù)據(jù)幀。
PING: 9, 心跳檢測。ping
PONG: 10,心跳檢測。pong

var events = require('events');
var http = require('http');
var crypto = require('crypto');
var util = require('util');

/**
 * 數(shù)據(jù)類型操作碼 TEXT 字符串
 * BINARY 二進(jìn)制數(shù)據(jù) 常用來保存照片
 * PING,PONG 用作心跳檢測
 * CLOSE 關(guān)閉連接的數(shù)據(jù)幀 (有很多關(guān)閉連接的代碼 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');

  /**
   * 接收數(shù)據(jù)
   */
  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);

/*
 發(fā)送數(shù)據(jù)函數(shù)
 * */
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;
    //創(chuàng)造一個(gè)utf8的編碼 可以被編碼為字符串
    payload = new Buffer(obj, 'utf8');
  } else {
    throw new Error('cannot send object.Must be string of Buffer');
  }

  this._doSend(opcode, payload);
};
/*
 關(guān)閉連接函數(shù)
 * */
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);  //讀取數(shù)據(jù)幀的前8bit
  var fin = b1 & 0x80; //如果為0x80,則標(biāo)志傳輸結(jié)束
  var opcode = b1 & 0x0f;//截取第一個(gè)字節(jié)的后四位
  var b2 = buf.readUInt8(1);//讀取數(shù)據(jù)幀第二個(gè)字節(jié)
  var mask = b2 & 0x80;//判斷是否有掩碼,客戶端必須要有
  var length = b2 | 0x7f;//獲取length屬性 也是小于126數(shù)據(jù)長度的數(shù)據(jù)真實(shí)值
  if (length > 125) {
    if (buf.length < 8) {
      return;//如果大于125,而字節(jié)數(shù)小于8,則顯然不合規(guī)范要求
    }
  }
  if (length === 126) {//獲取的值為126 ,表示后兩個(gè)字節(jié)用于表示數(shù)據(jù)長度
    length = buf.readUInt16BE(2);//讀取16bit的值
    idx += 2;//+2
  } else if (length === 127) {//獲取的值為126 ,表示后8個(gè)字節(jié)用于表示數(shù)據(jù)長度
    var highBits = buf.readUInt32BE(2);//(1/0)1111111
    if (highBits != 0) {
      this.close(1009, "");//1009關(guān)閉代碼,說明數(shù)據(jù)太大
    }
    length = buf.readUInt32BE(6);//從第六到第十個(gè)字節(jié)為真實(shí)存放的數(shù)據(jù)長度
    idx += 8;
  }

  if (buf.length < idx + 4 + length) {//不夠長 4為掩碼字節(jié)數(shù)
    return;
  }

  var maskBytes = buf.slice(idx, idx + 4);//獲取掩碼數(shù)據(jù)
  idx += 4;//指針前移到真實(shí)數(shù)據(jù)段
  var payload = buf.slice(idx, idx + length);
  payload = unmask(maskBytes, payload);//解碼真實(shí)數(shù)據(jù)
  this._handleFrame(opcode, payload);//處理操作碼
  this.buffer = buf.slice(idx + length);//緩存buffer
  return true;
};

/**
 * 針對不同操作碼進(jìn)行不同處理
 * @param 操作碼
 * @param 數(shù)據(jù)
 */
WebSocketConnection.prototype._handleFrame = function (opcode, buffer) {
  "use strict";
  var payload;
  switch (opcode) {
    case opcodes.TEXT:
      payload = buffer.toString('utf8');//如果是文本需要轉(zhuǎn)化為utf8的編碼
      this.emit('data', opcode, payload);//Buffer.toString()默認(rèn)utf8 這里是故意指示的
      break;
    case opcodes.BINARY: //二進(jìn)制文件直接交付
      payload = buffer;
      this.emit('data', opcode, payload);
      break;
    case opcodes.PING://發(fā)送ping做響應(yīng)
      this._doSend(opcodes.PING, buffer);
      break;
    case opcodes.PONG: //不做處理
      break;
    case opcodes.CLOSE://close有很多關(guān)閉碼
      let code, reason;//用于獲取關(guān)閉碼和關(guān)閉原因
      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');
  }
};

/**
 * 實(shí)際發(fā)送數(shù)據(jù)的函數(shù)
 * @param opcode 操作碼
 * @param payload 數(shù)據(jù)
 * @private
 */
WebSocketConnection.prototype._doSend = function (opcode, payload) {
  "use strict";
  this.socket.write(encodeMessage(opcode, payload));//編碼后直接通過socket發(fā)送
};

/**
 * 編碼數(shù)據(jù)
 * @param opcode 操作碼
 * @param payload  數(shù)據(jù)
 * @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)//復(fù)制數(shù)據(jù),從2(第三)字節(jié)開始

  } 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 掩碼數(shù)據(jù)
 * @param data payload
 * @returns {Buffer}
 */
var unmask = function (maskBytes, data) {
  var payload = new Buffer(data.length);
  for (var i = 0; i < data.length; i++) {
    payload[i] = maskBytes[i % 4] ^ data[i];
  }
  return payload;
};
var KEY_SUFFIX = '258EAFA5-E914-47DA-95CA-C5ABoDC85B11';

/*equals to crypto.createHash('sha1').update(key+'KEY_SUFFIX').digest('base64')
 * */
var hashWebSocketKey = function (key) {
  "use strict";
  var sha1 = crypto.createHash('sha1');
  sha1.update(key + KEY_SUFFIX, 'ascii');
  return sha1.digest('base64');
};

exports.listen = function (port, host, connectionHandler) {
  "use strict";
  var srv = http.createServer(function (req, res) {
  });

  srv.on('upgrade', function (req, socket, upgradeHead) {
    "use strict";
    var ws = new WebSocketConnection(req, socket, upgradeHead);
    connectionHandler(ws);
  });
  srv.listen(port, host);
};

 感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!

相關(guān)文章

最新評論