Websocket закрывается при сообщении клиента [JavaScript / Node.js] - PullRequest
0 голосов
/ 18 июня 2019

Я создаю простой сервер 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?

1 Ответ

1 голос
/ 11 июля 2019

Решил это два дня назад, не понимая, что смогу опубликовать свой собственный ответ (еще новость в размещении здесь, извините!)

Большая часть моего понимания сокетов Node.js пришла из сетевой документации. В этом есть пример взаимодействия сервера и клиента. Команда pipe() используется после записи на стороне сервера, поэтому я предположил, что это было необходимо при записи в сокет-клиент.

Это не требуется и фактически не должно использоваться. Примером является эхо-сервер, поэтому каждое сообщение, которое клиент отправляет на сервер, будет ретранслировано обратно клиенту. Этот пост помог мне с этим, но я немного злюсь, потому что я пытался следовать этому совету раньше, и он перестал работать, когда я удалил команды канала. Если определение безумия - «попробовать что-то еще и ожидать других результатов», тогда бросьте меня в психушку.

TL, DR;

Запись в сокет оказалась проще, чем я думал:

// Expected:
socket.write('blah blah blah');
socket.pipe(socket);

// Reality
socket.write('blah blah blah');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...