Websocket在客户端消息[JavaScript / Node.js]上关闭

时间:2019-06-18 01:01:24

标签: node.js google-chrome sockets firefox websocket

我正在创建一个简单的Node.js WebSocket服务器,但是在初次握手后遇到了问题。

在一开始,我只是使用chrome和命令行在HTML5 Websocket和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连接相同(我添加了一个事件侦听器以在Windows上发送消息)客户端上的套接字打开)。有谁知道为什么它只能在Firefox中使用?

1 个答案:

答案 0 :(得分:1)

这两天前解决了,但没意识到我可以发表自己的答案(对这是新的回答,对不起!)

我对Node.js套接字的很多理解来自网络文档。在此,有一个服务器和客户端交互的示例。 pipe()命令是在服务器端写入后使用的,因此我认为写入套接字客户端是必需的。

这不是必需的,实际上也不应使用。该示例是回显服务器,因此客户端发送到服务器的每条消息都将中继回客户端。这篇文章对我有帮助,但是我有点生气,因为我以前尝试遵循该建议,并且在删除管道命令时它停止工作。如果精神错乱的定义是“再次尝试并期望得到不同的结果”,那么就把我丢进懒散的垃圾箱。

TL,DR;

写套接字比我想象的要容易

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

// Reality
socket.write('blah blah blah');