• nodejs如何实现基于window File Mapping的进程间通信(IPC)


         笔者碰到这样的需求,即如何将C++代码产生的图像数据高速的渲染到由Nodejs编写的WEB页面上,而且图像数据以每秒几十帧以上的速度产生,WEB界面显示要平滑连续不卡顿。

          针对这样的需求,笔者做了下调研,确定了需要解决如下的关键技术点:

    1. 如何在C++代码和nodejs代码之间高速交换数据。
    2. 如何在浏览器前端和Nodejs后端传输和渲染图像数据

    如何在C++代码和nodejs代码之间高速交换数据

         采用window File Mapping来进行内存共享,实现进程间通信,nodejs可以通过编写addon(插件)的方式来访问window File Mapping 的API。编写nodejs addon插件不是一件容易的事情,好在目前有一种node-addon-api技术可大大简化插件的开发,只要具备一定的C++知识即可编写,具体样例可参考:

    GitHub - nodejs/node-addon-api: Module for using Node-API from C++Module for using Node-API from C++. Contribute to nodejs/node-addon-api development by creating an account on GitHub.https://github.com/nodejs/node-addon-api#examples    关于window File Mapping的API使用则很简单,微软官方提供了简单的示例,只需要将这个示例按照node-addon-api的接口要求进行改造一下即可。关于window File Mapping的使用可参考

    Creating Named Shared Memory - Win32 apps | Microsoft LearnTo share data, multiple processes can use memory-mapped files that the system paging file stores.icon-default.png?t=M85Bhttps://learn.microsoft.com/en-us/windows/win32/memory/creating-named-shared-memory    根据上面所述,笔者编写了一个demo,生成addon, 在nodejs代码里可以方便调用,例如进程生产数据的代码类似如下:

    1. var addon = require('../build/Release/addon')
    2. var env = require('./env')
    3. var mapName = env.file_mapping_name;
    4. var bufSize = env.image_width*env.image_height;
    5. var fileMap = new addon.FileMap();
    6. //直接返回封装好的Buffer对象
    7. var buf = fileMap.create(mapName,bufSize);
    8. buf[0] = 1
    9. buf[1] = 2;

        这个插件的create方法创建了FileMapping对象并返回Buffer对象,这个方法没有进行内存拷贝操作,是直接引用并封装到javascript里的Buffer对象里,可以利用Buffer对象的方法直接进行数据操作。

       而消费者进程则也可以用该addon获取共享内存,消费数据,代码类似如下

    1. var addon = require('../build/Release/addon')
    2. var env = require('./env')
    3. var mapName = env.file_mapping_name;
    4. var bufSize = env.image_width*env.image_height;
    5. var fileMap = new addon.FileMap();
    6. var buf = fileMap.open(mapName,bufSize,0);
    7. if(buf)
    8. {
    9. console.log(`buf size=${buf.length}`);
    10. var value = buf[0];
    11. }

    如何在浏览器前端和Nodejs后端传输和渲染图像数据

        笔者使用了koa2作为Nodejs的web框架,使用上面提到的插件来获取图像数据,并通过web api返回二进制流数据给前端,代码示例如下:

    1. router.get('/image',async (ctx, next) =>{
    2. const fileMap = new addon.FileMap();
    3. var buf = fileMap.open(env.file_mapping_name, env.image_height*env.image_width,0);
    4. if(!buf)
    5. {
    6. console.log('file mapping is not opened');
    7. ctx.throw(500);
    8. }
    9. ctx.set("Content-Type", "application/octet-stream");
    10. ctx.response.body = buf;
    11. })

     而前端页面则采用canvas的双缓冲来绘制图像,并使用requestAninationFrame方法来起到平滑播放动画的效果,代码示例如下:

    1. const downloadData = async () => {
    2. const resp = await axios.get('/image',{responseType: 'arraybuffer'});
    3. return resp.data;
    4. }
    5. const sleep = (interval) => {
    6. return new Promise(resolve => {
    7. setTimeout(resolve, interval);
    8. })
    9. }
    10. var canvas = document.querySelector('#my-canvas');
    11. var context = canvas.getContext('2d');
    12. // to increase performance createImageData method
    13. // should be executed once e.g. before drawing
    14. var image = context.createImageData(canvas.width, canvas.height);
    15. var data = image.data;
    16. var dataView = new Uint32Array(data.buffer);
    17. const height = canvas.height;
    18. const width = canvas.width;
    19. const byteLength = height*width;
    20. function drawPixel(i, value) {
    21. dataView[i] =
    22. (255 << 24) | // alpha
    23. (value << 16) | // blue
    24. (value << 8) | // green
    25. value; // red
    26. }
    27. function swapBuffer() {
    28. context.putImageData(image, 0, 0);
    29. }
    30. const showData = async () => {
    31. var buf = await downloadData();
    32. if(buf.byteLength < byteLength)
    33. {
    34. console.log("error:downloaded buffer's size is smaller than canvas's size");
    35. return;
    36. }
    37. var bufView = new Uint8Array(buf);
    38. var t1 = new Date();
    39. for(var y = 0; y < height; ++y) {
    40. for(var x = 0; x < width; ++x) {
    41. var index = y*height + x;
    42. var gray = bufView[index];
    43. //var color = { r : gray, g: gray, b:gray, a:255 };
    44. drawPixel(index, gray);
    45. }
    46. }
    47. swapBuffer();
    48. var t2 = new Date();
    49. var dt = t2 - t1;
    50. console.log('elapsed time = ' + dt + ' ms');
    51. }
    52. const animate = () => {
    53. showData().then( () =>
    54. window.requestAnimationFrame(animate)
    55. ).catch(err => console.log(err));
    56. }
    57. window.requestAnimationFrame(animate)

    整个demo完整代码示例可访问码云

    node-filemap-buf: 基于node-addon-api编写的插件,通过调用window file mapping的api来获取内存,实现进程间内存共享https://gitee.com/bellcode/node-filemap-buf

  • 相关阅读:
    一款好看的markdown编辑器:md-editor-v3
    springboot视频网站毕业设计-附源码240925
    自动化脚本如何切换环境?Pytest这些功能你必须要掌握
    MySQL数据加解密处理
    LOGO特训营 第一节 鉴别Logo与Logo设计思路
    Nginx的安装
    机试打卡 -05 接雨水(动态规划&栈)
    Netty系列(三):Netty服务端发送消息到客户端
    【Linux】 ubuntu内存清理
    Spring cloud stream binder kafka 常用配置
  • 原文地址:https://blog.csdn.net/omage/article/details/126950230