WebSocket 简化版
什么是WebSocket?
让我们开始编程吧
这是我撰写的WebSocket系列文章的第一篇,目标是用最简单的方式解释相关概念。让我们直接进入正题。
什么是WebSocket?
WebSocket 允许用户向服务器发送和接收消息。
简而言之,这是一种客户端和服务器之间的通信方式。
我们先来了解一下这种通信方式,稍后再回到 WebSocket。
客户端和服务器
Web浏览器(客户端)和服务器通过TCP/IP协议进行通信。超文本传输协议(HTTP)是TCP/IP之上的标准应用层协议,支持请求(来自Web浏览器)及其响应(来自服务器)。
这是如何运作的?
让我们一步一步来:
- 客户端(浏览器)向服务器发送请求。
- 连接已建立。
- 服务器返回响应。
- 客户收到回复。
- 连接已关闭。
这就是客户端和服务器之间通信的基本原理。现在让我们仔细看看步骤5。
连接已关闭。
HTTP 请求已完成其目的,不再需要,因此连接已关闭。
如果服务器想要向客户端发送消息呢?
必须成功建立连接才能开始通信。解决方法是客户端需要发送另一个请求来建立连接并接收消息。
客户端如何知道服务器想要发送消息?
考虑以下示例:
一位顾客饿极了,在网上订了一些食物。他每秒发出一次请求,以查询订单是否已准备好。
0 秒:食物准备好了吗?(顾客)
0 秒:没有,请稍等。(服务员)
1 秒:食物准备好了吗?(顾客)
1 秒:没有,请稍等。(服务员)
2 秒:食物准备好了吗?(顾客)
2 秒:没有,请稍等。(服务员)
3 秒:食物准备好了吗?(顾客)
3 秒:先生,您的餐点已准备好。(服务员)
这就是所谓的HTTP轮询。
客户端反复向服务器发出请求,并检查是否有消息要接收。
如您所见,这种方法效率不高。我们浪费了不必要的资源,而且请求失败的数量也令人头疼。
有什么办法可以解决这个问题吗?
是的,有一种轮询技术的变体可以克服这种缺陷,它被称为长轮询。
长轮询的基本原理是向服务器发出 HTTP 请求,然后保持连接打开,以便服务器在稍后的时间(由服务器决定)做出响应。
考虑上述示例的长轮询版本:
0秒:食物准备好了吗?(顾客)
3秒:是的先生,这是您的餐点。(服务员)
太好了,问题解决了。
其实不然。虽然长轮询确实有效,但它会消耗大量的 CPU、内存和带宽(因为我们需要占用资源来保持连接)。
现在该怎么办?情况似乎有点失控了。让我们再次求助于我们的救星:WebSocket。
为什么选择 WebSocket?
如您所见,轮询和长轮询都是模拟客户端和服务器之间实时通信的相当昂贵的选择。
正是由于这种性能瓶颈,你才应该改用 WebSocket。
WebSocket 不需要你发送请求就能响应。它们支持双向数据流,所以你只需要监听数据即可。
你只需监听服务器,当有货时它会给你发送消息。
我们来看看WebSocket的性能方面。
资源消耗
下图显示了在相对常见的用例中 WebSocket 与长轮询之间的带宽消耗差异: 差异巨大(对于相对较高的请求数量)。
速度
以下是每个连接每秒处理 1 个、10 个和 50 个请求的结果: 正如您所见,使用 Socket.io 处理单个请求的速度大约慢 50%,因为需要先建立连接。对于 10 个请求,这种开销虽然较小,但仍然很明显。当同一连接处理 50 个请求时,Socket.io 的速度已经快了 50%。为了更好地了解峰值吞吐量,我们将使用更大的请求数(500 个、1000 个和 2000 个)来测试每个连接的吞吐量:

