一、什么是 Socket.IO?
Socket.IO 是一个基于事件通信的实时应用程序框架,它在即时通讯、通知和消息推送,实时分析等场景中有广泛的应用。 Socket.IO 包括两个部分:
在 Server 端的模块(JSRE 已提供了 socket.io 模块);
Socket.IO 将实现分成了两层:
底层管道:即 Engine.IO 层,它是 Socket.IO 的内部引擎。
Engine.IO 负责建立服务器和客户端之间的低级连接,它的主要任务是处理各种传输和升级机制以及断线检测重连。客户端首先会尝试通过 WebSocket 进行连接,如果失败则会回退到 HTTP 进行长轮询。所以,理想情况下应该保证:
没有任何元素(代理、防火墙等)阻止客户端和服务器之间的 WebSocket 连接。 当连接建立完成之后,高层就可以通过暴露出来的 API 进行数据交换。
二、高层API
Socket.IO 是基于事件的,实际上就是对 EventEmitter 类的继承,在 EventEmitter 中,对象(发射器)发出命名事件,导致函数对象(侦听器)被调用。基于事件驱动,需要通过 on 方法向侦听器中添加事件名以及监听函数,通过 emit 来触发监听函数,从而完成通信任务。 Socket.IO 实现了 EventEmitter 中的事件触发以及监听函数,包括基本的 on(), emit(), removeListener() 等,同时也自定义了一些针对连接生命周期内的事件名,包括:
connection 是 connect 的别名;
disconnecting 当客户端将要断开连接(还未离开 rooms)时触发;
这些是 Socket.IO 的保留字段(还有 newListener,removeListener),在自定义的时候应该避免和这些字段重名。
三、JSRE 中的引用
JSRE 中提供了 Socket.IO 的模块,在服务端可以直接使用,需要注意的是当前 JSRE 中 Socket.IO 的版本为 2.x,前端在使用 Socket.IO 的模块时需要注意版本对应。 那么 Socket.IO 是如何运行的呢?
四、Server 端的创建
const WebApp = require ( 'webapp' ) ;
const io = require ( 'socket.io' ) ;
const myRouter = require ( './routers/rest' ) ;
const app = WebApp. createApp ( ) ;
app. use ( WebApp. static ( './public' ) ) ;
app. use ( '/api' , myRouter) ;
app. get ( '/temp.html' , function ( req, res) {
res. render ( 'temp' , { time: Date. now ( ) } ) ;
} ) ;
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)
} )
} )
require ( 'iosched' ) . forever ( ) ;
可以看到,创建 Socket.IO 的 server 还是很简单的,只需要将 webapp 的实例作为参数传递进去,然后监听 connection 方法。emit 会发射消息,消息内容可以根据自己的需求定义,可以是简单的字符串,也可以是一个结构化的 JSON 对象。 在 socket.on 中定义监听消息字段,如上示例,我们可以仅仅接收数据,进行处理,也可以给出 ack 返回确认信息。socket.emit 同样支持 response 的返回函数,以做出确认响应。
socket. emit ( 'ferret' , 'tobi' , ( data) = > {
console. log ( data) ;
} ) ;
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 进行一次封装,以下仅作为参考:
import io from 'socket.io-client'
class SocketIO {
constructor ( ) {
this. socket = io ( 'https://192.168.128.1:7369' )
this. socket. on ( 'connect' , ( ) = > {
console. log ( '>> 已连接!' )
} )
}
push ( event, msg) {
this. socket. emit ( event, msg, ( response) = > {
console. log ( '>>>> res:' , response)
} )
}
}
export const socketio = new SocketIO ( )
六、客户端连接生命周期
接下来在不同的页面就可以引用这个封装来进行简单操作:
import { defineComponent, onMounted } from 'vue'
import './app.less'
import { socketio } from './libs/socketio'
export default defineComponent ( {
name: 'APP' ,
setup ( props, ctx) {
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 消息返回确认,可以从实例中获取:
function handleEmitMsg ( ) {
socketio. socket. emit ( 'message' , 'data' , response = > {
} )
}
至此已经完整走完 Socket.IO 的建立过程,理解的点就在于需要知道 socket 是一个基于事件双向通信的过程,两端都可以进行主动消息的发送以及被动消息的接收,所以需要事先约定好一个消息名称,作为消息发送方,需要 emit 发送这个消息;作为消息接收方,需要 on 接收这个消息。