我们用 Javascript 做的很多工作都涉及到从服务器来回发送信息。您可能熟悉API的概念,它以特定格式将数据发送到服务器或网站,以获取特定的响应。
这些被称为REST API。虽然有用,但它们并不擅长持续的数据流。如果您尝试使用 REST API 实时执行某些操作,那么您将度过一段糟糕的时光。幸运的是,如果我们想要与用户建立实时连接,我们有一个替代方案,称为WebSockets。
WebSocket 的工作原理#
对于本教程,我们假设您熟悉Node.JS。WebSockets 本质上是在服务器和您的计算机之间建立的持续连接。当你访问一个网站时,它可以向服务器发送一个GET
请求,在用户和服务器之间发起一个WebSocket连接。
WebSocket 与 REST API
如果用户离开网站,则连接被切断,因此用户只要继续使用网站,就只能访问 WebSocket。
WebSocket 可以保持打开状态多长时间?
一旦创建了 WebSocket,理论上它可以永远保持打开状态。有几个例外:
- 服务器宕机– 这将破坏 WebSocket,但我们可以尝试重新连接到它。
- 停电或互联网连接问题 – 如果用户的互联网停止,连接将中断。
- 不活动– 如果用户不通过 WebSocket 交互或发送数据,连接不可避免地会超时。
因此,当我们设计我们的 WebSocket 时,我们需要考虑如果用户的连接由于某种原因停止,我们如何重新连接到它们,以免中断用户的体验。
制作一个 WebSocket#
因此,WebSocket 由两部分组成——服务器和用户正在使用的本地机器。对于我们正在做的事情,我们将使用 Node.JS 作为我们的服务器,但其他语言也支持 WebSockets。
当用户访问我们的网站时,我们会加载一个带有一些 Javascript 的文件,其中包含到我们的 WebSocket 的连接字符串。同时,在我们的后端,我们将设置用户将连接到的 WebSocket。如下图所示:
第 1 步:创建我们的服务器#
让我们首先为 WebSocket 连接创建 Node.JS Web 服务器。为此,我们将使用带有一个名为express-ws
. 这个额外的包将允许我们以与 expressws
相同的方式使用。get
如果您没有安装 Node.JS,您需要先通过此链接进行安装。安装后,创建一个名为server-websocket
. 打开终端,然后使用cd
进入该文件夹(如果您不知道cd
,请在此处阅读我们的文章!)。
进入文件夹后,您需要安装依赖包。通过运行以下每个命令开始安装依赖项:
npm i express
npm i express-ws
npm i path
npm i url
之后,创建一个名为index.js
并放入以下代码的文件:
// Import path and url dependencies
import path from 'path'
import { fileURLToPath } from 'url'
// Get the directory and file path
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Import express, expressWs, and http
import express from 'express'
import expressWs from 'express-ws'
import http from 'http'
// Our port
let port = 3000;
// App and server
let app = express();
let server = http.createServer(app).listen(port);
// Apply expressWs
expressWs(app, server);
app.use(express.static(__dirname + '/views'));
// Get the route /
app.get('/', (req, res) => {
res.status(200).send("Welcome to our app");
});
// This lets the server pick up the '/ws' WebSocket route
app.ws('/ws', async function(ws, req) {
// After which we wait for a message and respond to it
ws.on('message', async function(msg) {
// If a message occurs, we'll console log it on the server
console.log(msg);
// Start listening for messages
});
});
最后一个子句 ,app.ws
指的是 WebSocket,这就是我们将尝试在前端连接的内容。目前,只要 WebSocket 从前端收到一条消息,它就只记录一条消息。让我们改变它,让它发回一些东西:
// Get the /ws WebSocket route
app.ws('/ws', async function(ws, req) {
ws.on('message', async function(msg) {
// What was the message?
console.log(msg);
// Send back some data
ws.send(JSON.stringify({
"append" : true,
"returnText" : "I am using WebSockets!"
}));
});
});
现在,只要这个 WebSocket 连接接收到数据,它就会发回包含append
and的对象returnText
。我们还将控制台记录服务器收到的消息。
然后我们可以在前端操作这个对象,为用户显示或更改视图。
将该文件保存在您的websocket-server
文件夹中为index.js
. 然后从您的终端,在websocket-server
文件夹中,运行以下命令:
node index.js
第 2 步:在前端连接#
现在我们有一个正在运行的 websocket 服务器,但无法连接到它。我们想要实现这样的目标:
- 一位用户访问我们的网站。
- 我们从 Javascript 文件启动 WebSocket 连接。
- 用户成功连接到 WebSocket,一旦连接,就会向 WebSocket 发送消息。
- 然后,我们可以将数据发送回用户,因为他们与我们的 WebSocket 服务器建立了实时连接,从而创建了实时数据交换。
对于我们的演示,让我们从创建两个文件开始:index.html
和local.js
,这两个文件都是前端文件。接下来,让我们将以下内容放入我们的index.html
文件中:
<script src="local.js"></script>
<p>Welcome to WebSockets. Click here to start receiving messages.</p>
<button id="websocket-button">Click me</button>
<div id="websocket-returns"></div>
接下来,我们需要通过local.js
文件将用户连接到我们的 WebSocket。我们的local.js
文件最终将如下所示:
// @connect
// Connect to the websocket
let socket;
// This will let us create a connection to our Server websocket.
// For this to work, your websocket needs to be running with node index.js
const connect = function() {
// Return a promise, which will wait for the socket to open
return new Promise((resolve, reject) => {
// This calculates the link to the websocket.
const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:')
const port = 3000;
const socketUrl = `${socketProtocol}//${window.location.hostname}:${port}/ws/`
// Define socket
// If you are running your websocket on localhost, you can change
// socketUrl to 'http://localhost:3000', as we are running our websocket
// on port 3000 from the previous websocket code.
socket = new WebSocket(socketUrl);
// This will fire once the socket opens
socket.onopen = (e) => {
// Send a little test data, which we can use on the server if we want
socket.send(JSON.stringify({ "loaded" : true }));
// Resolve the promise - we are connected
resolve();
}
// This will fire when the server sends the user a message
socket.onmessage = (data) => {
console.log(data);
// Any data from the server can be manipulated here.
let parsedData = JSON.parse(data.data);
if(parsedData.append === true) {
const newEl = document.createElement('p');
newEl.textContent = parsedData.returnText;
document.getElementById('websocket-returns').appendChild(newEl);
}
}
// This will fire on error
socket.onerror = (e) => {
// Return an error if any occurs
console.log(e);
resolve();
// Try to connect again
connect();
}
});
}
// @isOpen
// check if a websocket is open
const isOpen = function(ws) {
return ws.readyState === ws.OPEN
}
// When the document has loaded
document.addEventListener('DOMContentLoaded', function() {
// Connect to the websocket
connect();
// And add our event listeners
document.getElementById('websocket-button').addEventListener('click', function(e) {
if(isOpen(socket)) {
socket.send(JSON.stringify({
"data" : "this is our data to send",
"other" : "this can be in any format"
}))
}
});
});
这可能看起来很多,但让我们分解一下。在我们的连接函数中,我们首先构建我们的 WebSocket URL。这可以简单地写成ws://localhost:3000/
,因为我们的 WebSocket 服务器在端口 3000 上运行。上面,它被配置为在您使用HTTP
或时自动调整HTTPS
。
然后我们将一些事件监听器传递给我们新创建的WebSocket
. 我们所有的事件监听器以及连接到 WebSocket 服务器的 URL 都位于我们的connect()
函数中——其目的是连接到我们的 WebSocket 服务器。
我们的WebSocket
事件监听器如下所示:
socket.onopen
– 如果连接成功并打开,则会触发。socket.onmessage
– 每当服务器向我们发送消息时,都会触发。append
在我们的示例中,如果用户收到设置为的数据,我们将在用户的 HTML 中附加一个新元素true
。socket.onerror
– 如果连接失败或发生错误,则会触发。
现在我们有了一个connect()
可以让我们连接到 Websocket 服务器的函数,我们必须运行它。我们首先等待页面加载,使用DOMContentLoaded
. connect()
然后我们使用该函数连接到我们的 websocket 。
最后,我们在 HTML 页面上的按钮上附加一个事件监听器,当单击该按钮时,现在将使用该socket.send()
函数向我们的 WebSocket 发送一些数据。当服务器接收到这些数据时,它会发回自己的数据,因为服务器 ‘ message
‘ 事件将触发。
// When the document has loaded
document.addEventListener('DOMContentLoaded', function() {
// Connect to the websocket
connect();
// And add our event listeners
document.getElementById('websocket-button').addEventListener('click', function(e) {
if(isOpen(socket)) {
socket.send(JSON.stringify({
"data" : "this is our data to send",
"other" : "this can be in any format"
}))
}
});
});
由于每当新数据来自 WebSocket 服务器时我们的onmessage
事件处理程序就会触发,因此单击该按钮会导致 WebSocket 服务器向我们发送回一条消息 – 从而创建一个新的 HTML元素。WebSocket
<p>
结论#
现在我们有一个正常运行的 websocket,它允许您将数据发送到服务器,然后返回给用户。这种双向连接可用于允许用户与服务器本身进行交互,甚至可以根据需要向其他用户发送数据。以下是一些有用的链接,可帮助您了解有关 WebSocket 的更多信息: