Я создаю простой сервер Node.js WebSocket, но после первоначального рукопожатия у меня возникает проблема.
Вначале я использовал только Chrome и командную строку для мониторинга ивперед между веб-сокетом HTML5 и сервером Node.js.Для реализации протокола потребовалось немного времени, но я только что закончил базовую версию декодирования сообщений на стороне сервера.Однако мне было трудно, потому что всякий раз, когда я звонил ws.send('some kind of message')
, веб-сокет закрывался на стороне клиента.Если посмотреть на вкладку «Сеть» 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?