nodejs / node-addon-examples

Node.js C++ addon examples from http://nodejs.org/docs/latest/api/addons.html
Other
2.5k stars 598 forks source link

Talking to C++ process through N-API #124

Open VincentAAX opened 4 years ago

VincentAAX commented 4 years ago

Hello; I want to send and receive data from a game simulation in a seperate C++ process, from Node.js. I want Node.js to connect to the outside internet and Node.js will need to communicate with a game simulation run in a seperate process.

Memory sharing seems to be the fastest way to do interprocess communication. Its faster than pipes or sockets, so I want to use memory sharing to do the IPC. I was thinking that N-API could be used to do this.

The question is can I use N-API to connect to a C++ DLL and create a shared memory object (file map). The DLL will be loaded into the Node.js process and it will load a DLL that has already been loaded by the C++ executable. The the file mapping of the system pagefile will be loaded into both the Node.js process and the seperate C++ process. To send something from Node.js to the C++ process, I would just call and required funtions., which would call a function in the DLL that wrote to the share memory map file. After the shared memory has been written to the, it would unblock a handle that the server has access to. The C++ process could then read the shared memory. Has anyone tried this? I mean N-API allows on to call C++ ftns in a DLL, but to call ftns in a seperate process is what I want.

NickNaso commented 4 years ago

Hi @VincentAAX, what I understood is that you have two different processes one is a Node.js application and another is a C++ application and you need to pass messages (data) between these two processes to create a sort of IPC communication. In theory you could create a native add-on that that use your dll to exchange / pass data to another process. In the Node.js ecosystem there is an example of this kind of native module that bind the api of ZeroMQ. https://github.com/zeromq/zeromq.js.

VincentAAX commented 4 years ago

Hey Nick;

I want to communicate to a C++ app in another process and I want to do by shared memory. I believe the zeromq.js does this by sockets. Shared memory is much faster.

NickNaso commented 4 years ago

Hi @VincentAAX, I don't know how to use the shared memory but in general you could use the ObjectWrap API .
You could do something like this:

index.js

'use strict'

const Shmt = require('YOUR-NATIVE-ADDON')

const opts = {
    name: 'test',
    // ...    
}

const shmt = new Shmt(opts)

try {
    shmt.open()
    shmt.put(key, value)
    // When you don't need to access shared memory you have to release it
   shmt.close()
} catch (err) {
    // Handle the error
}

shmt.h

#include <napi.h>

// Include headers that allow you to use the shared memory

class Shmt : public Napi::ObjectWrap<Shmt> {

    public:
        static Napi::Object Init(Napi::Env env, Napi::Object exports);
        Shmt(const Napi::CallbackInfo& info);

    private:
       // Reference to shared memory
        static Napi::FunctionReference constructor;
        Napi::Value Open(const Napi::CallbackInfo& info);
        Napi::Value Close(const Napi::CallbackInfo& info);
        Napi::Value Get(const Napi::CallbackInfo& info);
        Napi::Value Del(const Napi::CallbackInfo& info);
        Napi::Value Put(const Napi::CallbackInfo& info);     
};

shmt.cc

#include "shmt.h"

Napi::FunctionReference Shmt::constructor;
Napi::Object Shmt::Init(Napi::Env env, Napi::Object exports) {
    Napi::HandleScope scope(env);
    Napi::Function func = DefineClass(env, "Shmt", {
        InstanceMethod("open", &Shmt::Open),
        InstanceMethod("close", &Shmt::Close),
        InstanceMethod("get", &Shmt::Get),
        InstanceMethod("del", &Shmt::Del),
        InstanceMethod("put", &Shmt::Put)
    });

    constructor = Napi::Persistent(func);
    constructor.SuppressDestruct();

    exports.Set("Shmt", func);
    return exports;
}

Shmt::Shmt(const Napi::CallbackInfo& info) 
: Napi::ObjectWrap<Shmt>(info)  {
    // NOOP
}

Napi::Value Shmt::Open(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();
    // Initialize shared memory
    return info.Env().Undefined();
}

Napi::Value Shmt::Close(const Napi::CallbackInfo& info) {
    // Release the shared memory
    return info.Env().Undefined();
}

Napi::Value Shmt::Get(const Napi::CallbackInfo& info) {
   // Get a value from the shared memory
    return info.Env().Undefined();
}

Napi::Value Shmt::Del(const Napi::CallbackInfo& info) {
   // Remove a value from the shared memory
    return info.Env().Undefined();
}

Napi::Value Database::Put(const Napi::CallbackInfo& info) {
   // Insert a new value in the shared memory
    return info.Env().Undefined();
}

The code is only to give you an idea maybe it contains some errors.