从这里可以看出,HTTP 基准测试的峰值请求速度约为每秒 950 次,而 Socket.io 每秒可处理约 3900 次请求。效率很高,对吧?
注意:Socket.io 是一个用于实时 Web 应用程序的 JavaScript 库。它内部实现了 WebSocket。您可以将其视为 WebSocket 的一个封装,它提供了更多功能(本系列下一篇博文将详细介绍 Socket.io)。
WebSocket 的工作原理是什么?
以下是建立 WebSocket 连接的步骤。
- 客户端(浏览器)向服务器发送HTTP请求。
- 通过HTTP协议建立连接。
- 如果服务器支持 WebSocket 协议,它会同意升级连接。这称为握手。
- 握手完成后,初始的 HTTP 连接将被使用相同底层 TCP/IP 协议的 WebSocket 连接所取代。
- 此时,数据可以在客户端和服务器之间自由流动。
让我们开始编程吧
我们将创建两个文件:一个服务器端文件和一个客户端文件。
首先创建一个<html>名为 `<document_name>` 的简单文档,其中client.html包含一个 ` <script><tag>` 标签。让我们看看它的样子:
Client.html
<html>
<script>
// Our code goes here
</script>
<body>
<h1>This is a client page</h1>
</body>
</html>
现在创建另一个文件server.js。导入 HTTP 模块并创建一个服务器。让它监听某个端口port 8000。
这将作为一个简单的http服务器监听某个端口port 8000。我们也来看看它:
服务器.js
//importing http module
const http = require('http');
//creating a http server
const server = http.createServer((req, res) => {
res.end("I am connected");
});
//making it listen to port 8000
server.listen(8000);
运行命令
node server.js开始监听port 8000。
注意:您可以根据需要选择任何端口,我选择 8000 端口并没有什么特别的原因。
客户端和服务器的基本配置已经完成。很简单,对吧?现在让我们开始进入正题。
客户端设置
要构建WebSocket,请使用WebSocket()返回 websocket 对象的构造函数。此对象提供用于创建和管理与服务器的WebSocket 连接的 API 。
简而言之,此 websocket 对象将帮助我们与服务器建立连接,并创建双向数据流,即从两端发送和接收数据。
让我们看看它是如何工作的:
<html>
<script>
//calling the constructor which gives us the websocket object: ws
let ws = new WebSocket('url');
</script>
<body>
<h1>This is a client page</h1>
</body>
</html>
构造WebSocket函数需要一个要监听的 URL。在我们的例子中,'ws://localhost:8000'因为服务器就运行在这个 URL 上。
现在,这可能和您以往使用的方式略有不同。我们没有使用 ` HTTPwebSocket` 协议,而是使用了WebSocket`webSocket` 协议。这将告诉客户端“嘿,我们正在使用 WebSocket 协议”,因此我们使用 `webSocket`'ws://'而不是 ` webSocket` 'http://'。够简单吧?现在让我们实际创建一个 WebSocket 服务器server.js。
服务器设置
我们需要在 Node 服务器中引入一个第三方模块ws来设置服务器WebSocket。
首先,我们将导入该ws模块。然后,我们将创建一个 WebSocket 服务器,并将其传递HTTP给监听的服务器port 8000。
HTTP 服务器正在监听 8000 端口,而 WebSocket 服务器也在监听这个 HTTP 服务器。实际上,它是在监听这个监听器。
现在我们的 WebSocket 正在监听流量port 8000。这意味着一旦客户端可用,它就会尝试建立连接。我们的server.js文件内容将如下所示:-
const http = require('http');
//importing ws module
const websocket = require('ws');
const server = http.createServer((req, res) => {
res.end("I am connected");
});
//creating websocket server
const wss = new websocket.Server({ server });
server.listen(8000);
正如我们之前讨论过的:
WebSocket()构造函数返回一个 websocket 对象,提供用于创建和管理与服务器的WebSocket 连接的 API 。
在这里,该wss对象将帮助我们监听Event特定事件发生时发出的消息。例如,连接已建立、握手已完成或连接已关闭等等。
让我们看看如何监听这些消息:
const http = require('http');
const websocket = require('ws');
const server = http.createServer((req, res) => {
res.end("I am connected");
});
const wss = new websocket.Server({ server });
//calling a method 'on' which is available on websocket object
wss.on('headers', (headers, req) => {
//logging the header
console.log(headers);
});
server.listen(8000);
该方法'on'需要两个参数:事件名称和回调函数。事件名称用于识别要监听/发出的事件,回调函数则指定如何处理该事件。这里,我们只是记录headers事件。让我们看看得到了什么: 这是我们的 HTTP 头部,我希望你仔细观察它,因为这正是幕后发生的情况。让我们来分析一下,以便更好地理解。
- 首先你会注意到我们获得了状态码
101。你可能见过其他状态码,但这个看起来不一样。200这实际上是交换协议的状态码。它的意思是“嘿,我想升级”。201404101 - 第二行显示升级信息。它指定要升级到
websocket协议。 - 实际上,这就是握手过程中发生的情况。浏览器使用
HTTP连接通过协议建立连接HTTP/1.1,然后将Upgrade其传递给websocket协议。
现在我就能明白了。
Headers事件会在握手过程中响应头写入套接字之前发出。这允许您在发送响应头之前对其进行检查/修改。这意味着您可以根据需要修改请求头,使其接受、拒绝或执行任何其他操作。默认情况下,它会接受请求。
类似地,我们还可以添加一个connection在握手完成后触发的事件。连接成功建立后,我们会向客户端发送一条消息。让我们看看具体怎么做:
const http = require('http');
const websocket = require('ws');
const server = http.createServer((req, res) => {
res.end("I am connected");
});
const wss = new websocket.Server({ server });
wss.on('headers', (headers, req) => {
//console.log(headers); Not logging the header anymore
});
//Event: 'connection'
wss.on('connection', (ws, req) => {
ws.send('This is a message from server, connection is established');
//receive the message from client on Event: 'message'
ws.on('message', (msg) => {
console.log(msg);
});
});
server.listen(8000);
我们也在监听message来自客户端的事件。让我们创建它:
<html>
<script>
let ws = new WebSocket('url');
//logging the websocket property properties
console.log(ws);
//sending a message when connection opens
ws.onopen = (event) => ws.send("This is a message from client");
//receiving the message from server
ws.onmessage = (message) => console.log(message);
</script>
<body>
<h1>This is a client page</h1>
</body>
</html>
浏览器中的显示如下: 第一条日志列出了 WebSocket 对象的所有属性,第二条日志列出了包含特定属性的对象。仔细观察,你会发现我们已经从服务器收到了消息。 服务器日志大致如下: 我们已正确收到客户端消息。这表明我们的连接已成功建立。谢谢!
WebSocketMessageEventdata
结论
综上所述,让我们回顾一下我们学到的内容:
- 我们已经介绍了HTTP服务器的工作原理、轮询和长轮询的概念。
- 什么是WebSocket?为什么我们需要它?
- 我们了解了它们在幕后的工作原理,并对头部有了更深入的了解。
- 我们创建了自己的客户端和服务器,并成功建立了它们之间的连接。
这是 WebSocket 的基础知识及其工作原理。本系列的下一篇文章将更socket.io详细地介绍其工作原理。我们还将探讨为什么socket.io在仅使用原生方法就能正常工作的情况下,我们仍然需要WebSocket()WebSocket。既然我们可以成功地发送和接收消息,为什么还要使用臃肿的库呢?
如果您觉得这篇文章有用,请分享,并敬请期待下一篇文章。Shad
。
参考
- WebSocket - Web API | MDN: https: //developer.mozilla.org/en-US/docs/Web/API/WebSocket
wsNode 服务器模块 | 文档:https://github.com/websockets/ws/blob/HEAD/doc/ws.md#event-headers