前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于 socket.io 快速实现一个实时通讯应用

基于 socket.io 快速实现一个实时通讯应用

作者头像
用户2356368
发布于 2022-04-07 08:32:48
发布于 2022-04-07 08:32:48
1.7K01
代码可运行
举报
文章被收录于专栏:薄荷前端薄荷前端
运行总次数:1
代码可运行

随着web技术的发展,使用场景和需求也越来越复杂,客户端不再满足于简单的请求得到状态的需求。实时通讯越来越多应用于各个领域。

HTTP是最常用的客户端与服务端的通信技术,但是HTTP通信只能由客户端发起,无法及时获取服务端的数据改变。只能依靠定期轮询来获取最新的状态。时效性无法保证,同时更多的请求也会增加服务器的负担。

WebSocket技术应运而生。

WebSocket概念

不同于HTTP半双工协议,WebSocket是基于TCP 连接的全双工协议,支持客户端服务端双向通信。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

实现

原生实现

WebSocket对象一共支持四个消息 onopen, onmessage, onclose和onerror。

建立连接

通过javascript可以快速的建立一个WebSocket连接:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    var Socket = new WebSocket(url, [protocol] );

以上代码中的第一个参数url, 指定连接的URL。第二个参数 protocol是可选的,指定了可接受的子协议。

同http协议使用http://开头一样,WebSocket协议的URL使用ws://开头,另外安全的WebSocket协议使用wss://开头。

当Browser和WebSocketServer连接成功后,会触发onopen消息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Socket.onopen = function(evt) {};

如果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Socket.onerror = function(evt) { };

当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Socket.onclose = function(evt) { };

收发消息

当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Socket.onmessage = function(evt) { };

send用于向服务端发送消息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Socket.send();

socket

WebSocket是跟随HTML5一同提出的,所以在兼容性上存在问题,这时一个非常好用的库就登场了——Socket.io

socket.io封装了websocket,同时包含了其它的连接方式,你在任何浏览器里都可以使用socket.io来建立异步的连接。socket.io包含了服务端和客户端的库,如果在浏览器中使用了socket.io的js,服务端也必须同样适用。

socket.io是基于 Websocket 的Client-Server 实时通信库。

socket.io底层是基于engine.io这个库。engine.io为 socket.io 提供跨浏览器/跨设备的双向通信的底层库。engine.io使用了 Websocket 和 XHR 方式封装了一套 socket 协议。在低版本的浏览器中,不支持Websocket,为了兼容使用长轮询(polling)替代。

API文档

Socket.io允许你触发或响应自定义的事件,除了connect,message,disconnect这些事件的名字不能使用之外,你可以触发任何自定义的事件名称。

建立连接

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    const socket = io("ws://0.0.0.0:port"); // port为自己定义的端口号
    let io = require("socket.io")(http);
    io.on("connection", function(socket) {})

消息收发

一、发送数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    socket.emit(自定义发送的字段, data);

二、接收数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    socket.on(自定义发送的字段, function(data) {
        console.log(data);
    })

断开连接

一、全部断开连接

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    let io = require("socket.io")(http);
    io.close();

二、某个客户端断开与服务端的链接

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 客户端
    socket.emit("close", {});
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 服务端
    socket.on("close", data => {
        socket.disconnect(true);
    });

room和namespace

有时候websocket有如下的使用场景:1.服务端发送的消息有分类,不同的客户端需要接收的分类不同;2.服务端并不需要对所有的客户端都发送消息,只需要针对某个特定群体发送消息;

针对这种使用场景,socket中非常实用的namespace和room就上场了。

先来一张图看看namespace与room之间的关系:

namespace

服务端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    io.of("/post").on("connection", function(socket) {
        socket.emit("new message", { mess: `这是post的命名空间` });
    });
    
io.of(<span class="hljs-string">"/get"</span>).on(<span class="hljs-string">"connection"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">socket</span>) </span>{
    socket.emit(<span class="hljs-string">"new message"</span>, { <span class="hljs-attr">mess</span>: <span class="hljs-string">`这是get的命名空间`</span> });
});

