Hamza Ouaghad
Hamza Ouaghad

Reputation: 589

How to use Socket.io with emscripten using a javascript library?

I am using emscripten to port a c++ project into the web, and web application that is going to interact with my C++ code is on NodeJs.

So, I am using Socket.io on Node.js, and I want to use it with my c++ code too, so I went with using a javascript library that uses socket.io code, however it does not seem to work.

I wrote this little example demonstrating this case:

#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <emscripten.h>

int val = 0;

extern "C" 
{
    extern void send_board(char* flat_board);
    extern bool receive_board(char** _string_dest);
}

void one_iter()
{
    #ifdef SEND
    char* c = "test";

    std::cout << val << std::endl;

    if(val%1000 == 0){
        send_board(c);
    }
    val++;
    #else
    char* c;
    if(receive_board(&c)) std::cout << "Received:" << c << std::endl;
    #endif
}


int main()
{
    emscripten_set_main_loop(one_iter, 0, 1);
    return 0;
}

and

mergeInto(LibraryManager.library, {
      send_board: function(message) {
        socket.on('connect', function(){
            socket.emit('game_request_sender_pipeline', {
                message: "Hi",
            });
        }); 
      },

      receive_board: function(_string_dest_in_c){

        socket.on('connect', function(){
            socket.on('game_request_receiver_pipeline' , function (message)
            {
                var msg = "Other side : " + message.message;
                var buffer = Module._malloc(message.message.length + 1);
                Module.writeStringToMemory(msg, buffer);
                setValue(_string_dest_in_c, buffer, '*');
                return true;
            });
        });

        return false;
      },
    });

and I compiled with the following:

// for the sending test
em++ main.cpp --js-library path_to_js_library.js -o socket.html -DSEND=1
// for the receiving test
em++ main.cpp --js-library path_to_js_library.js -o socket.html

and in Node.Js server code, I have:

io.on('connection', function (socket) {

        socket.on('game_request_sender_pipeline' ,  function (message) 
        {      
            console.log("game_request_sender_pipeline on.");
            socket.broadcast.emit('game_request_receiver_pipeline', {
                message: message.message,
            });
            console.log("game_request_receiver_pipeline emitted.");
        });
});

The result is pretty weird, til I thought that it was not working, I cancel the nodejs server and relaunched it, and then the results popped up in the browsers' console.

Upvotes: 1

Views: 1001

Answers (1)

Hamza Ouaghad
Hamza Ouaghad

Reputation: 589

It seems that the suggestions in the comment have sense to them.

emscripten_set_main_loop would simulate a synchronous behavior, however the calls from the javascript api are asynchronous due to socket.io

So to solve the issue, instead of using return statements and conditionally execute the code I want- whether that be on true or false- I thought of using callbacks.

The idea goes like this:

  1. In the main loop, there going to be a call to receive_board.
  2. receive_board is going to receive a success callback and a failure callback as a parameter. (The callbacks are C function)
  3. We are going to call the callbacks from c using Module.ccall.
  4. The callbacks actually contain the code I wanted to execute conditionally upon the receipt

In order to use ccall, we will have to use the keyword EMSCRIPTEN_KEEPALIVE in the function definition, and in order to avoid writing that keyword for each callback that is going to be defined, I decided to use it only for a one function that will call the callbacks.

extern "C"
{
 EMSCRIPTEN_KEEPALIVE void callback_invoker(void* fn_ptr, void* fn_arg)
 {
    // some kind of cast to the original function signature, and to the original fn_arg type
    (*fn_ptr)(fn_arg);
 }
}

And in the javascript side

mergeInto(LibraryManager.library, {
      receive_board: function(_string_dest_in_c, succcess_callback, failure_callback, s_cb_arg, f_cb_arg){

        socket.on('connect', function(){
            socket.on('game_request_receiver_pipeline' , function (message)
            {
                var msg = "Other side : " + message.message;
                var buffer = Module._malloc(message.message.length + 1);
                Module.writeStringToMemory(msg, buffer);
                setValue(_string_dest_in_c, buffer, '*');
                Module.ccal('callback_invoker', 'void', ['number', 'number'], [success_callback, s_cb_arg]);
                return;
            });
        });

        Module.ccal('callback_invoker', 'void', ['number', 'number'], [failure_callback, f_cb_arg]);
      },
    });

This way, I solved the aforementioned problem.


Inspired by this answer

Upvotes: 2

Related Questions