WebSocket草案10文本数据解析和发送

握手协议实现了,接下来就是解析数据了,这个相对来说要麻烦很多,相比草案7.6变得更复杂了。下图是数据传输的格式:

WebSocket草案10文本数据解析和发送_第1张图片

各个值的具体含义可以参考这个中文翻译:http://blog.csdn.net/fenglibing/article/details/6852497,英文原文在这里:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#section-4.2

简单概括一下就是:FIN一直为1,RSV1、RSV2、RSV3一直为0,当发送内容为文本时,opcode为1,MASK如果为1就使用掩码,需要获取四位掩码,依次轮流和数据做异或运算,第一个内容数据与第一个掩码异或,第二个内容数据与第二个掩码异或……第五个内容数据与第一个掩码异或……一直到结束,然后再对内容进行编码就可以了。

在Chrome/Firefox下面,FIN永远为1,如果只是文本消息,opcode一直是1,RSV1、RSV2、RSV3都为0,所以第一个数据是0×81;MASK为1,再跟上数据长度,所以第二个数据会大于0×80,由于数据长度不一样,其需要占用的字节数也不一样,因此掩码的位置也不一样。根据规范,换算下来是这样:数据总长度(包含FIN、RSV等等信息)小于0×84,则掩码为第三个数据到第六个数据;数据总长度(包含FIN、RSV等等信息)小于0xfe81,则掩码为第五个数据到第八个数据;其他掩码则为第十三个数据到第十六个数据。当数据长度超过一个包的长度时,后面的数据包不再包含RIN、RSV和MASK等信息,二是直接为内容数据,需要根据上一次取到的MASK来做异或运算。在客户端主动要求断开链接时,会没有内容数据,只有头信息和掩码,长度固定为6,为了避免混淆,Chrome会保证正常内容发送时不会出现6字节的数据,基本上可以把6字节当断开连接请求处理。区分客户端主动要求断开连接和正常的连续数据包要根据长度区分:如果是连续的数据包,则已解析的包的长度会小于首包头信息里的长度,否则则认为是客户端主动要求断开链接。主动断开连接时会发送的一个额外的数据包,此时第一个字节不会为正常的0×81,通常为0×88,NodeJS写出来大致是这样的:

  
  
  
  
  1. module.exports = Parser;
  2. var util = require('util'),
  3.     events = require('events');
  4. /**
  5. * http.Server 数据解析类
  6. * */
  7. function Parser(version) {
  8.      events.EventEmitter.call(this);
  9.      this.version = version || 'draft10';
  10.     this.length = 0;
  11.     this.parsed = 0;
  12.      this.maskData = [];
  13.      this.frameData = [];
  14.      this.order = 0;
  15.      this.closing = false;
  16. }
  17. util.inherits(Parser, events.EventEmitter);
  18. Parser.prototype.write = function(data) {
  19.     console.log(data.length);
  20.      dataParser[this.version](data, this);
  21. };
  22. // 数据解析工具
  23. var dataParser = {
  24.      'draft10': function(data, parser){
  25.         var pkt, i =0, len = 0, start = 0;
  26.         // 如果不包含包头,而且数据已经解析完成
  27.         // 则认为这个包为结束包,首字节通常为0x88
  28.         if(data[0] != 0x81 && parser.length == 0){
  29.             parser.emit('close');
  30.             parser.closing = false;
  31.             return;
  32.         }
  33.         // 草案10
  34.         // 首包会包含掩码信息
  35.         if(data[0] == 0x81){
  36.             // 使用了掩码
  37.             if(data[1] >= 0x80){
  38.                 // 数据长度不一样,掩码位置不一样
  39.                 if(data.length < 0x84){
  40.                     // firefox下,超过一个包长度的数据会被拆分为多个包
  41.                     // 其中首包只包含头信息,第二个字节为0xfe
  42.                     if(data[1] == 0xfe){
  43.                         len = data.length;
  44.                         // firefox
  45.                         parser.maskData = [data[len - 4], data[len - 3], data[len - 2], data[len - 1]];
  46.                         parser.length = data[len - 5];
  47.                         for(i = len - 6; i > 1; i--){
  48.                             parser.length += data[i] * (len - 5 - i) * 256;
  49.                         }
  50.                         console.log('firefox multi packages, length: ', parser.length);
  51.                         start = data.length;
  52.                     }
  53.                     else{
  54.                         // chrome
  55.                         parser.length = data[1] - 0x80;
  56.                         console.log('7bit, length: ', parser.length);
  57.                         parser.maskData = [data[2], data[3], data[4], data[5]];
  58.                         start = 6;
  59.                     }
  60.                 }
  61.                 else if(data.length < 0xfe80){                     parser.length = data[2] * 256 + data[3];                     console.log('7 + 16bit, length: ', parser.length);                     parser.maskData = [data[4], data[5], data[6], data[7]];                     start = 8;                 }                 else{                     dparser.length = data[11];                     for(i = 10; i > 3; i--){
  62.                         parser.length += data[i] * (11 - i) * 256;
  63.                     }
  64.                     console.log('7 + 64bit, length: ', parser.length);
  65.                     parser.maskData = [data[12], data[13], data[14], data[15]];
  66.                     start = 16;
  67.                 }
  68.                 for(i = start, len = data.length; i < len; i++){
  69.                     parser.frameData.push(parser.maskData[(i - start) % 4] ^ data[i]);
  70.                 }
  71.             }
  72.             else{
  73.                 if(data.length < 0x80){
  74.                     start = 2;
  75.                 }
  76.                 else if(data.length < 0xfe81){
  77.                     start = 4;
  78.                 }
  79.                 else{
  80.                     start = 12;
  81.                 }
  82.                 // find contents
  83.                 parser.frameData = data.splice(start);
  84.             }
  85.             console.log('1st packge frame length: ', parser.frameData.length);
  86.             if(parser.frameData.length == parser.length){
  87.                 pkt = new Buffer(parser.frameData);
  88.                 // console.log(pkt.toString('utf8', 0, pkt.length));
  89.                 parser.emit('message', pkt.toString('utf8', 0, pkt.length));
  90.                 // 数据包结束,重置长度信息
  91.                 parser.frameData = [];
  92.                 parser.length = 0;
  93.             }
  94.             return;
  95.         }
  96.         // 连续的数据包
  97.         if(parser.maskData.length){
  98.             // continue to parse data
  99.             for(i = 0, l = data.length; i < l; i++){
  100.                 parser.frameData.push(parser.maskData[i % 4] ^ data[i]);
  101.             }
  102.             console.log('frame length: ', parser.frameData.length);
  103.             if(parser.frameData.length == parser.length){
  104.                 pkt = new Buffer(parser.frameData);
  105.                 // console.log(pkt.toString('utf8', 0, pkt.length));
  106.                 parser.emit('message', pkt.toString('utf8', 0, pkt.length));
  107.                 // 数据包结束,重置长度信息
  108.                 parser.frameData = [];
  109.                 parser.length = 0;
  110.             }
  111.             return;
  112.         }
  113.      }
  114. };