客户端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // index.js
    const socket = io("ws://0.0.0.0:****/post");
    socket.on("new message", function(data) {
        console.log('index',data);
    }
    
    //message.js
    const socket = io("ws://0.0.0.0:****/get");
    socket.on("new message", function(data) {
        console.log('message',data);
    }
room

客户端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    //可用于客户端进入房间;
    socket.join('room one');
    //用于离开房间;
    socket.leave('room one');

服务端

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    io.sockets.on('connection',function(socket){
        //提交者会被排除在外(即不会收到消息)
        socket.broadcast.to('room one').emit('new messages', data);
        // 向所有用户发送消息
        io.sockets.to(data).emit("recive message", "hello,房间中的用户");      
    }

用socket.io实现一个实时接收信息的例子

终于来到应用的阶段啦,服务端用node.js模拟了服务端接口。以下的例子都在本地服务器中实现。

服务端

先来看看服务端,先来开启一个服务,安装expresssocket.io

安装依赖

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    npm install --Dev express
    npm install --Dev socket.io

构建node服务器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    let app = require("express")();
    let http = require("http").createServer(handler);
    let io = require("socket.io")(http);
    let fs = require("fs");
    
http.listen(port); <span class="hljs-comment">//port:输入需要的端口号</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handler</span>(<span class="hljs-params">req, res</span>) </span>{
  fs.readFile(__dirname + <span class="hljs-string">"/index.html"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, data</span>) </span>{
    <span class="hljs-keyword">if</span> (err) {
      res.writeHead(<span class="hljs-number">500</span>);
      <span class="hljs-keyword">return</span> res.end(<span class="hljs-string">"Error loading index.html"</span>);
    }

    res.writeHead(<span class="hljs-number">200</span>);
    res.end(data);
  });
}

io.on(<span class="hljs-string">"connection"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">socket</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'连接成功'</span>);
    <span class="hljs-comment">//连接成功之后发送消息</span>
    socket.emit(<span class="hljs-string">"new message"</span>, { <span class="hljs-attr">mess</span>: <span class="hljs-string">`初始消息`</span> });
    
});

客户端

核心代码——index.html(向服务端发送数据)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    <div>发送信息</div>
    <input placeholder="请输入要发送的信息" />
    <button onclick="postMessage()">发送</button>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    // 接收到服务端传来的name匹配的消息
    socket.on("new message", function(data) {
      console.log(data);
    });
    
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postMessage</span>(<span class="hljs-params"></span>) </span>{
  socket.emit(<span class="hljs-string">"recive message"</span>, {
    <span class="hljs-attr">message</span>: content,
    <span class="hljs-attr">time</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
  });
  messList.push({
    <span class="hljs-attr">message</span>: content,
    <span class="hljs-attr">time</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
  });
}

核心代码——message.html(从服务端接收数据)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    socket.on("new message", function(data) {
      console.log(data);
    });

效果

实时通讯效果

客户端全部断开连接

某客户端断开连接

namespace应用

加入房间

离开房间

框架中的应用

npm install socket.io-client

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    const socket = require('socket.io-client')('http://localhost:port');

componentDidMount() {
    socket.on(<span class="hljs-string">'login'</span>, (data) =&gt; {
        <span class="hljs-built_in">console</span>.log(data)
    });
    socket.on(<span class="hljs-string">'add user'</span>, (data) =&gt; {
        <span class="hljs-built_in">console</span>.log(data)
    });
    socket.on(<span class="hljs-string">'new message'</span>, (data) =&gt; {
        <span class="hljs-built_in">console</span>.log(data)
    });
}

分析webSocket协议

Headers

请求包

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Accept-Encoding: gzip, deflate
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
    Cache-Control: no-cache
    Connection: Upgrade
    Cookie: MEIQIA_VISIT_ID=1IcBRlE1mZhdVi1dEFNtGNAfjyG; token=0b81ffd758ea4a33e7724d9c67efbb26; io=ouI5Vqe7_WnIHlKnAAAG
    Host: 0.0.0.0:2699
    Origin: http://127.0.0.1:5500
    Pragma: no-cache
    Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
    Sec-WebSocket-Key: PJS0iPLxrL0ueNPoAFUSiA==
    Sec-WebSocket-Version: 13
    Upgrade: websocket
    User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1

请求包说明:

  • 必须是有效的http request 格式;
  • HTTP request method 必须是GET,协议应不小于1.1 如: Get / HTTP/1.1;
  • 必须包括Upgrade头域,并且其值为“websocket”,用于告诉服务器此连接需要升级到websocket;
  • 必须包括”Connection” 头域,并且其值为“Upgrade”;
  • 必须包括”Sec-WebSocket-Key”头域,其值采用base64编码的随机16字节长的字符序列;
  • 如果请求来自浏览器客户端,还必须包括Origin头域 。 该头域用于防止未授权的跨域脚本攻击,服务器可以从Origin决定是否接受该WebSocket连接;
  • 必须包括“Sec-webSocket-Version”头域,是当前使用协议的版本号,当前值必须是13;
  • 可能包括“Sec-WebSocket-Protocol”,表示client(应用程序)支持的协议列表,server选择一个或者没有可接受的协议响应之;
  • 可能包括“Sec-WebSocket-Extensions”, 协议扩展, 某类协议可能支持多个扩展,通过它可以实现协议增强;
  • 可能包括任意其他域,如cookie.

应答包

