• nodejs之websocket


      1 var events = require("events");
      2 var http = require("http");
      3 var crypto = require("crypto");
      4 var util = require("util");
      5 //opcodes for WebSocket frames
      6 //http://tools.ietf.org/html/rfc6455#section-5.2
      7 var opcodes = 
      8 {
      9     TEXT:1,
     10     BINARY:2,
     11     CLOSE:8,
     12     PING:9,
     13     PONG:10
     14 };
     15 var WebSocketConnection = function(req,socket,upgradeHead)
     16 {
     17     var self = this;
     18     var key = hashWebSocketKey(req.headers["sec-websocket-key"]);
     19     //handshake response
     20     //http://tools.ietf.org/html/rfc6455#section-4.2.2
     21     socket.write('HTTP/1.1 101 Web Socket Protocol Handshake
    '+
     22     'Upgrade:WebSocket
    '+
     23     'Connection:Upgrade
    '+
     24     'sec-websocket-accept: '+key+'
    
    ');
     25     
     26     socket.on("data",function(buf)
     27     {
     28         self.buffer = Buffer.concat([self.buffer,buff]);
     29         while(self._processBuffer())
     30         {
     31             //process buffer while it contains complete frames
     32         }
     33     });    
     34     
     35     socket.on("close",function(hac_error)
     36     {
     37         if(!self.closed)
     38         {
     39             self.emit("close",1006);
     40             self.closed = true;
     41         }
     42     });
     43     // initialize connection state
     44     this.socket = socket;
     45     this.buffer = new Buffer(0);
     46     this.colsed = false;
     47     
     48 }
     49 
     50 util.inherits(WebSocketConnection,events.EventEmitter);
     51 // Send a text or binary message on the WebSocket connection
     52 
     53 WebSocketConnection.prototype.send = function(obj)
     54 {
     55     var opcode;
     56     var payload;
     57     if(Buffer.isBuffer(obj))
     58     {
     59         opcode = opcodes.BINARY;
     60         payload = obj;
     61     }
     62     else
     63     {
     64         throw new Error("Cannot send object. Must be string or Buffer");
     65     }
     66     this._doSend(opcode,payload);
     67 }
     68 //Close the WebSocket connection
     69 WebSocketConnection.prototype.close = function(code,reason)
     70 {
     71     var opcode = opcodes.CLOSE;
     72     var buffer;
     73     //Encode close and reason
     74     if(code)
     75     {
     76         buffer = new Buffer(Buffer.byteLength(reason)+2);
     77         buffer.writeUInt16BE(code,0);
     78         buffer.write(reason,2);
     79     }
     80     else
     81     {
     82         buffer = new Buffer(0);
     83     }
     84     this._doSend(opcode,buffer);
     85     this.close = true;
     86     
     87 }
     88 //process incoming bytes
     89 WebSocketConnection.prototype._processBuffer = function()
     90 {
     91     var buf = this.buffer;
     92     if(buf.length < 2)
     93     {
     94         //insufficient data read
     95         return;
     96     }
     97     
     98     var idx = 2;
     99     var b1 = buf.readUInt8(0);
    100     var fin = b1 & 0x80;
    101     var opcode = b1 & 0x0f;
    102     var b2 = buf.readUInt8(1);
    103     var mask = b2 & 0x80;
    104     var length = b2 & 0x7f;
    105     
    106     if(length > 125)
    107     {
    108         if(buf.length < 8)
    109         {
    110             //insufficient data read
    111             return;
    112         }
    113         
    114         if(length == 126)
    115         {
    116             length = buf.readUInt16BE(2);
    117             idx += 2;
    118         }
    119         else if(length == 127)
    120         {
    121             //discard high 4 bits because this server cannot handle huge lengths
    122             var highBits = buf.readUInt32BE(2);
    123             if(highBits != 0)
    124             {
    125                 this.close(1009,"");
    126             }
    127             length = buf.readUInt32BE(6);
    128             idx += 8;
    129         }
    130     }
    131     
    132     if(buf.length < idx + 4 + length)
    133     {
    134         //insufficient data read
    135         return;
    136     }
    137     
    138     maskBytes = buf.slice(idx,idx+4);
    139     idx += 4;
    140     var payload = buf.slice(idx,idx+length);
    141     payload = unmask(maskBytes,payload);
    142     this._handleFreme(opcode,payload);
    143     
    144     this.buffer = buf.slice(idx+length);
    145     return true;
    146 }
    147 
    148 WebSocketConnection.prototype._handleFrame = function(opcode,buffer)
    149 {
    150     var payload;
    151     switch(opcode)
    152     {
    153         case opcodes.TEXT:
    154             payload = buffer.toString("utf8");
    155             this.emit("data",opcode,payload);
    156             break;
    157         case opcode.BINARY:
    158             payload = buffer;
    159             this.emit("data",opcode,payload);
    160             break;
    161         case opcode.PING:
    162             //Respond to pings with pongs
    163             this._doSend(opcode.PONG,buffer);
    164             break;
    165         case opcode.PONG:
    166         //Ignore pongs;
    167             break;
    168         case opcode.CLOSE:
    169             //Parse close and reason
    170             var code,reason;
    171             if(buffer.length >= 2)
    172             {
    173                 code = buffer.readUInt16BE(0);
    174                 reason = buffer.toString("utf8",2);
    175             }
    176             this.close(code,reason);
    177             this.emit("close",code,reason);
    178             break;
    179         default:
    180             this.close(1002,"unknown opcode");
    181         
    182     }
    183 }
    184 //Format and send a WebSocket message
    185 WebSocketConnection.prototype._doSend = function(opcode,payload)
    186 {
    187     this.socket.write(encodeMessage(opcode,payload));
    188 }
    189 
    190 var KEY_SUFFIX = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    191 var hashWebSocketKey = function(key)
    192 {
    193     var sha1 = crypto.createHash("sha1");
    194     sha1.update(key+KEY_SUFFIX,"ascii");
    195     return sha1.digest("base64");
    196 }
    197 
    198 var unmask = function(maskBytes,data)
    199 {
    200     var payload = new Buffer(data.length);
    201     for(var i=0;i<data.length;i++)
    202     {
    203         payload[] = maskBytes[i%4] ^ data[i];
    204     }
    205     
    206     return payload;
    207     
    208 }
    209 
    210 var encodeMessage = function(opcode,payload)
    211 {
    212     var buf;
    213     //first byte:fin and opcode
    214     var b1 = 0x80 | opcode;
    215     //always send message as one frame(fin)
    216     //Second byte:mask and length part 1
    217     //Followed by 0,2, or 8 additional bytes of continued length
    218     var b2 = 0;//server does not mask frames
    219     var length = payload.length;
    220     if(length < 126)
    221     {
    222         buf = new Buffer(payload.length + 2 + 0);
    223         //zero extra bytes
    224         b2 |= length;
    225         buf.writeUInt8BE(b1,0);
    226         buf.writeUInt8BE(b1,1);
    227         payload.copy(buf.2);
    228     }
    229     else if(length<(1<<16))
    230     {
    231         buf = new Buffer(payload.length + 2 + 2);
    232         //two bytes extra
    233         b2 |= 126;
    234         buf.writeUInt8BE(b1,0);
    235         buf.writeUInt8BE(b1,1);
    236         //add two tyte length
    237         buf.writeUInt16BE(length,2);
    238         payload.copy(buf,4);
    239     }
    240     else
    241     {
    242         buf = new Buffer(payload.length + 2 + 8);
    243         //eight bytes extra
    244         b2 |= 127;
    245         buf.writeUInt8(b1,0);
    246         buf.writeUInt8(b1,1);
    247         //add eigth byte length
    248         //note:this implementation cannt handle lengths greater than 2^32
    249         //the 32 bit length is prefixed with 0X0000
    250         buf.writeUInt32BE(0,2);
    251         buf.writeUInt32BE(length,6);
    252         payload.copy(buf,10);
    253     }
    254     
    255     return;
    256 }
    257 
    258 exports.listen = function(port,host,connectionHandler)
    259 {
    260     var srv = http.createServer(function(req,res){});
    261 
    262     srv.on('upgrade',function(req,socket,upgradeHead)
    263     {
    264         var ws = new WebSocketConnection(req,socket,upgradeHead);
    265         connectionHandler(ws);
    266     });
    267     srv.listen(port,host);
    268 };
    269 
    270 //echo.js
    271 var websocket = require("./websocket-example");
    272 websocket.listen(9999,"localhost",function(conn)
    273 {
    274     console.log("connection opened");
    275     conn.on("data",function(opcode,data)
    276     {
    277         console.log("message: ",data);
    278         conn.send(data);
    279     });
    280     
    281     conn.on("close",function(code,reason)
    282     {
    283         console.log("connection closed: ",code,reason);
    284         
    285     });
    286     
    287 });
  • 相关阅读:
    “问答回复模块”Java开发文档官方改进版讲解【在线实习·吾研第二期】
    “付费邀请模块”产品原型图评审【在线实习·吾研第三期】
    “学长学姐认证模块”测试用例官方改进版讲解【在线实习·吾研第一期】
    “学长认证模块”Java代码2.0官方版要点讲解【在线实习·吾研第一期】
    “问答评论模块”UI作品评审【在线实习·吾研第二期】
    “认证模块”前端代码1.0评审【在线实习·吾研第一期】
    <<中国专利法详解>>学习笔记(一)
    JS 前端获得时间
    北漂的程序员
    Spring类注入异常
  • 原文地址:https://www.cnblogs.com/wangyonglong/p/7375802.html
Copyright © 2020-2023  润新知