客户端代码
<!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;
}
扩展阅读
- segmentfault.com/a/119000001…
- www.cnblogs.com/tugenhua070…