草庐IT

Node.js实现WebSocket

上庸者-不服周 2023-07-11 原文

一、WebSocket出现的原因

1、Http协议发布REST API 的不足:

每次请求响应完成之后,服务器与客户端之间的连接就断开了,如果客户端想要继续获取服务器的消息,必须再次向服务器发起请

求。这显然无法适应对实时通信有高要求的场景。

2、改善http的不足:Web通信领域出现了一些其他的解决方案,如轮询、长轮询、服务器推送事件、WebSocket

(1)轮询:就是重复发送新的请求到服务器。如果服务器没有新的数据,就发送适当的指示并关闭连接。然后客户端等待一段时间

(比如间隔一秒),再发送另一个请求。这种实现方式相对比较简单,无须做过多的更改。但缺点是轮询的间隔过长,会导致用户不能及

时接收到更新的数据;轮询时间过短,会导致查询请求过多,增加服务器端的负担。

(2)长轮询:客户端发送一个请求到服务器,如果服务器端没有新的数据,就保持这个连接直到有数据。一旦服务器端有了数据

(消息)给客户端,它就使用这个连接发送数据给客户端,接着连接关闭

(3)服务器推送事件:Server-Sent Events(SSE),SSE通常重用一个连接处理多个消息(事件)。SSE还定义了一个专门的媒体类

型,用于描述一个从服务端发送到客户端的简单格式。

(4)WebSocket:提供了一个真正的全双工连接。发起者是一个客户端,发送一个带特殊HTTP头的请求到服务端,通知服务器。

该方案的优点是属于html5标准,已经被大多数浏览器支持,而且是真正的全双工,性能比较好,其缺点是实现起来比较复杂,需要对ws

协议专门处理。

二、Node使用ws创建WebSocket服务器

1、Node.js原生API没有提供对WebSocket的支持,需要安装第三方包才能使用WebSocket功能

2、ws模块:是一个用于支持WebSocket客户端和服务器的框架。它易于使用,功能强大,且不依赖于其他环境

3、安装ws:npm install ws

4、创建WebSocket服务器:

 //创建一个WebSocket服务器,在8080端口启动
 const WebSocket = require('ws')
 const server = new WebSocket.Server({port:8080})

5、WebSocket.Server(options[,callback])方法中options对象所支持的参数

(1)host:绑定服务器的主机名

(2)port:绑定服务器的端口号

(3)backlog:挂起连接队列的最大长度

(4)server:预先创建的node.js http/s服务器

(5)verifyClient:可用于验证传入连接的函数

(6)handleProtocols:可用于处理WebSocket子协议的函数

(7)path:仅接受与此路径匹配的连接

(8)noServer:不启用服务器模式

(9)clientTracking:指定是否跟踪客户端

(10)perMessageDeflate:启用/禁用消息压缩

(11)maxPayload:允许的最大消息大小(以字节为单位)

三、监听连接:ws通过connection事件来监听连接

server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 })
 //只要有WebSocket连接到该服务器,就会触发'connection'事件;req对象可以用来获取客户端的信息,如ip、端口号
 //获取所有已连接的客户端信息,则可以使用server.clients数据集

四、发送数据:ws通过send()方法来发送数据

 /* send(data [,options][,callback])
       data:发送的数据
       options对象:
         (1)compress:指定数据是否需要压缩。默认为true
         (2)binary:指定数据是否通过二进制传送。默认是自动检测
         (3)mask:指定是否应遮罩数据。
         (4)fin:指定数据是否为消息的最后一个片段。默认为true
 */
 server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 })

五、接收数据:ws通过message事件来接收数据。当客户端有消息发送给服务器时,服务器就能够触发该消息

server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 ​
     ws.on('message',function incoming(message){
         console.log('received: %s from %s',message,clientName)
         server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
         })
     })
 })

六、准备的状态:ws中WebSocket类具有以下4中准备状态

1、CONNCETION:值为0,表示连接还没有打开

2、OPEN:值为1,表示连接已经打开,可以通信了

3、CLOSING:值为2,表示连接正在关闭

4、CLOSED:值为2,表示连接已经关闭

server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
 })

七、关闭WebSocket服务器:通过监听close事件关闭服务器

 server.on('close',function close(){
     console.log('disconnected')
 })

案例

1、服务器端:server.js

const WebSocket = require('ws')
 ​
 const server = new WebSocket.Server({port:8080})
 ​
 server.on('open',function open(){
     console.log('connected')
 })
 ​
 server.on('close',function close(){
     console.log('disconnected')
 })
 ​
 server.on('connection',function connection(ws,req){
     const ip = req.socket.remoteAddress
     const port = req.socket.remotePort
     const clientName = ip + port
 ​
     console.log('%s is connected ',clientName)
 ​
     ws.send('Welcome ' + clientName)
 ​
     ws.on('message',function incoming(message){
         console.log('received: %s from %s',message,clientName)
         server.clients.forEach(function each(client){
             if(client.readyState === WebSocket.OPEN){
                 client.send(clientName +" -> " + message)
             }
         })
     })
 })

2、客户端:

<!DOCTYPE html>
 <html lang="en">
 <head>
     <meta charset="UTF-8">
     <meta http-equiv="X-UA-Compatible" content="IE=edge">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>Document</title>
 </head>
 <body>
     <script>
         var socket
         if (!window.WebSocket) {
             window.WebSocket = window.MozWebSocket
         }
         if(window.WebSocket){
             socket = new WebSocket("ws://localhost:8080/ws")
             socket.onmessage = function(event){
                 var ta = document.getElementById('responseTest')
                 ta.value = ta.value + '\n' + event.data
             }
             socket.onopen = function(event) {
                 var ta = document.getElementById('responseTest')
                 ta.value = '连接开启!'
             }
 ​
             socket.onclose = function(event) {
                 var ta = document.getElementById('responseTest')
                 ta.value = '连接关闭!'
             }
         }else{
             alert('你的浏览器不支持WebSocket')
         }
 ​
         function send(message){
             if(!window.WebSocket){
                 return
             }
             if(socket.readyState === WebSocket.OPEN){
                 socket.send(message)
             }else{
                 alert('连接没有开启')
             }
         }
     </script>
     <form onsubmit="return false">
         <h3>WebSocket 聊天室:</h3>
         <textarea  id="responseTest" style="width: 500px;height: 300px;"></textarea>
         <br>
         <input type="text" name="message" style="width: 300px;" value="Welcome to woniuxy.com">
         <input type="button" value="发送消息" onclick="send(this.form.message.value)">
         <input type="button" value="清空聊天记录" onclick="javascript:document.getElementById('responseTest').value=''">
     </form>
 </body>
 </html>

有关Node.js实现WebSocket的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  5. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  6. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  7. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  8. ruby - Faye WebSocket,关闭处理程序被触发后重新连接到套接字 - 2

    我有一个super简单的脚本,它几乎包含了FayeWebSocketGitHub页面上用于处理关闭连接的内容:ws=Faye::WebSocket::Client.new(url,nil,:headers=>headers)ws.on:opendo|event|p[:open]#sendpingcommand#sendtestcommand#ws.send({command:'test'}.to_json)endws.on:messagedo|event|#hereistheentrypointfordatacomingfromtheserver.pJSON.parse(event.d

  9. ruby - 实现k最近邻需要哪些数据? - 2

    我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项

  10. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

随机推荐