应答包说明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    Connection: Upgrade
    Sec-WebSocket-Accept: I4jyFwm0r1J8lrnD3yN+EvxTABQ=
    Sec-WebSocket-Extensions: permessage-deflate
    Upgrade: websocket
  • 必须包括Upgrade头域,并且其值为“websocket”;
  • 必须包括Connection头域,并且其值为“Upgrade”;
  • 必须包括Sec-WebSocket-Accept头域,其值是将请求包“Sec-WebSocket-Key”的值,与”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″这个字符串进行拼接,然后对拼接后的字符串进行sha-1运算,再进行base64编码,就是“Sec-WebSocket-Accept”的值;
  • 应答包中冒号后面有一个空格;
  • 最后需要两个空行作为应答包结束。

请求数据

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    EIO: 3
    transport: websocket
    sid: 8Uehk2UumXoHVJRzAAAA
  • EIO:3 表示使用的是engine.io协议版本3
  • transport 表示传输采用的类型
  • sid: session id (String)

Frames

WebSocket协议使用帧(Frame)收发数据,在控制台->Frames中可以查看发送的帧数据。

其中帧数据前的数字代表什么意思呢?

这是 Engine.io协议,其中的数字是数据包编码:

[]

  • 0 open——在打开新传输时从服务器发送(重新检查)
  • 1 close——请求关闭此传输,但不关闭连接本身。
  • 2 ping——由客户端发送。服务器应该用包含相同数据的乓包应答 客户端发送:2probe探测帧
  • 3 pong——由服务器发送以响应ping数据包。 服务器发送:3probe,响应客户端
  • 4 message——实际消息,客户端和服务器应该使用数据调用它们的回调。
  • 5 upgrade——在engine.io切换传输之前,它测试,如果服务器和客户端可以通过这个传输进行通信。如果此测试成功,客户端发送升级数据包,请求服务器刷新其在旧传输上的缓存并切换到新传输。
  • 6 noop——noop数据包。主要用于在接收到传入WebSocket连接时强制轮询周期。

实例

以上的截图是上述例子中数据传输的实例,分析一下大概过程就是:

  1. connect握手成功
  2. 客户端会发送2 probe探测帧
  3. 服务端发送响应帧3probe
  4. 客户端会发送内容为5的Upgrade帧
  5. 服务端回应内容为6的noop帧
  6. 探测帧检查通过后,客户端停止轮询请求,将传输通道转到websocket连接,转到websocket后,接下来就开始定期(默认是25秒)的 ping/pong
  7. 客户端、服务端收发数据,4表示的是engine.io的message消息,后面跟随收发的消息内容

为了知道Client和Server链接是否正常,项目中使用的ClientSocket和ServerSocket都有一个心跳的线程,这个线程主要是为了检测Client和Server是否正常链接,Client和Server是否正常链接主要是用ping pong流程来保证的。

该心跳定期发送的间隔是socket.io默认设定的25m,在上图中也可观察发现。该间隔可通过配置修改。

参考engine.io-protocol

参考文章

Web 实时推送技术的总结 engine.io 原理详解

广而告之

本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/04/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)
本文介绍了如何利用 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯。首先介绍了 WebSocket 的概念和原理,然后阐述了利用 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯的具体步骤。最后通过一个示例展示了如何在前端实现该功能。
HT for Web
2018/01/03
1.5K0
基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)
《 Socket.IO》 解决 WebSocket 通信!
大家好呀,我是小菜~ 本文主要介绍 Socket.IO 微信公众号已开启,小菜良记,没关注的同学们记得关注哦! 在介绍 Socket.IO 之前, 我们先考虑一个问题, 如果这个时候有个需求, 类似
蔡不菜丶
2022/09/21
2.4K0
《 Socket.IO》 解决 WebSocket 通信!
Nodejs+socket.io搭建WebRTC信令服务器
我们在学习 WebRTC 时,首先要把实验环境搭建好,这样我们就可以在上面做各种实验了。
音视频_李超
2020/04/01
8.4K2
Nodejs+socket.io搭建WebRTC信令服务器
socket.io实践干货
一、前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码 socket.io 是跨平台的,可以实现多平台的即时通讯 由于 iOS 端进行 socket 编程主要使用 GCDAsyncSocket 框架,但要实现 Android、iOS、web 多平台的通讯,还是选择统一的框架或协议比较好。 基本 api,使用 soc
sunsky
2020/08/20
1.4K0
socket.io实践干货
基于 socket.io 快速实现一个实时通讯应用WebSocket概念实现用socket.io实现一个实时接收信息的例子分析webSocket协议参考文章
随着web技术的发展,使用场景和需求也越来越复杂,客户端不再满足于简单的请求得到状态的需求。实时通讯越来越多应用于各个领域。
用户2356368
2019/05/07
2.5K0
基于 socket.io 快速实现一个实时通讯应用WebSocket概念实现用socket.io实现一个实时接收信息的例子分析webSocket协议参考文章
JS Socket.IO 发送消息
在网上看到有人总结的 JS 的 Socket.IO 库发送消息的相关资料,觉得很不错,在这里做下整理与转载。
音视频_李超
2020/04/02
3.5K0
Node.js下基于Express + Socket.io 搭建一个基本的在线聊天室
  采用nodeJS设计,基于express框架,使用WebSocket编程之 socket.io机制。聊天室增加了 注册登录 模块 ,并将用户个人信息和聊天记录存入数据库.
