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图片过大时的过慢问题
最新推荐文章于 2025-03-22 19:32:08 发布