【发布时间】:2019-10-31 14:10:09
【问题描述】:
我正在创建一个简单的 Node.js WebSocket 服务器,但在初次握手后我遇到了问题。
一开始,我只是使用 chrome 和命令行在 HTML5 Websocket 和 Node.js 服务器之间来回监控。实现协议花了一些时间,但我刚刚完成了服务器端消息解码的一个非常基本的版本。然而,我遇到了困难,因为每当我打电话给ws.send('some kind of message') 时,websocket 都会在客户端关闭。查看 DevTools 的网络选项卡,看起来该消息将从客户端发送,并立即获得 (Opcode -1) 的错误响应,并将此错误记录在控制台中:
WebSocket connection to 'ws://localhost:4000/' failed: A server must not mask any frames that it sends to the client.
我已经研究了它的全部含义,但我无法弄清楚为什么我的代码会抛出它。我曾尝试重建它,并在确认后发送测试消息,这很有效。我唯一没有尝试过的是使用不同的浏览器,所以我今天尝试了一下。 它按预期工作。
下面是我所有的相关代码。
库、常量和监听:
const hostname = 'localhost';
const webport = 8080;
const socketport = 4000;
const http = require('http');
const net = require('net');
const mysql = require('mysql');
const rlm = require('readline');
const crypt = require('crypto');
...
server.listen(webport,hostname);
socketServer.listen(socketport,hostname);
HTTP 服务器:
const server = http.createServer(
function(req,res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write("
<html>
<head>
<title>Test Title</title>
</head>
<body>
<h1>Here's the thing</h1>
<p>im baby</p>
</body>
<script>
const ws = new WebSocket('ws://"+hostname+":"+socketport+"');
ws.addEventListener('message',function(data){
console.log(data.data)
});
</script>
</html>
"); // Reformatted for better reading
res.end();
});
网络服务器:
var sockets = new Map();
var socketInfo = {};
const socketDelimiters = {
'Accept-Encoding':',',
'Accept-Language':';',
'Sec-WebSocket-Extensions':'; '
}
const socketServer = net.Server(function(s) {
s.on('data',function(e) {
/*
* If the socket is not registered, read first message as
* the beginning to a handshake
*/
if(sockets.get(s)==null) {
var str = ""+e;
var tempobj = str.split("\r\n");
var newObj = {};
for(var i in tempobj) {
if(tempobj[i].length>0) {
var tempProperty = tempobj[i].split(': ');
if(tempProperty.length>1) {
if(socketDelimiters[tempProperty[0]]!=null){
tempProperty[1] = tempProperty[1].split(
socketDelimiters[tempProperty[0]]);
}
newObj[tempProperty[0]] = tempProperty[1];
} else {
newObj.header = tempProperty;
}
}
}
var protocolReturn = "
HTTP/1.1 101 Switching Protocols\r\n
Upgrade: websocket\r\n
Connection: Upgrade\r\n
Sec-Websocket-Accept: "+createAcceptKey(newObj['Sec-WebSocket-Key'])
+"\r\n\r\n"; //Reformatted for better reading
s.write(protocolReturn);
s.pipe(s);
sockets.set(s,newObj['Sec-WebSocket-Key']);
socketInfo[newObj['Sec-WebSocket-Key']] = {
socket:s,
isReading:false,
message:null,
mask:null,
handshake: newObj
};
s.write(Buffer.from([0x81,0x04,0x74,0x65,0x73,0x74])); // 'test'
s.pipe(s);
} else {
/*
* If the socket is found and registered, decode the incoming message
*/
var firstBytes = e.readUInt16BE(0);
console.log(firstBytes);
var length=((firstBytes & 0x007F)/0x0001);
var FIN = ((firstBytes & 0x8000))!=0;
var opcode = (firstBytes & 0x0F00)/0x0100;
var mask = ((firstBytes & 0x0080)!=0);
if(opcode!=8) {
console.log("end: "+FIN);
console.log("mask: "+mask);
console.log("op code: "+opcode);
console.log("length: "+length);
var mask = [];
for(var i=0; i<4; i++) {
var b = e.readUInt8(2+i);
mask.push(b);
}
var val=[];
for(var i=0; i<length; i++) {
var b = e.readUInt8(6+i) ^ mask[i%4];
val.push(b);
}
var newVal = new Buffer.from(val);
console.log(newVal.toString('utf8'));
}
}
})
// Handles error
s.on('error',function(err) {
console.log(err);
})
// Takes socket out of the socket list on close
s.on('close',function(hasError) {
if(hasError) {console.log("Please see error")}
delete socketInfo[sockets.get(s)];
sockets.delete(s);
});
});
// Generates accept key from given key
function createAcceptKey(key) {
var inKeyHash = crypt.createHash('sha1');
inKeyHash.update(key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
return (inKeyHash.digest('base64'));
}
这一切应该做什么(''表示客户端到服务器)
> [handshake initiation]
< [handshake confirmation]
< test
> [anything the client sends through the console]
/*
*All I do for the client to server bit at the end is go into the console,
* and plug in something like this
*/
ws.send('blah blah blah')
这在 Firefox 中工作得很好,但如上所述,在 chrome 中,它会抛出一个错误,声称服务器在客户端向服务器发送消息的同时发送了一个屏蔽帧。
chrome 读取屏蔽帧而 firefox 不读取是否有原因?
更新:
我现在尝试在不同的浏览器(确切地说是 OBS 浏览器)中使用它,它在服务器端引发与连接 Chrome 相同的错误(我添加了一个事件侦听器来发送消息在客户端打开套接字)。有谁知道为什么它只适用于 Firefox?
【问题讨论】:
-
您应该将您的答案作为答案发布。
-
@JoshLee 刚刚做到了,没想到我能做到。谢谢!
标签: node.js google-chrome sockets firefox websocket