
Node
const net = require('net');
const crypto = require('crypto');
const map = new Set();
net.createServer((socket)=>{
//完成tcp握手之后开始传输数据
const header = {};
socket.once('data',(data)=>{
let tmpHeader = data.toString().split('\r\n');
tmpHeader.shift();
tmpHeader.forEach(item=>{
if(item){
let index = item.indexOf(':');
const key = item.substr(0,index);
const value = item.substr(index+1);
header[key.trim().toLocaleLowerCase()] = value.trim();
}
})
if(header.upgrade.toLocaleLowerCase()==='websocket'&&header['sec-websocket-version']=='13'){
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
const socketkey = header['sec-websocket-key']
const hash = crypto.createHash('sha1') // 创建一个签名算法为sha1的哈希对象
hash.update(`${socketkey}${GUID}`) // 将key和GUID连接后,更新到hash
const result = hash.digest('base64') // 生成base64字符串
const responseHeader = `HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-Websocket-Accept: ${result}\r\n\r\n`;
socket.write(responseHeader)
map.add(socket);
socket.on('data',(d)=>{
const decodedData = decodeWsFrame(d);
if (decodedData.opcode === 8) {
//frame-opcode-control = %x8 ; connection close
console.log('close');
socket.end() // 与客户端断开连接
map.delete(socket);
} else {
map.forEach(w=>{
if(w!=socket){
w.write(encodeWsFrame({ payloadData: decodedData.payloadData || "" }))
}
})
}
})
}
})
}).listen(8083);
function decodeWsFrame(data) {
let start = 0;
let frame = {
isFinal: (data[start] & 0x80) === 0x80,//取前-位做&操作
opcode: data[start++] & 0xF,//取后4位做&操作
masked: (data[start] & 0x80) === 0x80,//取前-位做&操作
payloadLen: data[start++] & 0x7F,//取后7位做&操作
maskingKey: '',
payloadData: null
};
// if 0-125, that is the payload length
// If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length
// If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length.
if (frame.payloadLen === 126) {
//如果等于126 ,接下来的两个字节作为长度
//data[3]左移8位 + data[4];
frame.payloadLen = (data[start++] << 8) + data[start++];
} else if (frame.payloadLen === 127) {
//如果等于127 接下来的8个字节作为长度
frame.payloadLen = 0;
for (let i = 7; i >= 0; --i) {
frame.payloadLen += (data[start++] << (i * 8));
}
}
if (frame.payloadLen) {
if (frame.masked) {
const maskingKey = [
data[start++],
data[start++],
data[start++],
data[start++]
];
frame.maskingKey = maskingKey;
// Octet i of the transformed data ("transformed-octet-i") is the XOR of octet i of the
//original data ("original-octet-i") with octet at indexi modulo 4 of the masking key ("masking-key-octet-j"):
// j = i MOD 4
//transformed-octet-i = original-octet-i XOR masking-key-octet-j
frame.payloadData = data
.slice(start, start + frame.payloadLen)//截取数据的长度 单位为字节
.map((byte, idx) => byte ^ maskingKey[idx % 4]);//解密
} else {
frame.payloadData = data.slice(start, start + frame.payloadLen);//不需要解密
}
}
return frame;
}
function encodeWsFrame(data) {//对上面函数的反向编码,不需要mask
const isFinal = data.isFinal !== undefined ? data.isFinal : true,
opcode = data.opcode !== undefined ? data.opcode : 1,
payloadData = data.payloadData ? Buffer.from(data.payloadData) : null,
payloadLen = payloadData ? payloadData.length : 0;
let frame = [];
if (isFinal) frame.push((1 << 7) + opcode);
else frame.push(opcode);
if (payloadLen < 126) {
frame.push(payloadLen);
} else if (payloadLen < 65536) {// 127*2^8 + 127
frame.push(126, payloadLen >> 8, payloadLen & 0xFF);
} else {
frame.push(127);
for (let i = 7; i >= 0; --i) {
frame.push((payloadLen & (0xFF << (i * 8))) >> (i * 8));
}
}
frame = payloadData ? Buffer.concat([Buffer.from(frame), payloadData]) : Buffer.from(frame);
return frame;
}
Html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
p.me::after{
content: 'M';
margin-left:5px;
}
p.others::before{
content: 'U';
margin-right: 5px;
}
p.me{
text-align: right;
}
p.me::after,p.others::before{
display: inline-block;
width:30px;
height: 30px;
color:coral;
border:1px solid #999;
line-height: 30px;
text-align: center;
border-radius: 100px;
}
.messages{
width: 500px;
height: 800px;
border: 1px solid #eee;
padding: 10px;
}
</style>
</head>
<body>
<input type="text" id="message-input" />
<button id="submit">提交</button>
<div id="app" class="messages">
</div>
<script>
function addMessage(className,data){
const p = document.createElement('p')
p.innerHTML = data;
p.classList.add(className);
document.querySelector('#app').appendChild(p);
}
//let ws = new WebSocket("ws://localhost:8181");
let ws = new WebSocket("ws://localhost:8083");
let messageInput = document.querySelector('#message-input');
document.querySelector('#submit').addEventListener('click',()=>{
console.log(messageInput.value);
addMessage('me',messageInput.value);
ws.send(messageInput.value);
messageInput.value = "";
},false);
ws.onopen= function(){
console.log("start");
}
ws.onmessage = function(e){
console.log("client:接受到服务端的消息"+e.data);
addMessage('others',e.data);
}
</script>
</body>
</html>