PNaCl - How do i post message from index.html to PNaCl? - javascript

I run the hello_nacl example of nacl_sdk(pepper_39) and everything is fine.
But i try adding something on index.html to post message to PNaCl, it's not work and get error like this "NativeClient: NaCl module crashed".
This is my index.html, anyone can tell me what's wrong?
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
<script type="text/javascript">
var HelloTutorialModule = null;
function pageDidLoad() {
appendStatus('Page loaded');
HelloTutorialModule = document.getElementById('nacl_module');
HelloTutorialModule.postMessage('hello from HTML!!');
}
function appendStatus(opt_message) {
var statusField = document.getElementById('statusField');
if (statusField) {
var newElt = document.createElement("opt_message");
newElt.innerHTML = "<br>" + opt_message;
statusField.appendChild(newElt);
}
}
function handleMessage(message_event) {
appendStatus(message_event.data);
}
</script>
</head>
<body>
<div id="listener">
<script type="text/javascript">
var listener = document.getElementById('listener');
listener.addEventListener('message', handleMessage, true);
listener.addEventListener('load', pageDidLoad, true);
</script>
<h2>NaCl Module</h2>
<embed name="nacl_module"
id="nacl_module"
style="border-style: solid;"
width=200
height=200
src="newlib/hello_nacl.nmf"
type="application/x-nacl"/>
</div>
Thank you.
The following code is my hello_nacl.c
/* Copyright (c) 2012 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
// This project demonstrates how to migrate a Windows desktop app to Native
// Client, running first as a Win32 application (define STEP1), then as a PPAPI
// plugin (define STEP2 through STEP6), and finally as a Native Client module.
// Start with STEP1 defined and the defines for STEP2 through STEP6 commented
// out. For each step in the process, un-comment the next #define, leaving the
// previous ones on. Ready, set, port!
// *** SELECT THE WIN32 PLATFORM AND RUN WITH #define STEP1 ONLY ***
// #define STEP1
// Launches the original Windows desktop application, Hello World, which runs
// as WinMain. STEP1 encloses Windows-specific functions that are used with
// the WIN32 and PPAPI platforms. These will be removed when the full PPAPI
// port is finished (STEP6)
// *** SELECT THE PPAPI PLATFORM ***
#define STEP2
// What Changed: The platform launches Chrome, which will then load a Native
// Client Module. STEP2 encloses the Native Client module APIs needed to link
// any app to the browser. The module does nothing except report
// starting/ending the function Instance_DidCreate. The Windows app does not
// run because it is not being called.
#define STEP3
// What changed: Replace WinMain with WndProc, and call it from
// Instance_DidCreate, launching hello_nacl in its own window.
// Since WndProc spins in its message loop, the call to Instance_DidCreate
// never returns.
// Close the hello_nacl window and the module initialization will finish.
#define STEP4
// What changed: In WndProc replace the message loop with a callback function.
// Now the app window and the Native Client module are running concurrently.
#define STEP5
// What changed: Instance_DidCreate calls InitInstanceInBrowserWindow rather
// than InitInstanceInPCWindow.
// The InitInstanceInBrowserWindow uses postMessage to place text (now "Hello,
// Native Client") in the web page instead of opening and writing to a window.
#define STEP6
// What changed: All the Windows code is def'd out, to prove we are
// PPAPI-compliant. The functional code that is running is the same as STEP5.
// *** SELECT THE NACL64 PLATFORM AND RUN ***
// What changed: The code is the same as STEP6, but you are using the SDK
// toolchain to compile it into a nexe. The module is now running as a real
// Native Client executable in a NaCl sandbox, with nacl-gdb attached.
// *** RUN YOUR MODULE IN THE WILD ***
// You can run your nexe outside of Visual Studio, directly from Chrome by
// following these steps:
// - Build STEP6 and verify the file
// <project directory>/NaCl64/newlib/Debug/hello_nacl_64.nexe exists
// - Copy the folder <project directory> into your NaCl SDK's example
// directory.
// - Go to the NaCl SDK directory and launch the httpd.py server.
// - Launch Chrome, go to about:flags and enable the Native Client flag and
// relaunch Chrome
// - Point Chrome at http: //localhost:5103/hello_nacl
#ifdef STEP6
// remove Windows-dependent code.
#undef STEP1
#undef STEP3
#undef STEP4
#define NULL 0
#else
// includes for Windows APIs.
#include <windows.h>
#include <stdlib.h>
#include <tchar.h>
#endif
#ifdef STEP2
// includes for PPAPI
#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_messaging.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/c/ppb_core.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppp_messaging.h"
#include <string.h>
#include <stdio.h>
#include <time.h>
// Native Client APIs
static PPB_Messaging* ppb_messaging_interface = NULL;
static PPB_Var* ppb_var_interface = NULL;
static PPB_Core* ppb_core_interface = NULL;
PP_Instance myInstance;
int InitInstanceInPCWindow();
void InitInstanceInBrowserWindow();
#endif
#ifdef STEP4
// Implements message handling in a callback function.
void HelloWorldCallbackFun(void* user_data, int32_t result);
struct PP_CompletionCallback HelloWorldCallback = {
HelloWorldCallbackFun, NULL };
void HelloWorldCallbackFun(void* user_data, int32_t result) {
MSG uMsg;
if (PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
}
ppb_core_interface->CallOnMainThread(100, HelloWorldCallback, 0);
}
#endif
#ifdef STEP2
// The basic framework needed for all Native Client Modules. Handles creation
// of the module instance and initial handshake with the browser.
/**
* Creates new string PP_Var from C string. Useful utility for
* message-handling.
*/
static struct PP_Var CStrToVar(const char* str) { if (ppb_var_interface !=
NULL) { return ppb_var_interface->VarFromUtf8(str, strlen(str)); } return
PP_MakeUndefined(); }
void InitInstanceInBrowserWindow() {
// Pass the text to the browser page, there is no separate app window
// anymore. The text is added as a new element to the page, it does not
// appear in the module's embed view.
ppb_messaging_interface->PostMessage(myInstance, CStrToVar("Hello, Native Client! XDDXDXD"));
}
/**
* Called when the NaCl module is instantiated on the web page.
*/
static PP_Bool Instance_DidCreate(PP_Instance instance,
uint32_t argc,
const char* argn[],
const char* argv[]) {
myInstance = instance;
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Start Instance_DidCreate"));
#ifdef STEP5
// Will be included in STEP5 and STEP6
// Uses messaging to relay text to the module's view on the web page
InitInstanceInBrowserWindow();
#else
#ifdef STEP3
// Will be included in STEP3 and STEP4 only
// Uses WndProc to place text in a window separate from the browser.
InitInstanceInPCWindow();
#endif
#endif
ppb_messaging_interface->PostMessage(instance,
CStrToVar("End Instance_DidCreate"));
return PP_TRUE;
}
/**
* Called when the NaCl module is destroyed.
*/
static void Instance_DidDestroy(PP_Instance instance) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidDestroy"));
}
/**
* Called when the position, the size, or the clip rect of the element in the
* browser that corresponds to this NaCl module has changed.
*/
static void Instance_DidChangeView(PP_Instance instance,
PP_Resource view_resource) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidChangeView"));
}
/**
* Notification that the given NaCl module has gained or lost focus.
*/
static void Instance_DidChangeFocus(PP_Instance instance,
PP_Bool has_focus) {
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_DidChangeFocus"));
}
/**
* Handler that gets called after a full-frame module is instantiated based on
* registered MIME types. This function is not called on NaCl modules. This
* function is essentially a place-holder for the required function pointer in
* the PPP_Instance structure.
*/
static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance,
PP_Resource url_loader) {
/* NaCl modules do not need to handle the document load function. */
ppb_messaging_interface->PostMessage(instance,
CStrToVar("Instance_HandleDocumentLoad...."));
return PP_FALSE;
}
static void HandleMessage(PP_Instance instance, struct PP_Var message){
//ppb_messaging_interface->PostMessage(instance, CStrToVar("Get Message From HTML"));
}
/**
* Entry points for the module.
* Initialize needed interfaces: PPB_Core, PPB_Messaging and PPB_Var.
*/
PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id,
PPB_GetInterface get_browser) {
ppb_messaging_interface = (PPB_Messaging*)
get_browser(PPB_MESSAGING_INTERFACE);
ppb_var_interface = (PPB_Var*)get_browser(PPB_VAR_INTERFACE);
ppb_core_interface = (PPB_Core*)get_browser(PPB_CORE_INTERFACE);
return PP_OK;
}
/**
* Returns an interface pointer for the interface of the given name, or NULL
* if the interface is not supported.
*/
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad,
//&HandleMessage,
};
return &instance_interface;
}
return NULL;
}
/**
* Called before the plugin module is unloaded.
*/
PP_EXPORT void PPP_ShutdownModule() {
}
#endif
// **** Application Code ****
#ifdef STEP1
// Desktop Windows Hello World app. Native Client agnostic.
static TCHAR szWindowClass[] = _T("win32app");
static TCHAR szTitle[] = _T("hello_nacl");
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// WinMain
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
WNDCLASSEX wcex;
HWND hWnd;
MSG msg;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance,
MAKEINTRESOURCE(IDI_APPLICATION));
if (!RegisterClassEx(&wcex)) {
MessageBox(NULL,
_T("Call to RegisterClassEx failed!"),
_T("hello_nacl"),
0);
return 1;
}
hInst = hInstance;
hWnd = CreateWindow(
szWindowClass,
szTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
500, 100,
NULL,
NULL,
hInstance,
NULL);
if (!hWnd) {
MessageBox(NULL,
_T("Call to CreateWindow failed!"),
_T("hello_nacl"),
0);
return 1;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
// WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
PAINTSTRUCT ps;
HDC hdc;
TCHAR greeting[] = _T("Hello, World!");
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
TextOut(hdc, 5, 5, greeting, _tcslen(greeting));
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return 0;
}
#endif
#ifdef STEP3
// Replace WinMain with InitInstanceInPCWindow so the Native Client Module can
// launch the original application. Note the inclusion of a message-handling
// loop. STEP4 will replace the loop with a callback.
HINSTANCE g_hInstance = NULL;
HWND g_hWnd = NULL;
int InitInstanceInPCWindow() {
WNDCLASSEX winClass; MSG uMsg;
memset(&uMsg,0,sizeof(uMsg));
winClass.lpszClassName = _T("MY_WINDOWS_CLASS");
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
winClass.lpfnWndProc = WndProc;
winClass.hInstance = g_hInstance;
winClass.hIcon = NULL;
winClass.hIconSm = NULL;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName = NULL;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
if (!RegisterClassEx(&winClass))
return E_FAIL;
g_hWnd = CreateWindowEx(
0, _T("MY_WINDOWS_CLASS"),
_T("hello_nacl"), WS_OVERLAPPEDWINDOW,
0, 0, 640, 480, NULL, NULL, g_hInstance, NULL);
if (g_hWnd == NULL)
return E_FAIL;
ShowWindow(g_hWnd, 1);
UpdateWindow(g_hWnd);
#ifdef STEP4
// Skip the message loop, schedule a callback instead to periodically check
// for messages. Here we schedule at 100ms intervals.
ppb_core_interface->CallOnMainThread(100, HelloWorldCallback, 0);
return 0;
#else
// Main message loop, Windows style.
while(uMsg.message != WM_QUIT) {
if (PeekMessage(&uMsg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
}
return uMsg.wParam;
#endif
}
#endif

There is a lot of extra code in this example, but here is the problem:
PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
static PPP_Instance instance_interface = {
&Instance_DidCreate,
&Instance_DidDestroy,
&Instance_DidChangeView,
&Instance_DidChangeFocus,
&Instance_HandleDocumentLoad,
//&HandleMessage,
};
return &instance_interface;
}
return NULL;
}
When you post a message to your module, this function is called requesting the PPP_Messaging;1.0 interface. This code is not handling that case, so it returns NULL to the caller, which then crashes.
This is not desirable behavior (it should probably produce an error instead), but it is not very surprising.
To fix this bug, you need to return an interface when PPP_MESSAGING_INTERFACE is requested:
...
} else if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
static PPP_Messaging messaging_interface = {
&HandleMessage,
};
return &messaging_interface;
}

