WebSocket草案10テキストデータ解析と送信

44611 ワード

握手協議が実現しました.次はデータの解析です.これは相対的に面倒くさいです.草案7.6より複雑になりました.下図はデータ転送のフォーマットです.
WebSocket草案10文本数据解析和发送
各値の具体的な意味はこの中国語の翻訳を参照することができます.
簡単に要約すると、FINはずっと1で、RSV 1、RSV 2、RSV 3はずっと0で、内容がテキストを送る時、opcodeは1で、MASKは1のためにマスクを使うなら、4ビットのマスクを獲得して、順番に順番を変えて、データと異種の演算をします.最初のコンテンツデータは最初のマスクと違います.第二のコンテンツデータは第二のマスクと違っています.第五のコンテンツデータは第一のマスクと違っています.
Chrome/Firefoxの下では、FINは常に1であり、テキストメッセージだけであれば、opcodeは1であり、RSV 1、RSV 2、RSV 3はいずれも0であるため、最初のデータは0である.×81;MASKは1で、データ長についていくので、2番目のデータは0より大きいです.×80は、データの長さが違っていますので、必要なバイト数も違います.したがって、マスクの位置も違います.規格によると、換算すると、データの総長さ(FIN、RSVなどを含む)は0より小さいです.×84なら、マスクは第三のデータから第六のデータまでです.データの総長さ(FIN、RSVなどの情報を含む)が0 x fe81より小さい場合、マスクは5番目のデータから8番目のデータまでです.他のマスクは13番目のデータから16番目のデータまでです.データの長さが一つのパケットの長さを超えている場合、後ろのパケットはもう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において、データを解析する過程が明らかになれば、これはより簡単になります.戻りデータのフォーマットは前に受けたデータフォーマットと非常に似ています.ただし、markを生成しなくてもいいです.頭の他のフォーマットは同じです.
最初のバイトですか?それとも固定ですか?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