我們知道直播間的彈幕消息是通過websocket傳輸的,而且傳輸的并不是明文數據,而是protobuf消息,至于為什么使用這個protobuf消息,因為它是二進制傳輸,更快更穩,相對于直播這種實時性比較高的要求,使用這種消息傳輸是非常合適的。
websocket連接
我們先要在web直播端看一下websocket的連接是在哪里建立的,至于怎么看這個websocket在哪里建立的,可以監聽發送的ws請求,找到這個發送請求的代碼位置:
監聽消息
找到onMessage這個方法,這里面就是給這個socket實例添加了?this.socket.addEventListener("message", e) 方法,然后看一下這個e就是目標監聽函數,這個函數在哪里呢?繼續debug往下找:
再看看這個_receiveMessage函數里面是啥:
這里面還嵌套了一層es函數,這個es其實就是一個promise:
_receiveMessage里面就是處理收到消息的邏輯了,比較復雜,我們可以單獨把它拿出來,然后添加上備注看一下大概都是什么意思。把代碼拿出來,我們單獨看一下里面的邏輯是啥:
會到debug狀態,看一下這個e此時怎么像一個消息呢?沒錯,它就是一個消息:
再來看看t是啥?這怎么那么像彈幕或者聊天或者禮物或者觀眾的消息呢?是的,它就是:
每一個消息內容都有一個payload,里面就是真正的消息:
這里的ack消息里面就是需要使用PushFrame這個消息,里面添加payload_type +?payload+LogID編碼來的。
?
解析消息
上面的消息和payload內容都是二進制,怎么顯示出來二進制的呢?
查看一下調用棧,發現這些消息都是送s里面導出來的,那這個s是從哪里來的?
s是這個 transport.decode(new Uint8Array(e.data)) 解析出來的:
那這個transport是啥,怎么解析的呢?找到了:
我們把代碼折疊一下:這里就是創建了一個class e,其實這個e就是transport的類
看代碼:
var g = f;.....而這個f就是下面的代碼,也就是我截圖的那個class e:
f = class e {constructor() {this.cachedType = {},this.loading = null,this.loadSchema = ()=>{"undefined" != typeof window && window.requestIdleCallback(()=>{this._loadSchema()})},this._loadSchema = ()=>(this.loading || (this.loading = (0,n.C)(this, null, function*() {if (u.roots.transport) {this.root = u.roots.transport,this.loading = Promise.resolve();return}yield(0,o.y)(),yield r.e(2986).then(r.bind(r, 69949)),this.root = u.roots.transport,this.loading = Promise.resolve()})),this.loading)}static get instance() {return e.__instance ? e.__instance : e.__instance = new e}static addRelation(t, r) {e.relation[t] = r,e.relation[r] = t}static setRelation(t) {e.relation = (0,n.i)((0,n.i)({}, e.relation), null != t ? t : {})}getType(t) {let r = t.replace(a.nl, ""), i = this.cachedType[r];if (i)return i;try {let i = [e.relation[t], e.relation[r], r, t].filter(e=>e), n = i.map(t=>e.typeHintPrefix.map(e=>`${e}.${t}`)).reduce((e,t)=>e.concat(t)).concat(i);d("search types", n);let o = n.reduce((e,t)=>e && "function" == typeof e ? e : t.split(".").reduce((e,t)=>null == e ? void 0 : e[t], this.root), void 0);if ("function" != typeof o)throw Error("cannot find type");return o} catch (e) {return d(`no current schema[${String(r)}]`),null}}// 這里就是transport的decode代碼decode(e, t) {return (0,n.C)(this, null, function*() {var r, i, n, o, a, s, l, c, u;if (yield this._loadSchema(),t)return this._decode(e, t);let[p,h] = yield this._decodeFrameOrResponse(e), d = null != (o = null != (n = null == (i = null == (r = null == h ? void 0 : h.headers) ? void 0 : r.find(e=>"im-cursor" === e.key)) ? void 0 : i.value) ? n : p.cursor) ? o : "", m = null != (c = null != (l = null == (s = null == (a = null == h ? void 0 : h.headers) ? void 0 : a.find(e=>"im-internal_ext" === e.key)) ? void 0 : s.value) ? l : p.internal_ext) ? c : "";return {response: p,frame: h,needAck: null != (u = p.need_ack) && u,cursor: d,internalExt: m}})}encode(e, t) {return (0,n.C)(this, null, function*() {return yield this._loadSchema(),this._encode(e, t)})}ack(e, t) {return (0,n.C)(this, null, function*() {var r, i, n, o;let l = null != (o = null != (n = null == (i = null == (r = e.headers) ? void 0 : r.find(e=>"im-internal_ext" === e.key)) ? void 0 : i.value) ? n : t.internal_ext) ? o : "";return yield this.encode({payload_type: a.AG.Ack,payload: s(l),LogID: e.LogID}, "PushFrame")})}ping() {return this.encode({payload_type: a.AG.Hb}, "PushFrame")}_decodeFrameOrResponse(e) {return (0,n.C)(this, null, function*() {try {let t = this._decode(e, "PushFrame"), r = yield this._extractResponse(t);return [this._decode(r, "Response"), t]} catch (t) {return [this._decode(e, "Response")]}})}_extractResponse(t) {return (0,n.C)(this, null, function*() {var r;return (null == (r = t.headers) ? void 0 : r.some(e=>"compress_type" === e.key && "gzip" === e.value)) ? yield e.unGzip(t.payload) : t.payload})}_decode(e, t) {let r = this.getType(t);if (!r)return;let i = r.decode(e);return d("decoded success", t, i),m("decoded success", e),i}_encode(e, t) {let r = this.getType(t);if (!r)return;let i = r.encode(e).finish();return d("encoded success", t, e),m("encoded success", i),i}}
那這個decode里面怎么解析數據的?看代碼,找到_decode和_decodeFrameOrResponse這兩個函數的內容,看到了什么?原來解碼的就在這里啊:
到這里我覺得你應該就知道怎么解析了吧,下課