书童小二
2018/09/03
2.7K0
Node.js下基于Express + Socket.io 搭建一个基本的在线聊天室
socket.io
本译文来源于https://socket.io/get-started/chat/,不足之处请多批评指正。 最近在学些vuejs和websocket相关技术,使用了websocket的两个封装的库vue-socket.io和vue-websocket
ccf19881030
2020/04/10
4K0
socket.io
PHP系列 | PHP跨平台实时通讯框架 Socket.IO 的应用
利用PHP构建能够在不同浏览器和移动设备上良好运行的实时应用,如实时分析系统、在线聊天室、在线客服系统、评论系统、WebIM等。
Tinywan
2019/07/23
3.8K0
Koa结合socket.io实现分桌点餐
去饭店吃饭的时候,桌上都会有一个二维码,每一桌的每一个用户都可以拿出手机独立点餐,而且同一桌的用户点餐都会在同一个购物车里,比如张三与李四一起来吃饭,张三点了青椒炒肉,李四拿出手机点餐的时候,购物车里就会显示张三点的青椒炒肉,而且每一桌的点餐与其他桌的不会相互干扰,付款的时候以桌为单位独立结账。
越陌度阡
2020/11/26
5290
使用node、Socket.io 搭建简易聊天室
Socket.io是一个WebSocket库,会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,而且支持的浏览器最低达IE5.5。 Socket.io 服务器 和 Socket.io 客户端之间全双工通信信道 尽可能使用WebSocket 连接建立(”尽可能“就说明要求客户端和服务端都必须使用,HTTP 长轮询`作为后备。 在了解socket-io前,我们先了解三种通信方式和Http轮询。
can4hou6joeng4
2023/11/29
4620
websocket深入浅出
答: 它是一种网络通信协议,是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
全栈程序员站长
2022/09/14
2.3K0
websocket深入浅出
低延迟双向实时事件通信 Socket.IO
Socket.IO 是一个库,可以在客户端和服务器之间实现低延迟,双向和基于事件的通信。
Tinywan
2024/08/27
3560
低延迟双向实时事件通信 Socket.IO
socket.io不为人知的功能
socket.io 是一个基于websocket实现的前后端实时通讯框架,也对低版本浏览器做了封装。使用起来简单,方便。
frontoldman
2019/09/02
6360
uniapp使用WebSocket实现即时通讯
WebSocket是一种基于TCP协议的全双工通信协议,它可以在客户端和服务器之间建立一个持久性的连接,实现实时通信。在uniapp中,我们可以使用uni-socketio插件来实现WebSocket的功能。
超级小可爱
2023/10/18
4.4K0
实现一个简单的WebSocket聊天室
run node index.js,并在浏览器打开 http://localhost:3000,访问成功即可看到
超超不会飞
2020/09/18
1.1K0
实现一个简单的WebSocket聊天室
Express结合Socket.io实现智能回复机器人
之前写了一篇 《Node.js中运用socket.io实现智能回复机器人与聊天室功能》 发现浏览的人还挺多,不过这篇博客只是讲解了一些实现原理,现在运用Node的Express框架给大家实现一下智能回复机器人。
越陌度阡
2020/11/26
7670
搞懂现代Web端即时通讯技术一文就够:WebSocket、socket.io、SSE
有关Web端即时通讯技术的文章我已整理过很多篇,阅读过的读者可能都很熟悉,早期的Web端即时通讯方案,受限于Web客户端的技术限制,想实现真正的“即时”通信,难度相当大。
JackJiang
2021/09/07
3.3K0
搞懂现代Web端即时通讯技术一文就够:WebSocket、socket.io、SSE
Node.js + Socket.io 实现一对一即时聊天
实现一对一即时聊天应用,重要的一点就是消息能够实时的传递,一种方案就是熟知的使用 Websocket 协议,本文中我们使用 Node.js 中的一个框架 Socket.io 来实现。
五月君
2020/07/22
2.7K0
socket.io的简单使用
在开发websocket的时候,我们可能会用到socket.io这个库,来看一下这个库的简答应用,先看一个简单的案例,服务端代码:
挥刀北上
2021/02/03
2.1K0
推荐阅读
相关推荐
基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验