Node引入C++库文件

由于项目引入了一个通过C++语言实现的加密算法,需要在Node.js中调用相关端口实现加密,所以尝试实现了下载node.js中引入C++库文件。
此项目中使用的引入方法是直接使用node-gym模块编译一个基于C++的node.js模块,然后在主程序中引入这个模块。

node-gym

node-gyp 是基于 GYP( 全称 Generate Your Projects,是谷歌开发的一套构建系统) 的。它会识别包或者项目中的 binding.gyp文件,这个里面是JSON的文件对工程依赖的各种文件进行了描述(可以理解为一个node版的CMakeList),然后根据该配置文件生成各系统下能进行编译的项目,如 Windows 下生成 Visual Studio 项目文件(*.sln 等),Unix 下生成 Makefile。在生成这些项目文件之后,node-gyp 还能调用各系统的编译工具(如 GCC)来将项目进行编译,得到最后的动态链接库 *.node 文件。

cpp接口文件与gym编写

插件通常都会暴漏某些对象和函数给 Node.js 中的 JavaScript 调用。当 JavaScript 调用函数时,也必须将将传入的参数映射给 C/C++ 代码,在函数调用完成后,还要映射 C/C++ 传回的返回值。
下面代码演示了如何读取 JavaScript 传递来的函数以及如何传输返回值:
C++文件如下:

// AESencrypt.cpp
#include 
//此处引入要调用的头文件
#include 
namespace demo {

using v8::Exception;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

// 此处是encrypt方法的执行函数,在js中调用的参数通过args传入
void Encrypt(const FunctionCallbackInfo& args) {
  Isolate* isolate = args.GetIsolate();

  // 检查参数个数
  if (args.Length() < 3) {
    // 如果个数不正确便扔回错误到JavaScript
    isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "Wrong number of arguments")));
    return;
  }

  // 检查参数类型
  if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
    isolate->ThrowException(Exception::TypeError(
        String::NewFromUtf8(isolate, "Wrong arguments")));
    return;
  }
//当需要参数为string类型时的示例
     v8::String::Utf8Value param(args[2]->ToString());
     std::string inputstr= std::string(*param);

char buffer[60];
  // 执行主函数
aes_encrypt((char *)inputstr.c_str(),args[0],args[1],buffer);

  // 设定返回值 (using the passed in
  // FunctionCallbackInfo&)(返回一个字符串)
  args.GetReturnValue().Set(String::NewFromUtf8(isolate, buffer));
}

void Init(Local exports) {
  NODE_SET_METHOD(exports, "encrypt", Encrypt);
}

NODE_MODULE(AESencrypt, Init)

}  // namespace demo

值得注意的是,开发 Node.js 插件的开发时,必须输出一个初始化函数,模式如下:

void Initialize(Local exports);
NODE_MODULE(module_name, Initialize)

在 NODE_MODULE 的行末没有分号,因为它并不是一次函数调用(详见 node.h)。module_name 必须二进制文件名相匹配(不包含文件名的 .node 后缀)。通过上述代码,我们在 AESencrypt.cpp 文件中声明了初始化函数是 Init,插件名称是 encrypt

接下来编写gym文件

{
     "targets": [
     {
         "target_name": "AESencrypt",
             "sources": [ "AESencrypt.cpp" ],
             "include_dirs": ["include"],
             "libraries": [ "<(module_root_dir)/obj/lib/libencrypt.a" ],
             "cflags!": [ "-fno-exceptions" ],
             "cflags": [ "-std=c++11","-D_FILE_OFFSET_BITS=64"],
             "cflags_cc!": [ "-fno-exceptions" ]
    }
  ]
}

其中libraries中引用库文件就好,其中一些关于跨系统的兼容可以参考官方文档

接下来执行

node-gyp configure  
node-gyp build  

编译文件生成一个.node文件

在js代码中

const AES = require('./build/Release/AESencrypt.node');

引用,然后使用AES.encrypt()调用即可

一些问题总结

  • 编译中引用静态库造成的问题
    由于编译生成的是动态库,引用的是静态库,在编译时可能弹出
make: Entering directory '/home/work/work/testAES/build'
  ACTION Regenerating Makefile
  SOLINK_MODULE(target) Release/obj.target/testAES.node
/usr/bin/ld: /home/work/work/testAES/obj/lib/libaes.a(test_aes.c.o): relocation R_X86_64_32S against `.rodata.cst16' can not be used when making a shared object; recompile with -fPIC
/home/work/work/testAES/obj/lib/libaes.a: error adding symbols: Bad value
collect2: error: ld returned 1 exit status

报错。此时需要重新加"-fPic"参数编译一遍库文件。
在cmake中需要写:

set(CMAKE_POSITION_INDEPENDENT_CODE ON)
  • 自动生成
    由于最终需要提供给用户使用,模块在不同环境下都需要重新编译一遍,而每次部署都让用户执行一次node-gyp configure build明显不现实。直接在package.json里面加上一句
gypfile": true

让每次执行npm install的时候自动编译一遍,让问题能方便很多

还可以考察的一些模块

  • node-ffi: 这个模块可以直接引入C++的库,实现不用操作任何C++代码的C++库文件引入
  • node-pre-gym:

你可能感兴趣的:(Node引入C++库文件)