前言 了解node的开发者清楚, node不是一门语言, 而是一个平台. 多亏了libuv屏蔽了操作系统层面的差异, 优秀的js语言特性(有些人不以为然, 不评论)能让node做很多事情. 我们知道node擅长的部分是异步IO, 这里还是要感谢libuv, 但是不擅长的是CPU密集型操作, 这作为解释型脚本语言的弱项, 静态语言则反之表现出色.
Node的C/C++模块 在朴老师的深浅node中指出, node with c++ module的fab数列计算数列与c语言不相上下, 当然C/C++作为node这一平台的补充是完全没有问题的, 但是这里朴老师明显是有误导读者的嫌疑(“感觉”node的计算速度比得上c). C/C++模块的另一大用处是可以获得系统的完全操作权限, 使用C的第三方lib, 完成js不能完成的任务, 例如线程层面的Sleep, 以及扩展node支持不好的crypto模块等等. 但是使用C/C++模块的时候, 要认识到我们其实是绕开了js/libuv提供的一些便利, 所以在开发的时候, 需要特别注意平台兼容性, 内存管理等等.
准备 之前给腾讯的微校开发CET无准考证查询功能, 由于api使用了加密手段, 导致调用起来比较麻烦, 得知其加密手法为CFB模式下64位的DES, 稍微查了一下node手册的crypto模块, 没有发现node对这个方法的支持, 而且考虑到js对加解密计算的弱项, 便打算借助C/C++模块配合openssl实现. 我们使用node-gyp工具进行模块的交叉编译, 由于nodejs4.0以上使用了新的C++ Compiler11标准, 所以需要更新到gcc >=4.8才能对第三方C/C++模块进行编译, centos安装devtoolset3, ubuntu使用ppa源升级, 我在上一篇文章提到了如何升级 .
NAN模块 由于node各个版本使用的V8版本差异, 所以编写C/C++模块的时候语法差异比较大, 这里我们使用nan 这个包抹平这些差异, nan提供了一个包含很多抹平差异宏定义实现的nan.h文件, 所以我们只要了解NAN这一套就可以了.
然后新建一个binding.gyp
文件.
1 2 3 4 5 6 7 8 9 10 11 { "targets ": [ { "target_name ": "cetdecoder-main" , "sources ": [ "cetdecoder-main.cc" ] , "include_dirs ": [ "<!(node -e \"require('nan')\")" ] } ] }
C/C++ 先创建一个cc文件, 来看看里面的内容
这里添加了几个函数, 它的参数类型是Nan::FunctionCallbackInfo& info```, 然后Init方法里的`exports`方法把这两个函数以"decodeTicket"和"encodeRequest"作为方法名暴露给node. 1
这里可以看到, 从`char*`建立一个v8中String类型对象的方法是: `Nan::New("string").ToLocalChecked()`, 相同的初始化函数是`Nan::New<v8::FunctionTemplate>(func)->GetFunction()`.
最后的`NODE_MODULE`宏完成整个对象的暴露, 这一句与`Init`方法相当于node中的
``` javascript
module.exports = {
foo: bar
}
再来看具体方法内的操作方法, 这里我们要关心的是如何将js中也就是V8的数据类型转化成c/c++的数据类型.
这个函数的需求是传入一个Buffer, 对Buffer中的二进制数据进行位解码操作后返回对应的Buffer.
这里比较惭愧, 我翻了手册好几遍都没找到如何转化Buffer对象的方法(粗粗看了一下, doc的位置在node_modules/nan/doc), 为了方便, 我这里就传入一个数组, 例如[0x41, 0x6d, 0x44, ...]
, 然后返回另一个数组.
当然我也没有找到如何转化数组的方法, 但是我找到了一个args.Length()
的方法, 于是借助js的apply
, 再在C中使用一个循环就ok了. 这里具体的获取某一个参数方法为info[i]->NumberValue()
, 查V8手册 可知返回值是一个double
类型, 这里我们根据需要转化成char类型的一个数组.
接下来是返回的数组的创建.
1 2 3 4 5 v8::Local<v8::Array > retArr = Nan::New<v8::Array >(len); for (i = 0 ; i < len; i++) { Nan::Set (retArr, i, Nan::New(ret[i])); } info.GetReturnValue().Set(retArr);
编译 1 node-gyp configure
node-gyp build
也可以使用一句话build
1 node-gyp configure build
生成的.node文件位于./build/Release
, 可在node中直接require
可以在package.json
中加入"gypfile": true
让其自动编译
需要注意的地方 其实这里我并没有使用NAN的example, nan的标准创建函数方法是使用NAN_METHOD宏.我建议参照NAN官方写法为了升级方便.
后记 因为做的比较急, 对V8的了解还不够深刻, 我甚至没有C++基础(只有很少的C), 例如Scope等概念还不是很清楚. 所以C/C++模块的开发对大部分前端工程师来说, 还真是一个技术活.
这里是我的一个粗浅记录, 如果有什么不恰当的欢迎指出.
发表于我的博客Zero’s Corner .