1、背景
公司項目有個需求,將發生的事件使用webSocket推送到前端(包括一張高清圖),要求1秒之內在web上顯示,且不能失真。
方案1:首先是將圖片轉換成base64,作爲字符串推送給前端,但是推送過來的信息量太大,導致接收信息延時。
方案2:改爲推送文件路徑,但是web請求圖片會有0.6-0.7毫秒的時間,一旦推送過多,會更慢,且web加載圖片時會有短暫的閃爍。
2、解決方案
將字符串信息壓縮後傳給web,但是java對於字符串壓縮量不大,因此通過node來進行處理。使用開源插件pako。
將node作爲服務端,java後臺(客戶端)通過socket將字符串發給node,node將壓縮後的圖片發送給java後臺(也可以直接通過node服務器發給web,當前項目重構量比較大,未使用)。再發給web去解壓。
3、代碼
node服務端代碼
/** * js壓縮函數 * @type {{}} */ const pako = require('pako'); /** * 導入websocket */ const websocket = require('./websocket.js'); var net = require('net'); var HOST = '127.0.0.1'; var PORT = 11111; /** * 壓縮函數 * @param str * @returns {void | number | * | Deflate.result} */ const gzip = function (str){ const _str = str || args[0] || ''; return pako.deflate(_str, { to: 'string' }); }; net.createServer(function(socket) { console.log('connection: ' + socket.remoteAddress + ':' + socket.remotePort); /** * 連接後 接收消息 */ let __data = ''; const BEGIN = '0x420x450x470x490x4E'; const END = '0x450x4E0x44'; const setData = function(data) { // endsWidth 無用 if (data.includes(END)) { const _data = gzip(__data.replace(BEGIN, '').replace(END, '')); // 測試代碼 // websocket.connections.forEach(function(conn) { // conn.sendText(_data); // }); socket.write(_data + '\n'); __data = ''; } }; socket.on('data', function(data) { data = data.toString(); const len = data.length; if (data.startsWith(BEGIN)) { __data = data; } else { __data += data; } setData(data); }); /** * 監聽關閉狀態 */ socket.on('close', function(data) { console.log('close: ' + socket.remoteAddress + ' ' + socket.remotePort); }); }).listen(PORT, HOST); console.log('Server listening on ' + HOST +':'+ PORT);
node websocket測試代碼
const ws = require('nodejs-websocket') const AllUserData = []; // Scream server example: 'hi' -> 'HI!!!' const connection = function (conn) { conn.on('text', function (str) { AllUserData.push({ 'id':str, 'ws':conn }); conn.sendText(str.toUpperCase()+'!!!') }); conn.on('close', function (code, reason) { console.log('Connection closed') // 當用戶退出的時候捕捉到退出的用戶 for (let i in AllUserData) { if (AllUserData[i].ws == conn) { console.log(AllUserData[i]) } } }); conn.on('error', function(code) { // 某些情況如果客戶端多次觸發連接關閉,會導致connection.close()出現異常,這裏try/catch一下 try { conn.close() } catch (error) { console.log('close異常', error) } console.log('異常關閉', code) }); } const server = ws.createServer(connection).listen(8001); module.exports = server; // console.log(server.connections);
java客戶端代碼
import java.io.*; import java.net.Socket; public class Pako { private Socket socket = null; // Socket 對象 private PrintWriter printWriter = null; // 發送事件對象 private BufferedReader bufferedReader = null; // 接收事件對象 public Pako() throws IOException { socket = new Socket("127.0.0.1", 11111); printWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); } /** * @param str * @return * @throws IOException */ public String getPakoDataBySocket(String str) throws IOException { // 事件發送 this.sendMsg(str); // 事件接收 return this.getMsg(); } /** * 關閉連接 * @throws IOException */ public void closeSocket() throws IOException { socket.close(); } /** * 發送事件 * @param str * @throws IOException */ public void sendMsg(String str) throws IOException { printWriter.println(str); printWriter.flush(); } /** * 接收事件 * @throws IOException * @return */ private String getMsg() throws IOException { return bufferedReader.readLine(); } }
java客戶端測試代碼
import java.io.IOException; import java.util.Date; public class Test { public static Pako pako; static { try { pako = new Pako(); } catch (IOException e) { } } public Test() throws IOException { } public static void main(String[] args) throws IOException { Test _test = new Test(); _test.test(); } public void test() throws IOException { String b64Data = "H4sIAAAAAAAAAJ3UMQ7CMAwF0KugP2ewEzdpcxXUAbWAOiHUMqCqdyeVQAobfGXIYL8hP5ZXnEdkeNEk6vUgXTbLonC4zMjHFY/5Wm511ekdTsOCLKVp2rlIKOA2jTuBot//cr7BhobEwsbAloY8kDGyqoQ5H/oHsdwQ21cCmaspCz0L2jcYOgLHhNGw4TT1yVmBpuS9PZHWY35siqnxvimEvpE9FY4peQhfbhO0FDnuFqWAEAAA=end"; for (int j = 0; j < 5; j++) { try{ String _str = ""; for (int i = 0; i < 1000; i++) { _str += b64Data; } _str = "0x420x450x470x490x4E" + _str + "0x450x4E0x44"; System.out.println(new Date().getTime()); String str = pako.getPakoDataBySocket(_str); Thread thread = Thread.currentThread(); thread.sleep(1);//暫停1毫秒後程序繼續執行1 }catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(new Date().getTime()); } }
web客戶端測試代碼
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>django-websocket</title> <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> <script src="./node_modules/pako/dist/pako.min.js"></script> <script type="text/javascript">//<![CDATA[ $(function () { function cnn() { if (window.s) { window.s.close() } /*創建socket連接*/ var socket = new WebSocket("ws://127.0.0.1:8001"); socket.onopen = function () { console.log('WebSocket open');//成功連接上Websocket }; socket.onmessage = function (e) { const str = pako.inflate(e.data, { to: 'string' }); console.log(new Date().getTime()) console.log(str.length);//打印出服務端返回過來的數據 $('#messagecontainer').prepend('<p>' + str + '</p>'); }; // Call onopen directly if socket is already open if (socket.readyState == WebSocket.OPEN) socket.onopen(); window.s = socket; } cnn(); $('#connect_websocket').click(function () { cnn(); }); $('#send_message').click(function () { //如果未連接到websocket if (!window.s) { alert("websocket未連接."); } else { window.s.send($('#message').val());//通過websocket發送數據 } }); $('#close_websocket').click(function () { if (window.s) { window.s.close();//關閉websocket console.log('websocket已關閉'); } }); }); //]]></script> </head> <body> <br> <input type="text" id="message" value="user1"/> <button type="button" id="connect_websocket">連接 websocket</button> <button type="button" id="send_message">發送 message</button> <button type="button" id="close_websocket">關閉 websocket</button> <h1>Received Messages</h1> <div id="messagecontainer"> </div> </body> </html>
4、耗時(24萬字符長度,包含解壓縮大概100毫秒)目前未找到socket一次傳輸大批量數據的方法
發送事件:0 發送時間:1571033967852
發送事件:1 發送時間:1571033967939
發送事件:2 發送時間:1571033967989
發送事件:3 發送時間:1571033968013
發送事件:4 發送時間:1571033968053
解決webSocket中傳輸base64圖片過大時的過慢問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.