You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
5.8 KiB
198 lines
5.8 KiB
import store from '@/store/index' |
|
|
|
import proto from '@/api/proto'; |
|
import { fetchOpMap, fetchRouteWs } from '@/api/api' |
|
import { getStorage, removeStorage } from '@/utils/storage'; |
|
import { redirectTo, showToast } from '@/utils/application'; |
|
import { setUserInfo } from '@/store/user' |
|
|
|
const { api } = proto |
|
|
|
const websocket = { |
|
conn: null, |
|
status: 0, // 0未初始,1打开,2已认证,3关闭 TODO 未连接,连接中 方法等待队列 |
|
seq: 1, // 消息序号 |
|
offset: 0, |
|
opFail: 0, |
|
opSuccess: 0, |
|
opPathMap: {}, |
|
nameOpMap: {}, |
|
// 消息监听列表 |
|
msgListener: {}, // {op: func} |
|
waitInitCalls: [], |
|
init(token, { offset, opFail, opSuccess, opPathMap, nameOpMap }, { wsAddr }) { |
|
|
|
// TODO 连接状态判定 |
|
this.offset = offset || 0; |
|
this.opFail = opFail; |
|
this.opSuccess = opSuccess; |
|
this.opPathMap = opPathMap || {}; |
|
this.nameOpMap = nameOpMap || {}; |
|
// window.proto = proto; |
|
|
|
const ws = new WebSocket(`ws://${wsAddr}/api/conn/ws`) |
|
if (this.conn) { |
|
this.conn.close(); |
|
} |
|
this.conn = ws |
|
console.log(this) |
|
ws.binaryType = 'arraybuffer' |
|
ws.onopen = (e) => { |
|
this.status = 1; |
|
// 连接后立即认证连接 |
|
const req = api.ReqIdentity.create({ token }) |
|
// const reqBuffer = api.ReqIdentity.encode(req).finish() |
|
this.send(req, res => { |
|
this.status = 2; |
|
// const dispatch = useDispatch(); |
|
// window.res = res; |
|
// console.log(store, setUserInfo(res)) |
|
store.dispatch(setUserInfo(res.toJSON())); |
|
|
|
console.log('identity success:', res) |
|
|
|
// 依次发送等待连接的消息队列 |
|
for (let i = 0; i < this.waitInitCalls.length; i++) { |
|
if (this.waitInitCalls[i]) { |
|
this.waitInitCalls[i](); |
|
this.waitInitCalls[i] = null; |
|
} |
|
} |
|
}, err => { |
|
removeStorage('_t') |
|
showToast({title: err}) |
|
console.error('连接认证失败:', err) |
|
}) |
|
} |
|
ws.onmessage = (e) => { |
|
// console.log('msg:', e.data) |
|
const wrap = api.ProtoWrap.decode(new Uint8Array(e.data)) |
|
// 解码响应体 |
|
const opPath = this.opPathMap[wrap.op] |
|
if (!opPath) { |
|
console.error('res op not exists!', wrap.op) |
|
return |
|
} |
|
const res = proto[opPath[0]][opPath[1]].decode(wrap.body) |
|
// const res = api.ResIdentity.decode(wrap.body) |
|
console.log('res msg:', res) |
|
if (wrap.seq !== 0) { |
|
// 对应请求callback |
|
const caller = this.waitCall[wrap.seq]; |
|
delete this.waitCall[wrap.seq]; |
|
// console.log(waitCall, this.waitCall); |
|
if (caller) { |
|
if (wrap.op === opFail) { |
|
if (res.code === 401) { // 重新登录 |
|
setTimeout(() => redirectTo({url: '/pages/login/login'}), 500); |
|
} |
|
caller.onFail(res, wrap); |
|
} else { |
|
caller.onRes(res, wrap); |
|
} |
|
return; |
|
} |
|
} |
|
// 找消息类型的listen执行 |
|
const listener = this.msgListener[wrap.op]; |
|
if (listener) { |
|
listener(res); |
|
return; |
|
} |
|
console.log('no handler msg:', wrap.op, res) |
|
} |
|
|
|
ws.onerror = (a, b, c) => { |
|
this.status = 3; |
|
console.log('ws error:', a, b, c) |
|
// var interval = setInterval(() => { |
|
// this.reconnect(); |
|
// }, Math.random() * 3); |
|
} |
|
|
|
ws.onclose = e => { |
|
this.status = 3; |
|
console.log('ws close', e) |
|
} |
|
}, |
|
reconnect() { |
|
|
|
}, |
|
waitCall: {}, // 等待响应的 callback 列表 |
|
send(msg, resCall, errCall) { |
|
window.msg = msg; |
|
// 不是认证消息且连接未认证,将请求放到等待队列 |
|
if (msg.__proto__.constructor !== api.ReqIdentity && this.status !== 2) { |
|
this.waitInitCalls.push(() => this.send(msg, resCall, errCall)); |
|
return; |
|
} |
|
// 序列化请求消息... |
|
let typeUrl = msg.constructor.getTypeUrl(); |
|
const idx = typeUrl.lastIndexOf("/"); |
|
typeUrl = idx === -1 ? typeUrl : typeUrl.substring(idx + 1); |
|
const op = this.nameOpMap[typeUrl]; |
|
console.log('req', typeUrl, op); |
|
const msgBufer = msg.constructor.encode(msg).finish(); |
|
const wrap = api.ProtoWrap.create({ ver: 1, op: op, seq: this.seq++, body: msgBufer }); |
|
const wrapBuffer = api.ProtoWrap.encode(wrap).finish(); |
|
this.conn.send(wrapBuffer); |
|
if (resCall) { |
|
this.waitCall[wrap.seq] = { |
|
seq: wrap.seq, |
|
onRes: resCall, |
|
onFail: errCall, |
|
} |
|
} |
|
// 定时检查是否超时... |
|
}, |
|
} |
|
|
|
export default websocket |
|
|
|
export const addMsgListen = (type, call) => { |
|
if (websocket.status !== 2) { |
|
websocket.waitInitCalls.push(() => addMsgListen(type, call)); |
|
return; |
|
} |
|
console.log('add listen...', type) |
|
const op = websocket.nameOpMap[type]; |
|
if (op === undefined) { |
|
console.error(`not found listen msg type [${type}] for all type:`, websocket.nameOpMap); |
|
return; |
|
} |
|
websocket.msgListener[op] = call; |
|
} |
|
|
|
// 监听消息类型 |
|
export const removeMsgListener = type => { |
|
if (websocket.status !== 2) { |
|
websocket.waitInitCalls.push(() => removeMsgListener(type)); |
|
return; |
|
} |
|
const op = websocket.nameOpMap[type]; |
|
if (op === undefined) { |
|
console.error(`when remove, not found listen msg type [${type}] for all type:`, websocket.nameOpMap); |
|
return -1; |
|
} |
|
websocket.msgListener[op] = null; |
|
} |
|
|
|
export const sendPromise = function(msg) { |
|
return new Promise((resolve, reject) => { |
|
websocket.send(msg, resolve, reject) |
|
}) |
|
} |
|
|
|
// ;(async function() { |
|
// // const token = "15PohpTztq4YkMmErI4H71kJWX2pPqnvOZar9HKOfg1KEy0fd8fUNxnBIwuMDcKQEBPG7b9UC7ja_8FtwvYjnA==" |
|
// const token = getStorage('_t') |
|
// if (!token) { |
|
// return; |
|
// } |
|
// // console.log('token', token) |
|
// const [opMap, wsRes] = await Promise.all([fetchOpMap(), fetchRouteWs()]); |
|
// websocket.init(token, opMap.data, wsRes.data); |
|
|
|
// window.websocket = websocket; |
|
// })() |
|
|
|
|