Related

The `Module` variable's mechanism in Emscripten while compiling Pthread to workers

I am confused by the Module variable in Emsripten while compiling Pthread to Web Worker + Wasm
There is a simple Pthread code which just adds 1 in the shared variable sum in each thread. (simple.c is attached in the end.)
We can use the command to compile the Pthread code:
$ emcc simple.c -s USE_PTHREADS=1 -s PTHREAD_POOL_SIZE=4 -o simple.html
Emscripten will generate simple.html, simple.js, simple.worker.js, and simple.wasm.
In simple.worker.js, there is a snippet:
// simple.worker.js
var Module = {};
// ...
Module['instantiateWasm'] = function(info, receiveInstance) {
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
var instance = new WebAssembly.Instance(Module['wasmModule'], info);
// We don't need the module anymore; new threads will be spawned from the main thread.
Module['wasmModule'] = null;
receiveInstance(instance); // The second 'module' parameter is intentionally null here, we don't need to keep a ref to the Module object from here.
return instance.exports;
};
Note that it declares var Module = {} in the worker, and defines Module['instantiateWasm'].
However, Module['instantiateWasm'] is only called by simple.js, which the code snippet looks like:
//simple.js
var Module = {}
// ...
if (Module['instantiateWasm']) {
try {
var exports = Module['instantiateWasm'](info, receiveInstance);
return exports;
} catch(e) {
err('Module.instantiateWasm callback failed with error: ' + e);
return false;
}
}
// ...
As we can see, simple.js also declares var Module = {} too.
AFAIK, VAR global variables cannot be accessed across the main thread and its worker. I don't understand why simple.js can call Module['instantiateWasm'] as the Module of simple.js and the Module of simple.worker.js should be not the same thing.
Pthread Code:
// simple.c
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUMTHRDS 4
#define MAGNIFICATION 1e9
typedef struct
{
int thread_id;
double *sum;
} Arg;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
void *count_pi(void *arg)
{
Arg *data = (Arg *)arg;
int thread_id = data->thread_id;
double *sum = data->sum;
pthread_mutex_lock(&mutexsum);
*sum += 1;
pthread_mutex_unlock(&mutexsum);
printf("Thread %d: sum=%f\n", thread_id, *sum);
pthread_exit((void *)0);
}
int main(int argc, char *argv[])
{
pthread_mutex_init(&mutexsum, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
double *sum = malloc(sizeof(*sum));
*sum = 0;
Arg arg[NUMTHRDS];
for (int i = 0; i < NUMTHRDS; i++)
{
arg[i].thread_id = i;
arg[i].sum = sum;
pthread_create(&callThd[i], &attr, count_pi, (void *)&arg[i]);
}
pthread_attr_destroy(&attr);
void *status;
for (int i = 0; i < NUMTHRDS; i++)
{
pthread_join(callThd[i], &status);
}
printf("Final Sum = %f \n", *sum);
free(sum);
pthread_mutex_destroy(&mutexsum);
pthread_exit(NULL);
}
A main program sends self to worker.
// simple.js
// Ask the new worker to load up the Emscripten-compiled page. This is a heavy operation.
worker.postMessage({
'cmd': 'load',
// If the application main .js file was loaded from a Blob, then it is not possible
// to access the URL of the current script that could be passed to a Web Worker so that
// it could load up the same file. In that case, developer must either deliver the Blob
// object in Module['mainScriptUrlOrBlob'], or a URL to it, so that pthread Workers can
// independently load up the same main application file.
'urlOrBlob': Module['mainScriptUrlOrBlob'] || _scriptDir,
'wasmMemory': wasmMemory,
'wasmModule': wasmModule,
'DYNAMIC_BASE': DYNAMIC_BASE,
'DYNAMICTOP_PTR': DYNAMICTOP_PTR
});
and worker import that.
// simple.worker.js
if (typeof e.data.urlOrBlob === 'string') {
importScripts(e.data.urlOrBlob);
} else {
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
importScripts(objectUrl);
URL.revokeObjectURL(objectUrl);
}
Therefore Module isn't shared, but it is initialized independently.

Calling callback inside callback in native Node

I'm a node & C++ newbie so be clement.
I'm writing a native node addon.
My addon start a webcam streaming (using UVC lib) and I want every frame to be available to node.
My CC addon do something like
uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0)
Where:
devh: is the UVC device
ctrl: are device config
frameProcess: is my function callback to be called at every new frame
args: are arguments from javascript function.
The c++ callback is called every new frame and I want to simple print something like "new frame received" so my C++ is like:
void frameProcess(uvc_frame_t *frame, void *ptr) {
const FunctionCallbackInfo<Value> args = *((const FunctionCallbackInfo<Value>*)(ptr));
Isolate* isolate = args.GetIsolate();
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "new frame received") };
cb->Call(Null(isolate), argc, argv);
}
void testStreaming (const FunctionCallbackInfo<Value>& args) {
...
res = uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0);
puts("Streaming...");
Sleep(10000); /* stream for 10 seconds */
uvc_stop_streaming(devh);
puts("Done streaming.");
...
}
...
NODE_SET_METHOD(exports, "testStreaming", testDevice);
My js is something like:
'use strict';
var uvc = require('../build/Release/binding')
uvc.testStreaming(
function (x) {
console.log(x)
}
)
The problem is that node exit without any message or error when program reach cb->Call.
If I comment cb->Call row the program run for 10 seconds (continuosly calling the ) as programmed and then exit.
But if I uncomment cb->Call the program exit immediatly.
Your frameProcess() function should call v8::Function callback in the Node.js thread, see https://stackoverflow.com/a/28116160/1355844

