以前寫WebSocket
都是基於文本傳輸的,後來準備升級項目,因而打算嘗試一下arraybuffer
傳輸方式,因爲是第一次使用javascript
處理字符串轉arraybuffer
,不過真的是一把辛酸淚啊,特此記錄。javascript
仍是以前寫的(基於Tio通信框架的SpringBootLayIM項目)[https://github.com/fanpan26/SpringBootLayIM].java
arraybuffer
。var ws = new WebSocket(tool.options.server + '?access_token=' + token); ws.binaryType = 'arraybuffer';
IWsMsgHandler.onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext)
方法接收客戶端的消息/** * 字節傳輸 * */ public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception { System.out.println("接收到的消息爲:"+new String(bytes)); return null; }
ws.send
以前要先將發送的內容轉換。// var buff = new TextEncoder().encode(str);
很好,這三個步驟作完以後,咱們試一下。能夠看到,打印結果正常git
接收到的消息爲:{"username":"雍正王","avatar":"/static/images/demo/huangshang.jpg","id":203328,"type":"friend","content":"22"}
到此爲止呢,仍是沒有任何問題的。按照我以前的思路就是將消息反序列化成對象實例,而後進行相應的邏輯操做。不過呢,後來突發奇想,原本我傳遞過來的消息就是想原封不動的發送給對方,也就是說程序不須要執行反序列化的過程。那可不能夠這樣,以下圖:
沒錯就是這樣子,而後我打印了一下new TextEncoder().encode(str);
返回的內容以下:
能夠看到,它是一個 Uint8Array
,因而我就看看可否從新構造一個數組,用僞代碼實現就是醬紫:github
var newBuff = new ArrayBuffer(4+1+bodyArray.buffer.byteLength)
再查資料發現ArrayBuffer
得依賴DataView
來進行賦值操做,那不就簡單啦。先把接收人ID(UInt32)和 消息類型 (UInt8) 寫入,以下:數組
var view = new DataView(dataBuff.buffer); view.setInt32(0, targetId); view.setInt8(4, 1);
那剩下的body怎麼寫進去呢?按照我想的,應該是有個 setContent(arr,offset:number)
方法,但是查了一下,沒有。因而乎再查文檔,原來Unit8Array
有個遍歷的方法,那就用這個方法試試吧。框架
//從索引第五個開始寫 dataBuff.forEach(function (value, index) { view.setInt8(5+index,value); });
爲了驗證我這個思路(嗚嗚,這個思路仍是研究了很久,浪費了不少時間。。。其實中間因爲不熟悉DataView和ArrayBuffer致使作了不少嘗試的工做,並且都失敗了,要麼就是報錯,要麼就是覆蓋了消息體)的正確性,咱們將後臺代碼改一下:學習
byte[] targetIdBytes = Arrays.copyOf(bytes,4); byte[] contents = Arrays.copyOfRange(bytes,5,bytes.length); System.out.println("消息體:"+new String(contents)); int targetId = ConvertUtil.byteArrayToInt(targetIdBytes); System.out.println("接收人:"+targetId); System.out.println("消息類型"+bytes[4]);
運行正常。
但是,總以爲在遍歷複製一遍有點繁瑣。那既然setInt32,setUInt8
這些方法是覆蓋數組裏的值的,那我可不能夠這樣寫呢?使用佔位符的方式,也就是說,在不影響消息體的狀況下,在轉化成byte
數組以後,前五位是佔位數據,後邊纔是正確的消息,那麼我重寫前五位的內容也不會受到什麼影響了,並且不用遍歷賦值了。說幹就幹,改爲代碼以下:this
var str = placeholder + JSON.stringify(d); var buff = new TextEncoder().encode(str); return buff;
問題就在於這個placeholder
的值是什麼呢?其實咱們使用小寫字母代替就能夠了。好比 'abcde',看一下轉換的結果:
不出我所料,那這樣的話,就沒問題啦,直接覆蓋前五位就能夠了。客戶端完整代碼以下:code
//根據layim提供的data數據,進行解析 var mine = data.mine, to = data.to, id = mine.id, group = to.type === 'group'; if (group) { id = to.id; } //構造消息 var msg = { username: mine.username , avatar: mine.avatar , id: id , type: to.type , content: mine.content }, targetId = to.id var dataBuff = this.encode(msg); var view1 = new DataView(dataBuff.buffer); view1.setInt32(0, targetId); view1.setInt8(4, group ? msgType.chatGroup : msgType.chatFriend); return view1.buffer;
運行一下,結果沒問題,大功告成!
server
從問題的發出到解決,雖然從博客上來看沒有什麼難度,可是本身在作的時候,搜了不少資料,嘗試了不少次也沒有結果,緣由是本身自動腦補了一些對象的操做API,基礎知識仍是很重要的啊,滾回去學習。雖然最終的思路不必定是最好的,或者還有一些其餘的問題,可是多嘗試一下仍是有不少意外的收穫。那麼問題來了。反序列化和數組的拷貝哪一個效率更高一些呢?
//這個好仍是 byte[] targetIdBytes = Arrays.copyOf(bytes,4); byte[] contents = Arrays.copyOfRange(bytes,5,bytes.length); //這個好呢? Json.toBean(new String(contents))