百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

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

toyiye 2024-07-04 09:10 13 浏览 0 评论

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

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

WebSocket技术应运而生。

WebSocket概念

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

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

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

实现

原生实现

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

建立连接

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

var Socket = new WebSocket(url, [protocol] );

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

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

  1. 当Browser和WebSocketServer连接成功后,会触发onopen消息。
Socket.onopen = function(evt) {};
  1. 如果连接失败,发送、接收数据失败或者处理数据出现错误,browser会触发onerror消息。
Socket.onerror = function(evt) { };
  1. 当Browser接收到WebSocketServer端发送的关闭连接请求时,就会触发onclose消息。
Socket.onclose = function(evt) { };

收发消息

  1. 当Browser接收到WebSocketServer发送过来的数据时,就会触发onmessage消息,参数evt中包含server传输过来的数据。
Socket.onmessage = function(evt) { };
  1. send用于向服务端发送消息。
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这些事件的名字不能使用之外,你可以触发任何自定义的事件名称。

建立连接

 const socket = io("ws://0.0.0.0:port"); // port为自己定义的端口号
 let io = require("socket.io")(http);
 io.on("connection", function(socket) {})

消息收发

一、发送数据

socket.emit(自定义发送的字段, data);

二、接收数据

 socket.on(自定义发送的字段, function(data) {
 console.log(data);
 })

断开连接

一、全部断开连接

 let io = require("socket.io")(http);
 io.close();

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

 // 客户端
 socket.emit("close", {});
 // 服务端
 socket.on("close", data => {
 socket.disconnect(true);
 });

room和namespace

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

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

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

namespace

服务端

 io.of("/post").on("connection", function(socket) {
 socket.emit("new message", { mess: `这是post的命名空间` });
 });
 
 io.of("/get").on("connection", function(socket) {
 socket.emit("new message", { mess: `这是get的命名空间` });
 });

客户端

 // 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

客户端

 //可用于客户端进入房间;
 socket.join('room one');
 //用于离开房间;
 socket.leave('room one');

服务端

 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模拟了服务端接口。以下的例子都在本地服务器中实现。

服务端

先来看看服务端,先来开启一个服务,安装express和socket.io

安装依赖

 npm install --Dev express
 npm install --Dev socket.io

构建node服务器

 let app = require("express")();
 let http = require("http").createServer(handler);
 let io = require("socket.io")(http);
 let fs = require("fs");
 
 http.listen(port); //port:输入需要的端口号
 
 function handler(req, res) {
 fs.readFile(__dirname + "/index.html", function(err, data) {
 if (err) {
 res.writeHead(500);
 return res.end("Error loading index.html");
 }
 
 res.writeHead(200);
 res.end(data);
 });
 }
 
 io.on("connection", function(socket) {
 console.log('连接成功');
 //连接成功之后发送消息
 socket.emit("new message", { mess: `初始消息` });
 
 });

客户端

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

 <div>发送信息</div>
 <input placeholder="请输入要发送的信息" />
 <button onclick="postMessage()">发送</button>
 // 接收到服务端传来的name匹配的消息
 socket.on("new message", function(data) {
 console.log(data);
 });
 
 function postMessage() {
 socket.emit("recive message", {
 message: content,
 time: new Date()
 });
 messList.push({
 message: content,
 time: new Date()
 });
 }

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

 socket.on("new message", function(data) {
 console.log(data);
 });

效果

实时通讯效果

客户端全部断开连接

加入房间

离开房间

框架中的应用

npm install socket.io-client

 const socket = require('socket.io-client')('http://localhost:port');
 componentDidMount() {
 socket.on('login', (data) => {
 console.log(data)
 });
 socket.on('add user', (data) => {
 console.log(data)
 });
 socket.on('new message', (data) => {
 console.log(data)
 });
 }

分析webSocket协议

Headers

请求包

 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.

应答包

应答包说明:

 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”的值;
  • 应答包中冒号后面有一个空格;
  • 最后需要两个空行作为应答包结束。

请求数据

 EIO: 3
 transport: websocket
 sid: 8Uehk2UumXoHVJRzAAAA
  • EIO:3 表示使用的是engine.io协议版本3
  • transport 表示传输采用的类型
  • sid: session id (String)

Frames

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

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

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

<Packet type id> [<data>]

  • 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,在上图中也可观察发现。该间隔可通过配置修改。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码