Node.js的Net套接字+WebSocket,无需使用Socket.IO

18

我想用nodejs创建类似聊天的东西。 我是nodejs的新手,想在不使用socket.io的情况下创建它(我想学习它的工作原理)。 这是我正在使用的代码。

var http = require('http');
var net = require('net');


var server = http.createServer(function(req,res){

    res.writeHead(200,{'content-type' : 'text/html'});
    res.write('<a href="./lol/">lol</a><br>');
    res.end('hello world: '+req.url);



    var client = new net.Socket();
    client.connect('7001', '127.0.0.1', function() {

        console.log('CONNECTED TO: ');
        // Write a message to the socket as soon as the client is connected, the server will receive it as message from the client 
        client.write('I am Chuck Norris!');

    });

    // Add a 'data' event handler for the client socket
    // data is what the server sent to this socket
    client.on('data', function(data) {

        console.log('DATA: ' + data);
        // Close the client socket completely
        client.destroy();

    });

    // Add a 'close' event handler for the client socket
    client.on('close', function() {
        console.log('Connection closed');
    });
    //req.

});
server.listen(7000);


require('net').createServer(function (socket) {
    console.log("connected");

    socket.on('data', function (data) {
        console.log(data.toString());
    });
}).listen(7001);

一切都很好(我想)。 当我打开localhost:7000时,我在节点CMD消息中看到了关于“CONNECTED TO:”、“connected”和“I am Chack Norris”的内容。 之后,我尝试在浏览器控制台中输入:

var conn = new WebSocket('ws://localhost:7001/');

同样没有错误,但当我尝试这行代码时:

conn.send('lol');

我遇到了一个错误:"Uncaught DOMException: Failed to execute 'send' on 'WebSocket': Still in CONNECTING state.(…)"

一段时间后,我又遇到了另一个错误:"WebSocket connection to 'ws://localhost:7001/' failed: WebSocket opening handshake timed out"

也许我的代码有问题,但我已经尝试了在谷歌上找到的一切方法。有人能帮我解决这个问题吗?

2个回答

30
如果您想创建自己的webSocket服务器,可以从浏览器接收webSocket连接,您需要在服务器上实现webSocket协议。它不仅仅是一个简单的套接字连接。它有一个启动序列,从HTTP连接开始,然后“升级”到webSocket协议,包括安全信息的交换,然后对于所有通过webSocket发送的数据都有一个webSocket帧格式。您不能只是在webSocket上发送纯文本。
您可以在这里看到webSocket协议的样子:编写WebSocket服务器。除非您真的只是为了学习而想制作自己的webSocket服务器,否则我真的建议您获取一个已经完成了所有烦琐协议工作的现有模块。
然后,socket.io库是建立在webSocket协议之上的,在其之上添加了其他功能和消息格式。
为了让您了解webSocket连接的方式,这里是一个典型的连接序列:
浏览器发送连接请求:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

服务器响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

然后,双方都切换到webSocket协议,该协议具有如下数据分帧格式:

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

此外,还有用于保持连接的ping和pong数据包,以及用于大型数据包和片段的协议和客户端/服务器可以协商子协议。


谢谢!那非常有帮助! - Illia Moroz
2
感谢@jfriend00,我在nodejs的net模块和http升级之间花费了很多时间,以避免使用第三方库,你的回答让我相信现在应该停止这样做,开始使用现成的socket库,谢谢。 - Hasan A Yousef
@jfriend00,那是一个非常棒的答案。然而,有一件事情我不太明白。当我从客户端连接到WebSocket.org进行回声测试时,我从来没有向他们的服务器发送HTTP GET请求。我所做的就是用new WebSocket('wss://echo.websocket.org')打开一个WebSocket。我的问题是:只有服务器需要实现上面的升级吗?是否设置了GET /chat的HTTP路由? 另外,您能详细说明一下Sec-WebSocket-Accept头部值吗?对我来说,它看起来不像是静态的Base-64字符串。 - Cody
2
@Cody - new WebSocket('wss://echo.websocket.org') 会帮你完成 HTTP 请求。它会为你执行整个 WebSocket 协议,包括初始的 HTTP 请求。通常情况下,你不会自己实现 WebSocket 服务器,而是使用一个库来挂钩到现有的 HTTP 服务器上,监听传入的请求并设置 upgrade: websocket 的头部,然后从那里接管该请求。 - jfriend00
谢谢,@jfriend00。所以听起来只有一个在GET /chat的路由/控制器响应这些头部信息,但之后除了心跳/保持活动的实现外没有太多要处理的。我不需要担心在那之后解析数据包,对吗?虽然好奇驱使我问一下,但我应该如何预期接收和处理消息呢?您能提供任何相关信息吗?(顺便说一句,非常感谢您在这方面的所有知识;超级有帮助!) - Cody
1
@cody - 浏览器内置的WebSocket实现会为您处理所有数据包操作和协议相关的事项。您只需以所需格式发送/接收数据即可。WebSocket本身没有任何特定的保持活动或心跳,因此如果您需要这些功能,则需要在WebSocket协议之上自行实现,通过定期发送自己的数据来完成。 - jfriend00

-1

如上所建议,除非你确实需要或者想要,最好不要编写自己的socket服务器。

一个带有简单接口的优秀socket服务器是ws https://github.com/websockets/ws

只需要几行代码即可在服务器端开始发送和接收消息。

同时,在客户端,你无需使用库,只需要:

new WebSocket('wss//echo.websocket.org')

阅读更多


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接