原生webSocket分析

109 阅读1分钟

客户端代码


<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<button id="btn">按钮</button>	
</body>
<script src="https://cdn.bootcss.com/jquery/2.0.1/jquery.js"></script>
<script type="text/javascript">
	var ws = new WebSocket('ws://localhost:8080');
	ws.onopen = function () {
		console.log('连接');
	}

	$("#btn").click(function(event) {
		ws.send('你好吗?');
	});

</script>
</html>

服务器代码


const http=require('http');
const net=require('net');         //TCP     原生Socket
const crypto=require('crypto');

function decodeDataFrame(e) {

  var i = 0, j, s, arrs = [],
    frame = {
      // 解析前两个字节的基本数据
      FIN: e[i] >> 7,
      Opcode: e[i++] & 15,
      Mask: e[i] >> 7,
      PayloadLength: e[i++] & 0x7F
    };

    // 处理特殊长度126和127
    if (frame.PayloadLength === 126) {
      frame.PayloadLength = (e[i++] << 8) + e[i++];
    }
    if (frame.PayloadLength === 127) {
      i += 4; // 长度一般用4个字节的整型,前四个字节一般为长整型留空的。
      frame.PayloadLength = (e[i++] << 24)+(e[i++] << 16)+(e[i++] << 8) + e[i++];
    }
    // 判断是否使用掩码
    if (frame.Mask) {
      // 获取掩码实体
      frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
      // 对数据和掩码做异或运算
      for(j = 0, arrs = []; j < frame.PayloadLength; j++) {
        arrs.push(e[i+j] ^ frame.MaskingKey[j%4]);
      }
    } else {
      // 否则的话 直接使用数据
      arrs = e.slice(i, i + frame.PayloadLength);
    }
    // 数组转换成缓冲区来使用
    arrs = new Buffer(arrs);
    // 如果有必要则把缓冲区转换成字符串来使用
    if (frame.Opcode === 1) {
      arrs = arrs.toString();
    }
    // 设置上数据部分
    frame.PayloadLength = arrs;
    // 返回数据帧
    return frame;
}


let server=net.createServer(sock=>{
  console.log('连接');

  sock.once('data', function (data) {
  	console.log(data.toString());

  	var httpString = data.toString();

  	var dataArray =httpString.split("\r\n");
  	dataArray = dataArray.slice(1, dataArray.length-2);
  	console.log(dataArray);

  	var rs = [];
  	dataArray.forEach(function (ele, index) {
  		var [key,v] = ele.split(': ');
  		rs[ key.toLowerCase() ] = v;
  	})

  	console.log(rs);

  	// toBase64( sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ) )
  	var key = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
  	var secKey = rs['sec-websocket-key'];

  	var data = crypto.createHash('sha1').update( secKey + key  ).digest('base64');


  	 sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ${data}\r\n\r\n`);

  	console.log( data );

  	sock.on('data', function (req) {
  		// console.log(req.toString());

  		var decode = decodeDataFrame(req);

  		console.log(decode);
  	})




  })


    
});
server.listen(8080);


帧函数

function decodeDataFrame(e) {

  var i = 0, j, s, arrs = [],
    frame = {
      // 解析前两个字节的基本数据
      FIN: e[i] >> 7,
      Opcode: e[i++] & 15,
      Mask: e[i] >> 7,
      PayloadLength: e[i++] & 0x7F
    };

    // 处理特殊长度126和127
    if (frame.PayloadLength === 126) {
      frame.PayloadLength = (e[i++] << 8) + e[i++];
    }
    if (frame.PayloadLength === 127) {
      i += 4; // 长度一般用4个字节的整型,前四个字节一般为长整型留空的。
      frame.PayloadLength = (e[i++] << 24)+(e[i++] << 16)+(e[i++] << 8) + e[i++];
    }
    // 判断是否使用掩码
    if (frame.Mask) {
      // 获取掩码实体
      frame.MaskingKey = [e[i++], e[i++], e[i++], e[i++]];
      // 对数据和掩码做异或运算
      for(j = 0, arrs = []; j < frame.PayloadLength; j++) {
        arrs.push(e[i+j] ^ frame.MaskingKey[j%4]);
      }
    } else {
      // 否则的话 直接使用数据
      arrs = e.slice(i, i + frame.PayloadLength);
    }
    // 数组转换成缓冲区来使用
    arrs = new Buffer(arrs);
    // 如果有必要则把缓冲区转换成字符串来使用
    if (frame.Opcode === 1) {
      arrs = arrs.toString();
    }
    // 设置上数据部分
    frame.PayloadLength = arrs;
    // 返回数据帧
    return frame;
}

扩展阅读

  1. segmentfault.com/a/119000001…
  2. www.cnblogs.com/tugenhua070…