🗒️WebSocket
原生的
const socket = new WebSocket(ws_url)
// 监听事件
socket.onopen = () => { }
socket.onclose = () => { }
socket.onerror = () => { }
socket.onmessage = (event) => {
let data = JSON.parse(event.data) // 接收数据 event.data
}
// 调用方法
socket.close()
SocketIO
使用,代码示意:
// https://github.com/socketio/socket.io-client/blob/master/docs/API.md
// https://github.com/socketio/engine.io-client#methods
import io from 'socket.io-client'
class SocketIO {
socket;
constructor(){
this.socket = io(
ws_origin, // url 默认是 window.location
{
path: ws_path, // String. 默认是 '/socket.io'
// query: {}, // Object. 查询参数
// parser:; // 使用的解析器。默认是 socket.io 自带的 Parser。详见 socket.io-parser
// reconnection: true, // Boolean. 是否自动重新连接。默认 true
// reconnectionAttempts: 1, // Number. 尝试重新连接的次数。默认 Infinity
// reconnectionDelay: 1000, // Number. 尝试重新连接之前的等待多长。默认 1000。但它受随机因子影响,比如默认的初始延时会介于 500-1500 ms
// reconnectionDelayMax: 5000, // Number. 重新连接之间等待的最长时间。默认 5000。每次尝试都将重新连接延迟增加2倍以及随机化
// randomizationFactor: 0.5, // Number. 默认 0.5。 0 <= randomizationFactor <= 1
// timeout: 20000, // Number. 连接超时, 默认 20000。 如果多长时间之内连接还没建成功,则认为失败,会触发 connect_error 和 connect_timeout 事件。进而触发重连机制
autoConnect: autoConnect, // Boolean. 默认是 true。如果设置成 false,则必须手动调用 manager.open,在你觉得合适的时候
transports: ['websocket', 'polling'] // Array. 要尝试的传输列表(按序)。默认是 ['polling', 'websocket']。
})
// 监听事件
// this.socket.on('connect', () => {})
// this.socket.on('connect_error', (error) => {})
// this.socket.on('connect_timeout', (error) => {})
// this.socket.on('reconnect', (attemptNumber) => {})
// this.socket.on('disconnect', (reason) => {})
// this.socket.on('error', (error) => {})
// this.socket.on('ping', () => {
// console.warn('套接字: ping')
// })
// this.socket.on('pong', (latency) => {
// console.warn('套接字: pong')
// })
// 监听服务器推送的消息(自定义类型)
socket.on('REPLY', (res) => {
let data = JSON.parse(res) // 再传给对应的函数
})
socket.on('END', (res) => {
let data = JSON.parse(res) // 再传给对应的函数
})
socket.on('DRAWBACK', (res) => {
let data = JSON.parse(res) // 再传给对应的函数
})
}
}
对外 API,代码示意:
class SocketIO {
// 一. 连接和断开
connect(){
// 若用户登录了,再连接
socket.connect()
}
close(msg){
socket.close(msg) // 手动断开
}
// 二. 发送消息
send(msg){
socket.send('client send ' + msg)
}
// 三. 接收消息:自定义的三类
captureReply(callback, path){
this.#capture(CB_REPLY_LIST, callback, path)
}
captureEnd(callback, path){
this.#capture(CB_END_LIST, callback, path)
}
captureDrawBack(callback, path){
this.#capture(CB_DRAWBACK_LIST, callback, path)
}
#capture(obj, callback, path){
if(path){
obj[path] = callback
}else{
obj['*'] = callback
}
}
}
centrifuge-js
使用上:若用户登录了,则开始连接:先 new,再 subscribe 再 connect。代码示意:
import Centrifuge from 'centrifuge' // https://github.com/centrifugal/centrifuge-js
class SocketIO {
centrifuge;
subscription;
constructor(){
// 关于 ping 的间隔,默认是 25000 即 25s
// 这里重置为 5s,让其在 10s 之内有心跳,因为服务器 proxy 代理有超时时间 10s
// 设置成 2s,以适应移动端不稳定的网络状态(网络不稳定时,能及时收到短信提醒/页面重刷)
this.centrifuge = new Centrifuge(ws_url, {
pingInterval: 5000
})
// 用 user_id 生成 connection token
const jsw_token = this.#getJWT(user_id, 'secret')
this.centrifuge.setToken(jsw_token)
// this.centrifuge.on('connect', (context) => {})
// this.centrifuge.on('disconnect', (context) => {})
// 订阅并连接
this.#subscribeAndConnect()
}
#subscribeAndConnect(){
this.subscription = centrifuge.subscribe(uid, (msg) => {
// 收到消息 msg
let res = msg.data
const data = JSON.parse(res.content)
const type = res.msg_type // 自定义的:end drawback reply
// 具体的业务逻辑:把 data 传给特定的函数(因为不止一个页面要接收后台的消息)
if(CB_LIST[type]){
let cur_path = location.pathname
if(CB_LIST[type][cur_path]){
call_cb(CB_LIST[type][cur_path], data) // cb(data)
}
call_cb(CB_LIST[type]['*'], data) // cb(data)
}
})
// .on('join', (msg) => {})
// .on('leave', (msg) => {})
// .on('unsubscribe', (context) => {})
// .on('subscribe', (context) => {})
// .on('error', (msg) => {})
// .on('publish', (msg) => {})
this.centrifuge.connect()
}
}
对外提供的接口,有三类,代码示意如下。
// 代码示意
class SocketIO {
// 一. 连接和关闭
connect(){
this.centrifuge.connect()
}
close(){
this.centrifuge.disconnect() // 也会触发 subscription 的 unsubscribe
}
// 二. 当页面是可见状态、不可见状态时
revisible(){
this.connect()
this.#subscriptChannel()
}
rehide(){
// subscription.unsubscribe()
// subscription.removeAllListeners()
// 单纯取消订阅,会导致发短信有延迟,故改为直接关闭
this.close()
}
// 三. 接收服务器传来的消息
captureReply(callback, path){
this.#capture(callback, path, 'REPLY') // 医生的新回复
}
captureEnd(callback, path){
this.#capture(callback, path, 'END') // 订单正常关闭
}
captureDrawBack(callback, path){
this.capture(callback, path, 'DRAWBACK') // 退款
}
}
class SocketIO {
// 私有方法:把接收方的回调函数都存起来
#capture(callback, path, type){
if(!CB_LIST[type]){
CB_LIST[type] = {}
}
if(path){
CB_LIST[type][path] = callback
}else{
CB_LIST[type]['*'] = callback
}
}
}
Last updated