Boost::asio::async_read_until never calls handler

I am trying to build a connection between two computers on a local network, one using a slightly modified version of the Boost Asio C++ TCP asynchronous server sample, the other one using NodeJS.
tcp_client.js :
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 14002;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
// Write a message to the socket as soon as the client is connected
//the server will receive it as message from the client
client.write('Hello');
});
// Add a 'data' event handler for the client socket
// data is what the server sent to this socket
client.on('data', function(data) {
var fs = require('fs');
fs.writeFile("test.txt", data, function(err) {
if(err) {
return console.log(err);
}
client.write("Data written"); // returns successful
console.log("The file was saved!");
});
});
// Add a 'close' event handler for the client socket
client.on('close', function() {
console.log('Connection closed');
});
tcpServer.cpp :
#include <ctime>
#include <iostream>
#include <fstream>
#include <string>
#include <unistd.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
extern string _coordinates;
using namespace std;
using boost::asio::ip::tcp;
std::string inline make_daytime_string() {
std:: time_t now = std::time(0);
return std::ctime(&now);
}
class tcp_connection
// Using shared_ptr and enable_shared_from_this
// because we want to keep the tcp_connection object alive
// as long as there is an operation that refers to it.
: public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service) {
cout << "Creates a pointer for the tcp connection" <<endl;
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket() {
return socket_;
}
// Call boost::asio::async_write() to serve the data to the client.
// We are using boost::asio::async_write(),
// rather than ip::tcp::socket::async_write_some(),
// to ensure that the entire block of data is sent.
void start() {
while(1) {
start_read();
// This is going to read after every 1ms the _coordinates variable
usleep(1000);
m_message = _coordinates;
boost::asio::async_write(
socket_,
boost::asio::buffer(m_message),
boost::bind(
&tcp_connection::handle_write,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
}
private:
tcp_connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
void start_read() {
// Start an asynchronous operation to read a newline-delimited message.
// When read, handle_read should kick in
boost::asio::async_read_until(
socket_,
input_buffer_,
'\n',
boost::bind(
&tcp_connection::handle_read,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
// When stream is received, handle the message from the client
void handle_read(const boost::system::error_code& ec) {
std::cout << "HANDLE_READ - line 101" << "\n";
messageFromClient_ = "";
if (!ec) {
// Extract the newline-delimited message from the buffer.
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
// Empty messages are heartbeats and so ignored.
if (!line.empty()) {
messageFromClient_ += line;
std::cout << "Received: " << line << "\n";
}
start_read();
}
else {
std::cout << "Error on receive: " << ec.message() << "\n";
}
}
// handle_write() is responsible for any further actions
// for this client connection.
void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/) {
m_message += "helloo\n";
}
tcp::socket socket_;
std::string m_message;
boost::asio::streambuf input_buffer_;
std::string messageFromClient_;
};
class tcp_server {
public:
// Constructor: initialises an acceptor to listen on TCP port 14002.
tcp_server(boost::asio::io_service& io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 14002))
{
// start_accept() creates a socket and
// initiates an asynchronous accept operation
// to wait for a new connection.
start_accept();
}
private:
void start_accept() {
// creates a socket
cout << "creating a new socket for the communication" <<endl;
tcp_connection::pointer new_connection = tcp_connection::create(acceptor_.get_io_service());
// initiates an asynchronous accept operation
// to wait for a new connection.
acceptor_.async_accept(
new_connection->socket(),
boost::bind(
&tcp_server::handle_accept,
this,
new_connection,
boost::asio::placeholders::error
)
);
}
// handle_accept() is called when the asynchronous accept operation
// initiated by start_accept() finishes. It services the client request
void handle_accept(tcp_connection::pointer new_connection, const boost::system::error_code& error) {
if (!error) {
cout << "Starting the new tcp connection" <<endl;
new_connection->start();
}
// Call start_accept() to initiate the next accept operation.
start_accept();
}
tcp::acceptor acceptor_;
};
int inline launch_server() {
try {
boost::asio::io_service io_service;
tcp_server server(io_service);
// Run the io_service object to perform asynchronous operations.
io_service.run();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
Sending the message from the C++ to NodeJS works (the async_write(..) in the start() while loop, which sends _coordinates every 1ms), but I can't manage to handle the messages coming from my NodeJS program :
When running (both programs on my computer, on localhost), the output of ss -tp | grep 14002 (14002 being the port), the Recv-Q/Send-Q of the NodeJS process are empty (and the socket.write(...) returns successful), while, for the C++ part, the Recv-Q is constantly growing and Send-Q is empty
Moreover, when running, all the cout .. of the handler_read() are not printed, which means that the async_read_until() function never calls the handler.
I tried all the overload versions of the async_read_until(), none of them works. And, as the messages are not of constant size, it seems that i have no choice but to use read_until.
I hope I didn't forget any useful information. Thank you for your help !
You are basically saturating your CPU with the infinite while loop in your tcp_connection::start method. Not only that it is saturating the CPU, it is also a bug in your design. Why would you want to continuously attach handlers for read and also send/write data to the socket in a infinite loop ? Most probably you want to write to the socket after receiving a request from the client.
Below are the methods I changed to make it work like a regular client-server:
void start() {
start_read();
// This is going to read after every 1ms the _coordinates variable
usleep(1000);
m_message = _coordinates;
}
void start_read() {
// Start an asynchronous operation to read a newline-delimited message.
// When read, handle_read should kick in
boost::asio::async_read_until(
socket_,
input_buffer_,
'\n',
boost::bind(
&tcp_connection::handle_read,
shared_from_this(),
boost::asio::placeholders::error
)
);
}
void handle_read(const boost::system::error_code& ec) {
std::cout << "HANDLE_READ - line 101" << "\n";
messageFromClient_ = "";
if (!ec) {
// Extract the newline-delimited message from the buffer.
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
// Empty messages are heartbeats and so ignored.
if (!line.empty()) {
messageFromClient_ += line;
std::cout << "Received: " << line << "\n";
}
start_read();
}
else {
std::cout << "Error on receive: " << ec.message() << "\n";
}
start_read();
boost::asio::async_write(
socket_,
boost::asio::buffer(m_message),
boost::bind(
&tcp_connection::handle_write,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
}
I am not sure what you are 'actually' trying to do based on the question as it stands now, but the above changes should be a good point to start with.

Does boost::asio support websockets?

I posted a question earlier asking why does my server (written in C++ and boost::asio) can't connect with a client (written in Javascript). Is the problem that the Javascript Websockets are different than boost::asio sockets ? Does boost::asio not support websockets ? What is the easiest way to work this out ?
Boost.Beast, now part of Boost, is built on top of Boost.Asio and works the way you expect. It comes with example code and documentation. Check it out here: www.boost.org/libs/beast
Here's a complete program that sends a message to the echo server:
#include <boost/beast/core.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
// Sends a WebSocket message and prints the response
int main(int argc, char** argv)
{
try
{
// Check command line arguments.
if(argc != 4)
{
std::cerr <<
"Usage: websocket-client-sync <host> <port> <text>\n" <<
"Example:\n" <<
" websocket-client-sync echo.websocket.org 80 \"Hello, world!\"\n";
return EXIT_FAILURE;
}
std::string host = argv[1];
auto const port = argv[2];
auto const text = argv[3];
// The io_context is required for all I/O
net::io_context ioc;
// These objects perform our I/O
tcp::resolver resolver{ioc};
websocket::stream<tcp::socket> ws{ioc};
// Look up the domain name
auto const results = resolver.resolve(host, port);
// Make the connection on the IP address we get from a lookup
auto ep = net::connect(ws.next_layer(), results);
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host += ':' + std::to_string(ep.port());
// Set a decorator to change the User-Agent of the handshake
ws.set_option(websocket::stream_base::decorator(
[](websocket::request_type& req)
{
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-client-coro");
}));
// Perform the websocket handshake
ws.handshake(host, "/");
// Send the message
ws.write(net::buffer(std::string(text)));
// This buffer will hold the incoming message
beast::flat_buffer buffer;
// Read a message into our buffer
ws.read(buffer);
// Close the WebSocket connection
ws.close(websocket::close_code::normal);
// If we get here then the connection is closed gracefully
// The make_printable() function helps print a ConstBufferSequence
std::cout << beast::make_printable(buffer.data()) << std::endl;
}
catch(std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

Writing ASYNC CPP nodejs (0.5.3+) modules

I´m searching for a way to build c++ modules for NodeJs with the current release (0.5.9).
By using the following tutorial and abstracting from nodes node_file.cc I was able to build a module by my self for node 0.5.3.
https://github.com/jedp/node-rot13/blob/master/src/rot13.cpp
But with node 0.5.4 some of the API´s must have changed because because I´m no longer able to warp functions with eio_* anymore.
Looking at node_file.cc I discovered that the eio_* wrapping is replaced by new ReqWrap classes.
For Example in this macro: https://gist.github.com/1303926
No I wonder what's the best way of writing async extensions?
Basically looking at the crypto node module solved my problem this module was not poluted with macros like the file module. I came up with a simple async module that calculates the sum of two integers:
#include <v8.h>
#include <node.h>
#include <stdlib.h>
#include <errno.h>
using namespace node;
using namespace v8;
struct Test_req
{
ssize_t result;
ssize_t int1;
ssize_t int2;
Persistent<Function> callback;
};
void TestWorker(uv_work_t* req)
{
Test_req* request = (Test_req*)req->data;
request->result = request->int1 + request->int2;
}
void TestAfter(uv_work_t* req)
{
HandleScope scope;
Test_req* request = (Test_req*)req->data;
delete req;
Handle<Value> argv[2];
// XXX: Error handling
argv[0] = Undefined();
argv[1] = Integer::New(request->result);
TryCatch try_catch;
request->callback->Call(Context::GetCurrent()->Global(), 2, argv);
if (try_catch.HasCaught())
{
FatalException(try_catch);
}
request->callback.Dispose();
delete request;
}
static Handle<Value> Test(const Arguments& args)
{
HandleScope scope;
if ( args.Length() < 3 || !args[0]->IsNumber() || !args[1]->IsNumber() )
{
return ThrowException(Exception::TypeError(String::New("Bad argument")));
}
ssize_t int1 ( args[0]->Int32Value() );
ssize_t int2 ( args[1]->Int32Value() );
if ( args[2]->IsFunction() )
{
Local<Function> callback = Local<Function>::Cast(args[2]);
Test_req* request = new Test_req;
request->callback = Persistent<Function>::New(callback);
request->int1 = int1;
request->int2 = int2;
uv_work_t* req = new uv_work_t();
req->data = request;
uv_queue_work(uv_default_loop(), req, TestWorker, TestAfter);
}
else
{
return ThrowException(Exception::TypeError(String::New("Callback missing")));
}
return Undefined();
}
extern "C"
{
static void init(Handle<Object> target)
{
HandleScope scope;
}
}
NODE_MODULE(node_AsyncTest, init);
On the node side you call the module like this:
var foo = process.binding('AsyncTest');
foo.Test(1,2,function(err,data){
console.log(err,data);
});
result:
undefined 3
Hope this is helpful ;)
Ps: Since the lack of node compiling extensions under windows. I´m building this directly into the core of the node windows port with the Visual Studio build solution.

Categories