转自:http://fdream.net/blog/article/759.aspx

在Draft 10中,如果解析数据的过程弄清楚了,这个就更简单了,返回数据的格式和之前接受到的数据格式非常类似,只是你不用生成mask了,头部的其他格式还是一模一样的。

第一个字节还是固定的,是0×81,意义和接受数据的意义一样,第二个字节也是,后七个位表示数据长度,由于没有mask,所以第一位是0;长度的表示方法和接受的标识方法一致,可能用7位表示,也可能用16位表示。

用NodeJS实现如下:

   
   
   
   
  1. var socketWriter = {
  2.     'draft10': function(socket, data){
  3.         var frames,
  4.             length = new Buffer(data, 'utf8').length;
  5.         if(data.length > 0x7d){
  6.             frames = new Buffer(4);
  7.             frames[0] = 0x81;
  8.             frames[1] = 0x7e;
  9.             frames[2] = length >> 8;
  10.             frames[3] = length & 0xFF; //1111 1111
  11.         }
  12.         else{
  13.             frames = new Buffer(2);
  14.             frames[0] = 0x81;
  15.             frames[1] = length;
  16.         }
  17.         if (socket.writable) {
  18.             socket.write(frames, 'binary');
  19.             socket.write(data, 'utf8');
  20.             return true;
  21.         }
  22.         return false;
  23.     },
  24.     'draft76': function(socket, data){
  25.         var byteLen = Buffer.byteLength(data, 'utf8'),
  26.             bytes = new Buffer(byteLen + 2);
  27.         bytes[0] = 0x00;
  28.         bytes.write(data, 1, 'utf8');
  29.         bytes[byteLen + 1] = 0xFF;
  30.         return socket.write(bytes);
  31.     }
  32. };
  33. socketWriter['draft75'] = socketWriter['draft76'];
转自:http://fdream.net/blog/article/767.aspx
http://fdream.net/blog/article/767.aspx

你可能感兴趣的:(function,chrome,websocket,buffer,firefox,events)