在爱智应用中使用 Socket.IO,轻松实现双向通信

leecactus0 2022-01-05 16:52:26

什么是 Socket.IO?

Socket.IO 是一个基于事件通信的实时应用程序框架,它在即时通讯、通知和消息推送,实时分析等场景中有广泛的应用。它包括两个部分:

  • 在 Server 端的模块(JSRE 已提供了 socket.io 模块)。

  • 在 Client  端的模块。

 

Socket.IO 的实现分层

Socket.IO 将实现分成了两层:

  • 底层管道:即 Engine.IO 层,它是 Socket.IO 的内部引擎。

  • 高级API:即 Sokcet.IO 本身。

     

 

Engine.IO

Engine.IO 负责建立服务器和客户端之间的低级连接,它的主要任务是处理各种传输和升级机制以及断线检测重连。

 

客户端首先会尝试通过 WebSocket 进行连接,如果失败则会回退到 HTTP 进行长轮询。所以,理想情况下我们应该保证:

  • 浏览器是支持 WebSocket 的。

  • 没有任何元素(代理、防火墙等)阻止客户端和服务器之间的 WebSocket 连接。

     

当连接建立完成之后,高层就可以通过暴露出来的 API 进行数据交换了。

 

高层API

上面提到,Socket.IO 是基于事件的,实际上就是对 EventEmitter 类的继承 。在 EventEmitter 中,对象(发射器)发出命名事件,导致函数对象(侦听器)被调用。基于事件驱动,我们需要通过 on 方法向侦听器中添加事件名以及监听函数,通过 emit 来触发监听函数,从而完成通信任务。

 

Socket.IO 实现了 EventEmitter 中的事件触发以及监听函数,包括基本的 on(), emit(), removeListener() 等,同时也自定义了一些针对连接生命周期内的事件名,包括:

  • connect 来自客户端连接时触发。

  • connection 是 connect 的别名。

  • disconnect 断开连接时触发。

  • disconnecting 当客户端将要断开连接(还未离开 rooms)时触发。

  • error 发生错误时触发。

这些是 Socket.IO 的保留字段(还有 newListener,removeListener),我们在自定义的时候应该避免和这些字段重名。

 

JSRE 中的引用

JSRE 中提供了 Socket.IO 的模块,在服务端我们可以直接使用,需要注意的是当前 JSRE 中 Socket.IO 的版本为 2.x,前端在使用 Socket.IO 的模块时需要注意版本对应。

 

那么 Socket.IO 是如何运行的?

Server 端的创建

以下是 Server 端的示例代码:


/* Import system modules */
const WebApp = require('webapp');
const io = require('socket.io');

/* Import routers */
const myRouter = require('./routers/rest');

/* Create App */
const app = WebApp.createApp();

/* Set static path */
app.use(WebApp.static('./public'));

/* Set test rest */
app.use('/api', myRouter);

/* Rend test */
app.get('/temp.html', function(req, res) {
  res.render('temp', { time: Date.now() });
});

/* Start App */
app.start();

const socketio = io(app, {
  serveClient: false,
  pingInterval: 10000,
  pingTimeout: 5000
})

socketio.on('connection', socket => {
  console.log('>> socket connected <<')
  socket.emit('falcon', 'reply please')

  socket.on('message', (data, ack) => {
    console.log('recive:', data)
    ack('0')
  })

  socket.on('message2', data => {
    console.log('recive:', data)
  })
})


/* Event loop */
require('iosched').forever();

可以看到,创建 Socket.IO 的 server 还是很简单的,只需要将 webapp 的实例作为参数传递进去,然后监听 connection 方法。emit 会发射消息,消息内容可以根据自己的需求定义,可以是简单的字符串,也可以是一个结构化的 JSON 对象。

 

在 socket.on 中定义监听消息字段,如上示例,我们可以仅仅接收数据,进行处理,也可以给出 ack 返回确认信息。

 

socket.emit 同样支持 response 的返回函数,以做出确认响应。


socket.emit('ferret', 'tobi', (data) => {
  console.log(data); // data will be 'woot'
});

// client code example
client.on('ferret', (name, fn) => {
  fn('woot');
});

Client 端的创建

接下来我们看一下前端应用如何使用使用 Socket.IO 与服务端的建立连接并通信。

 

以 Vue3 为例,使用 socket.io-client 这个包。

npm install socket.io-client
# or use yarn 
yarn add socket.io-client

 

为了方便在不同的页面进行调用,可以将 socket.io 进行一次封装,以下仅作为参考:


// src/libs/socketio.js
import io from 'socket.io-client'
class SocketIO {
  constructor () {
    this.socket = io('https://192.168.128.1:7369')
    this.socket.on('connect', () => {
      // 连接上服务端的回调函数
      // 还可以监听 'error' 并对错误连接进行相应处理
      console.log('>> 已连接!')
    })
  }

  push (event, msg) {
    // 这里封装了一个通用 response 的 emit 方法用以处理相同确认操作的 emit
    this.socket.emit(event, msg, (response) => {
      console.log('>>>> res:', response)
    })
  }
}

export const socketio = new SocketIO()

 

客户端连接生命周期

生命周期示意图:

 

接下来我们在不同的页面就可以引用这个封装来进行简单操作:


// src/App.jsx
import { defineComponent, onMounted } from 'vue'
import './app.less' // css 设置白色背景及安全距离
// 上文中导出的 socketio
import { socketio } from './libs/socketio'

export default defineComponent({
  name: 'APP',
  setup (props, ctx) {
    //
    onMounted(() => {
      // 如果此页面需要监听后端发送数据
      // 最好将监听函数在 onMounted 生命周期中创建出来
      socketio.socket.on('message', data => {
        console.info('>> client recive:', data)
      })
    })

    // 发送数据的函数
    function handleEmitMsg () {
      socketio.push('message', 'gg')
    }
    return () => (
      <div>
        <p><button onClick={ handleEmitMsg }>emit</button></p>
      </div>
    )
  }
})

 

以上就是前端监听消息,以及发送消息的操作,如果此时想对特定操作执行 emit 消息返回确认,可以从实例中获取:

// 修改上例中的 emit 函数
// 发送数据的函数
function handleEmitMsg () {
  socketio.socket.emit('message', 'data', response => {
    // 逻辑代码
  })
}

 

至此我们已经完整走完 Socket.IO 的建立过程,理解的点就在于需要知道 socket 是一个基于事件双向通信的过程,两端都可以进行主动消息的发送以及被动消息的接收,所以我们需要事先约定好一个消息名称,作为消息发送方,需要 emit 发送这个消息;作为消息接收方,需要 on 接收这个消息。

参考链接:

1.爱智 APP demo

2.web 端 demo

3.Socket.IO

...全文
614 回复 点赞 打赏 收藏 举报
写回复
回复
切换为时间正序
请发表友善的回复…
发表回复
相关推荐
发帖
爱智开发者社区
加入

1444

社区成员

爱智开发者平台是一个开放的物联网平台,通过爱智世界,应用开发者可以把自己的应用分发到亿万用户的设备上,硬件开发者能够把设备能力开放给海量的开发者,让优质的应用脱颖而出,为用户提供更优秀的使用体验。
帖子事件
创建了帖子
2022-01-05 16:52
社区公告
暂无公告