標籤:響應 拼接 檢測 connect 數值 div eth int 串連
websocket進行通訊時,可以選擇採用字串或者位元組流的傳輸模式。但在發送與接收時,需要考慮資料的分包,即分成一個個請求與響應訊息。無論是採用哪種傳輸模式,都不免要遇到這個問題。
採用字串傳輸時,接收端可以將每次接收到的字串拼接到一起,再檢測是否出現了某一特定子串,比如連續兩個換行,即可將一個長的字串分隔成一個個的請求或響應訊息。這種處理方式比較簡單且有效。但這裡,介紹另一種模式,即傳輸位元組流。
首先考慮下分包的問題,一般分為訊息頭與訊息體。出於簡單目的,訊息頭裡只存放一個訊息體的長度,訊息體為位元組數組。
確定了資料包格式接下來可以寫實現代碼了,首先是串連:
var socket;var uri = "ws://" + window.location.host + "/push"; // 樣本地址function Connect(uri) { socket = new WebSocket(uri); socket.binaryType = "arraybuffer"; socket.onopen = function (e) { console.log("已串連至伺服器"); }; socket.onclose = function (e) { console.log("連結已關閉"); }; socket.onmessage = function (e) { doReceive(e.data); }; socket.onerror = function (e) { console.log("出現錯誤"); };}
這裡將socket變數定義為公用的,因為後續的發送方法會用到這個變數。預設JavaScript裡的WebSocket傳輸是採用字串模式的,採用UTF-8編碼,通過將binaryType屬性設定為arraybuffer來使用位元組流傳輸。
當發生onmessage事件時代表接收到資料,儲存在參數e.data裡。每一次接收都可能接收到一個完整的訊息或部分訊息,我們通過一個doReceive方法來進行訊息資料包的拆分。代碼如下:
var receive = [];var length = 0;function doReceive(buffer) { receive = receive.concat(Array.from(new Uint8Array(buffer))); if (receive.length < 4) { return; } length = new DataView(new Uint8Array(receive).buffer).getUint32(0); if (receive.length < length + 4) { return; } var bytes = receive.slice(4, length + 4); doSomething(bytes); receive = receive.slice(length + 4);};
其中receive作為接收緩衝區,每次接收到資料時先將其存到該緩衝區裡。之後檢查其長度是否大於等於4位元組,即上文定義的訊息頭長度。若滿足條件,則將其作為Uint32值讀取,代表訊息體長度。之後檢查緩衝區是否大於訊息頭加訊息體長度,若滿足則讀取訊息體,之後得到的bytes位元組數組即為完整的訊息體。最後,用剩餘位元組重設緩衝區以備下一次讀取。
其中buffer參數為ArrayBuffer類型,其代表原始的位元組數組,本身是無意義的。JavaScript通過一個個視圖來解釋這些位元組。Uint8Array即是其中一種視圖,它將ArrayBuffer中的位元組作為8位不帶正負號的整數來對待,正好一位元組對應一個uint8整數。類似的還有Uint16Array,它將ArrayBuffer中的位元組作為16位不帶正負號的整數來對待,則每兩位對應一個uint16整數。
而DataView是JavaScript API提供的一種視圖,他將ArrayBuffer中的資料作為網路流對待,它採用大端編碼,可以用它來讀取或寫入資料。這裡我們用它讀取了一個Uint32的值。
接下來是發送方法,代碼如下:
function doSend(bytes) { var buffer = new ArrayBuffer(bytes.length + 4); var view = new DataView(buffer); view.setUint32(0, bytes.length); for (var i = 0; i < bytes.length; i++) { view.setUint8(i + 4, bytes[i]); } socket.send(view);};
其中參數bytes是已經編碼過的位元組數組,這裡通過DataView視圖將其儲存到ArrayBuffer對象裡,以備發送。按照上文約定,需先設定Uint32型的訊息體長度,再設定訊息體。
最後,本文按約定的訊息格式來進行請求與響應訊息的傳輸,訊息體為不定長度的位元組序列。其本身是無意義的。我們可以通過API提供的Uint16Array、Uint32Array等視圖將其作為整數值序列,也可以自我實現其內容的解釋方式。
比如參考這裡將其作為採用UTF-8編碼的字串。之後可再將字串列印或還原序列化為JSON對象等。
JavaScript進行WebSocket位元組流通訊樣本