目前OWT服务端使用了两种方式开发C++ addon: 使用内部的V8、libuv 和 Node.js 库 第三方NAN库 OWT服务端更新最频繁的数WebRTC agent代码了,目前WebRTC agent用的是NAN方式开发C++ addon,其他agent用的是内部的V8、libuv 和 Node.js 库开发C++ addon,开发起来会比较复杂,需要熟悉Nodejs各个组件的知识以及libuv、V8相关API调用。所以对于新开发的C++ addon,推荐使用NAN方式开发,简单,而且不用考虑Nodejs升级后的兼容性问题。 1. NAN简介 NAN的全称为**Native Abstraction for Node.js** , 其表现上是一个Node.js包。安装后,就得到一堆C++头文件,里面是一堆宏。它主要为Node.js和V8跨版本提供了封装的宏,使得开发者不用关心各个版本之间的API的差异。 NAN官方地址为:https://github.com/nodejs/nan。 NAN的优势在于可以屏蔽不同版本Node的API,使得C++ addon可以wirte once, compile anywhere,一份C++ addon可以适用于不同版本的Nodejs。 接下来我们以WebRTC agent中的代码简单讲解下如何使用NAN开发一个C++ addon。 2. NAN C++ addon开发 这里我们以WebRTC agent中的rtcConn addon作为示例,代码位于source/agent/webrtc/rtcConn中。 2.1 使用 安装NAN包: [crayon-69cbc6099ec3b110483709/] 编写binding.gyp,将NAN路径添加到binding.gyp中: [crayon-69cbc6099ec4a938506941/] NAN的路径会通过node -e "require('nan')"获取。 2.2 目录结构 如下是rtcConn addon的目录结构: [crayon-69cbc6099ec4e868786110/] 在每个C++ addon中都包含:binding.gyp,addon.cc。 binding.gyp。使用JSON风格,包含当前addon的编译配置,例如用到的文件以及库 addon.cc。类似于main函数入口,调用包含的各个C++类的Init接口 binding.gyp 如下是rtcConn addon中,binding.gyp部分内容。 首先通过target_name定义了该C++ addon名称,这样会在build目录下生成target_name.node文件。接着包含相关源码文件以及链接用到的第三方库。 [crayon-69cbc6099ec51812051288/] addon.cc 调用用到的各个C++类的Init方法,这些C++类都是按照NAN规定的方式进行wrap,这样我们就可以将这些C++类导出,以addon.cppclass方式调用各个C++类。 [crayon-69cbc6099ec56292672397/] 目录中除了binding.gyp,addon.cc,剩下的都是用到的各个C++类NAN方式wrap后的文件。 OWT服务端不同agent代码文件命名,以及代码规范比较乱。这里规定新代码中wrap后的文件名最好加上Wrapper修饰,便于区分。可以参照WebRTC agent中最新的rtcFrame addon(基于WebRTC SDK 实现的一个C++ addon)。 2.3 NAN wrap C++类 我们的C++类都要按照NAN规定的方式wrap,这样Nodejs层才可以调用。 我们有个licode中的erizo::WebRtcConnection(third_party/licode/erizo/src/erizo/WebRtcConnection.h)类,用于处理WebRTC连接相关的配置信息,我们要将它提供给Nodejs层调用。我们可以通过如下方式进行wrap,这里看下wrap后的类头文件大概内容。 [crayon-69cbc6099ec59870742415/] 对于C++类要导出的接口使用NAN_METHOD进行修饰。我们要wrap的类作为me成员。 2.3.1 NAN_MODULE_INIT 用于定义该模块的入口函数。在这里注册要使用的C++类接口,这样Nodejs层才可以调用。当Nodejs层执行require('path/xxx.node')的时候,就会执行该函数。 [crayon-69cbc6099ec5c678054252/] 2.3.2 NAN_METHOD(New) 构造函数入口,在Nodejs层New一个该C++ wrap类时,就会进入这里。在这里可通过判断Nodejs层传入的参数个数,调用不同的构造函数。 我们通过info.Length()判断传入的参数个数。 2.3.3 NAN_METHOD(setRemoteSdp) 通过这个函数讲下怎么传递参数到C++。这个函数主要传递Nodejs层的字符串参数到C++底层。 [crayon-69cbc6099ec5f624297077/] 2.3.4 NAUV_WORK_CB 通过这个函数讲下怎么将C++底层的数据回调到Nodejs层。大概函数调用层次如下: [crayon-69cbc6099ec63792747667/] 首先按NAN方式定义一个回调 [crayon-69cbc6099ec66425610006/] 定义一个抽象类,在这里定义回调接口,其实就是常见的观察者模式 [crayon-69cbc6099ec69306334712/] NAN方式wrap后的类继承前一步定义的抽象类 [crayon-69cbc6099ec6c215071411/] 实现抽象类的回调接口 [crayon-69cbc6099ec6f519438731/] NAN_METHOD(New)中构造对象时,将wrap的类对象传递进去 [crayon-69cbc6099ec72794062335/] 看下erizo::WebRtcConnection中的实现: [crayon-69cbc6099ec75628533628/] 通过conn_event_listener_进行各种事件回调: [crayon-69cbc6099ec78172240913/] 这样回调的信息就会传到NAN这一层,NAN这一层再调用Nodejs层的回调函数,即可将信息回调到Nodejs层。 Nodejs层回调 函数这里我们通过调用定义的init接口获取Nodejs层的回调函数 [crayon-69cbc6099ec7b295804334/] 这里看下Nodejs层代码调用: [crayon-69cbc6099ec7e701462297/] 回调事件队列处理 [crayon-69cbc6099ec81734171831/] eventsCallback与libuv的绑定在NAN_METHOD(WebRtcConnection::New)中: [crayon-69cbc6099ec84783512936/] 这样调用uv_async_send(&async_)就会异步执行eventsCallback。 退出时关闭处理 [crayon-69cbc6099ec87210381941/] 2.4 Nodejs层调用 编译后如下方式引入: [crayon-69cbc6099ec8a106839957/] 构造: [crayon-69cbc6099ec8d865629769/] 传入回调函数: [crayon-69cbc6099ec90625897548/] 相关接口调用: [crayon-69cbc6099ec93175619761/] 3. 总结 本文简单介绍了目前服务端Nodejs c++ addon开发,通过列举的参数传递,回调例子,对着相关代码过一遍,基本能快速上手。