node.jsとネイティブアプリで通信を行う方法について

5,592 views
Skip to first unread message

竹内佑介

unread,
Apr 7, 2013, 4:04:24 AM4/7/13
to node...@googlegroups.com
はじめまして、竹内佑介と申します。

node.js初心者なので基本的な質問になりますが、
node.jsとネイティブアプリで通信を行う方法にはどのようなものがあるかについて
教えて頂けないでしょうか。

現在私がやろうとしていることは、
Windowsサービス上で動作しているC++モジュールのコンソール上メッセージを、
Webソケットを用いてブラウザ上で表示させるというものです。
メッセージの流れとしては、
   C++モジュール -> node.js ->Webブラウザ
というものを考えています。

node.jsとwebブラウザ間の通信は情報が沢山ありましたが、
C++モジュールとnode.jsでの通信については分からないことが多かったので、
質問をさせて頂きました。

以上、ご確認の程よろしくお願い致します。

Shigeki Ohtsu

unread,
Apr 7, 2013, 11:59:58 PM4/7/13
to node...@googlegroups.com
大津です。

具体的にどのようなネイティブアプリを考えてらっしゃるのかがわからないので
すが、

> node.js初心者なので基本的な質問になりますが、
> node.jsとネイティブアプリで通信を行う方法にはどのようなものがあるかに
> ついて教えて頂けないでしょうか。

**一般的な広い意味**での「Node.jsとネイティブアプリで通信を行う方法」

というのでしたらソケット通信と名前付きパイプの2つの種類のプロセス間通信
が挙げられます。(後者は同一サーバ上であることが条件です)どちらも
Node.js の net モジュールを使って利用することができます。

> 現在私がやろうとしていることは、
> Windowsサービス上で動作しているC++モジュールのコンソール上メッセージ
> を、Webソケットを用いてブラウザ上で表示させるというものです。

コンソール上の標準出力に出続けているものなら、そのままコマンドパイプで
Nodeの標準入力で受けるのが簡単じゃないでしょうか?

https://gist.github.com/shigeki/5334078

な感じで Nodeの標準入力をそのまま WebSocket に渡すことができるので、あと
はパイプでつないでやればいいんじゃないかと思います。

竹内佑介

unread,
Apr 8, 2013, 1:38:50 AM4/8/13
to node...@googlegroups.com
大津さん

質問へのご回答、ありがとうございました。
とりあえず、コマンドパイプの方法を試してみます。
また何かありましたら、よろしくお願いします。

以上です。


2013年4月8日 12:59 Shigeki Ohtsu <oh...@iij.ad.jp>:

--

---
このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。
このグループから退会し、メールの受信を停止するには、nodejs_jp+...@googlegroups.com にメールを送信します。
その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。





--
*********************************************
電気通信大学情報システム学研究科
社会知能情報学専攻2年
竹内 佑介(たけうち ゆうすけ)
e-mail:kaido...@gmail.com
*********************************************

open.tommie

unread,
Apr 8, 2013, 1:55:36 AM4/8/13
to node...@googlegroups.com
tommieと申します。

整理すると、
同じマシン上なら、Node.jsとネイティブアプリの構成方法として、以下が考えられますね。

1.Node.jsとネイティブアプリを別プロセスとして実行し、プロセス間通信でデータ交換する
    → 大津さんの回答

2.Node.jsとネイティブアプリを同一プロセスで別スレッドとして実行し、スレッド間通信でデータ交換する

興味があったので2の同一プロセス別スレッドの場合について調べてみた所、
libuvのuv_queue_work()が使えるようです。

以下のサイトが参考になりました。

Node.js でマルチスレッド対応のネイティブモジュールを作成する
http://d.hatena.ne.jp/hecomi/20121021/1350819390

ただし、Windowsで動くのか不明ですが。



(2013/04/08 14:38), 竹内佑介 wrote:
大 津さん

質 問へのご回答、ありがとうございました。

Shigeki Ohtsu

unread,
Apr 8, 2013, 2:39:54 AM4/8/13
to node...@googlegroups.com
大津です。

> 2.Node.jsとネイティブアプリを同一プロセスで別スレッドとして実行し、ス
> レッド間通信でデータ交換する

おー、ネイティブアドオン化ですか。 質問者の方が「C++モジュール」と書かれ
てたので、最初ネイティブアドオンの事かと思いましたが、「コンソール上メッ
セージ出力」とのことなので独立したネイティブアプリケーションと読み替えて
ました。

>
> 興味があったので2の同一プロセス別スレッドの場合について調べてみた所、
> libuvのuv_queue_work()が使えるようです。

- 「C++モジュール」というのが dll などのライブラリ化されている。
- 「C++モジュール」内の処理が iterative で、(コンソールに出力される予定
の)メッセージ(のポインター)が取得できる

という条件であれば可能でしょうね。

> ただし、Windowsで動くのか不明ですが。

libuv API は Windows でも動くので大丈夫でしょう。(というか動かなかったら
バグです。)

ちなみに node-v0.10.x で Unix の libuv threadpool 実装は、おもいっきり拡
張されています。

もはや libeio の 4スレッド制限は過去のもので、最大128スレッドまで可能で
す。マルチコア+高性能ファイルI/O な環境で Node を使われている方は、どの
程度性能向上するか一度試してみるといいでしょう。

open.tommie

unread,
Apr 8, 2013, 5:15:28 AM4/8/13
to node...@googlegroups.com
tommieです。

以下のサイトによると、
uv_async_send()を使えば別スレッドからいつでもメッセージを送信できるので
すね。

Inter-thread communication
http://nikhilm.github.io/uvbook/threads.html#inter-thread-communication

> 現在私がやろうとしていることは、
> Windowsサービス上で動作しているC++モジュールのコンソール上メッセージを、
> Webソケットを用いてブラウザ上で表示させるというものです。

上記サイトのサンプルコードで説明すると、以下のような流れになると思います。

1.fake_download()内で、コンソール上に出力していたメッセージを
uv_async_send()で送信
2.print_progress()内で、Node.jsのコールバック関数を呼び出す


> - 「C++モジュール」というのが dll などのライブラリ化されている。

Node.jsを同一プロセス別スレッドで実行するには、以下の構成が考えられると
思います。

1.Node.jsのアドオンとしてネイティブアプリを実行する
    → 大津さんのご指摘の想定

2.ネイティブアプリへNode.jsを埋め込み実行する
    → この場合は特にライブラリ化してなくても良いのでは。

……と思ったのですが、
ぐぐっても2の例やドキュメントが見つかりませんでした。
現状、Node.jsは、アプリへの埋め込みは想定していないようですね。

V8の埋め込み方法だけでは、情報が足りない気が。
https://developers.google.com/v8/get_started


(Node関係ないですが参考)Embedding Python in Another Application
http://docs.python.org/2/extending/embedding.html




Shigeki Ohtsu

unread,
Apr 8, 2013, 6:23:59 AM4/8/13
to node...@googlegroups.com
大津です。

> 以下のサイトによると、
> uv_async_send()を使えば別スレッドからいつでもメッセージを送信できるので
> すね。
> Inter-thread communication
> http://nikhilm.github.io/uvbook/threads.html#inter-thread-communication
>

えー、 worker の中から uv_async_send() してるんですかぁ。
これ async ハンドラが thread safe じゃないような気がしますが、大丈夫なん
かなぁ?

私だったら condition variable を使います。。

参考:「libuvで消費者ー生産者問題を解く(Condition Variableを使う)」
http://d.hatena.ne.jp/jovi0608/20121009/1349755819

> 2.ネイティブアプリへNode.jsを埋め込み実行する
>     → この場合は特にライブラリ化してなくても良いのでは。
>
> ……と思ったのですが、
> ぐぐっても2の例やドキュメントが見つかりませんでした。
> 現状、Node.jsは、アプリへの埋め込みは想定していないようですね。

ライブラリリンクで以外の「アプリへの埋め込み」が何を意味しているのか
ちょっとわかりませんが、アプリ単体が実行オブジェクトとしてしか存在しえな
いなら、Nodeと別プロセスでの起動になりますね。

それなら、 child_process でアプリを fork & exec して、 stdout を IPC で
受けて WS に出力というやり方もありです。(本質的にパイプのやり方と変わら
ないですけど)


open.tommie

unread,
Apr 8, 2013, 8:18:04 AM4/8/13
to node...@googlegroups.com
tommieです。

(2013/04/08 19:23), Shigeki Ohtsu wrote:
> 大津です。
>
>> 以下のサイトによると、
>> uv_async_send()を使えば別スレッドからいつでもメッセージを送信できるので
>> すね。
>> Inter-thread communication
>> http://nikhilm.github.io/uvbook/threads.html#inter-thread-communication
>>
> えー、 worker の中から uv_async_send() してるんですかぁ。
> これ async ハンドラが thread safe じゃないような気がしますが、大丈夫なん
> かなぁ?

大丈夫だと思います。

uv_async_send()はworkerスレッドで実行しますが、
イベントキューにパラメータを書き込むだけで、
ハンドラは実行せずに直ぐに戻ります。
ハンドラはメインループスレッド(Node.jsのスレッド)で実行されます。
なので、ハンドラで、Node.jsのコールバック関数を呼べます。

渡されたパラメータについては、thread safeか注意が必要ですね。

テストプログラムで実験したいところです。

|д゜)チラッ 誰か、検証してくれないかしら…


> 私だったら condition variable を使います。。
>
> 参考:「libuvで消費者ー生産者問題を解く(Condition Variableを使う)」
> http://d.hatena.ne.jp/jovi0608/20121009/1349755819

condition variableは便利そうですね。

竹内さん(質問者さん)の応用の場合、63行目consumer()関数内の74行目辺りで
メッセージ出力するNode.jsコールバック関数を呼ぶ必要がありますが、
consumer()はメインスレッドではないので、
このままでは、コールバック関数を呼べないと思うのですが、
いかがでしょうか。

メインスレッドでconsumer()を実行すると、ブロックしてしまうし。。。
結局、uv_async_send()のような、condVarとは別のスレッド間通信関数を呼ぶ必
要があるのでは。

>> 2.ネイティブアプリへNode.jsを埋め込み実行する
>>     → この場合は特にライブラリ化してなくても良いのでは。
>>
>> ……と思ったのですが、
>> ぐぐっても2の例やドキュメントが見つかりませんでした。
>> 現状、Node.jsは、アプリへの埋め込みは想定していないようですね。
> ライブラリリンクで以外の「アプリへの埋め込み」が何を意味しているのか
> ちょっとわかりませんが、アプリ単体が実行オブジェクトとしてしか存在しえな
> いなら、Nodeと別プロセスでの起動になりますね。
>

1.Node.jsのアドオン と 2.アプリへのNode.js埋め込み の違いは
簡単にいうと main() がどこにあるかです。

1.では main()はNode.jsにありますね。
2.では main()はアプリにあります。
アプリのmain()の中でNode.js初期化関数を呼び出す形です。

なので、どちらも同一プロセスになります。

Pythonでは(当然、V8もですけどw)、2.アプリへの言語の埋め込みができます。
以下、Pythonを埋め込む例です。Py_*がPython関数。

#include <Python.h>

int
main(int argc, char *argv[])
{
Py_SetProgramName(argv[0]); /* optional but recommended */
Py_Initialize();
PyRun_SimpleString("from time import time,ctime\n"
"print 'Today is',ctime(time())\n");
Py_Finalize();
return 0;
}

以下から引用
http://docs.python.org/2/extending/embedding.html


アプリへのNode.js埋め込みがサポートされると、
竹内さん(質問者さん)のような応用が簡単になると思います。
既存アプリにWebサーバログ出力機能追加ってわりと需要ありそうですよね。
もちろん、アドオンでもできますが。

何度も失礼しました。






Tomoya Shimaguchi

unread,
Apr 8, 2013, 8:22:26 AM4/8/13
to node...@googlegroups.com
こんばんは、島口と申します。

僕は、ソケットを使ったプログラムを実務でしたこと無いんですが、
ちょっと気になったので質問させてください。

Node.jsのサーバーの方をクラスタリングした場合、
1.標準ストリームで通信する場合
2.TCPで通信する場合、
どうすればいいんでしょうかね?

標準ストリームの場合、teeコマンドなんかで、複数のNode.jsサーバーに送ることはできる(?)けど、全部受け取ってしまう。
TCPのコネクションの場合は、僕には?です。

ZeroMQみたいなライブラリを使うしかないんでしょうか?
標準ストリームとZeroMQってどっちが速いんでしょうか?
(僕、ZeroMQのことあんまり知りません・・・)

Tomoya Shimaguchi

unread,
Apr 8, 2013, 9:39:10 AM4/8/13
to node...@googlegroups.com
よく考えてみると、TCPでクラスタリングでも、データ受けるのは1つのプロセスですね・・・
普段、httpしか使わないので、変に考えてしまいました。
cluster.jsのコードを少し見てみました。
変なこと聞いてすみません・・・・・忘れてくださいw

竹内佑介

unread,
Apr 8, 2013, 12:41:24 PM4/8/13
to node...@googlegroups.com
こんばんわ、質問者の竹内です。

私の質問に、こんなに多くのレスポンスをして頂きありがとうございました。

>既存アプリにWebサーバログ出力機能追加ってわりと需要ありそうですよね。 
ネイティブアプリと通信をするということについてですが、
tommieさんの言うとおり既存アプリにブラウザからログメッセージが見れるような機能をつけようと考えています。

大津さんの方法が既存アプリに一切手を入れずに目的を達成できそうなので、サンプルを作って確かめてみました。
このような感じのメッセージを無限に吐き出すCプログラムのコンソールメッセージを、
webソケットを使いブラウザ上で表示しようと考えています。

---------------------------------------------------------------------
mestest,c

#include <stdio.h>
#include <time.h>
#include <windows.h>

void main() {
    time_t timer;
    struct tm *date;

while(1) {
    timer = time(NULL);         
date = localtime(&timer);

    printf("%s\n", asctime(date));
Sleep(5000);
}
---------------------------------------------------------------------

ただ、すごく初歩的なところでつまずいてしまったのですが、
パイプを使いメッセージ受け渡すにはどうすればいいのかが分かりませんでした。
私が今回ためしたコマンドは以下の通りです。

@モジュール一覧
mestest        メッセージを無限にはくCプログラム
wsoctest.js    大津さんのサンプルコード


@ためしたコマンド
mestest | node wsoctest.js
mestest >&0 node wsoctest.js


すごく初歩的な質問になってしまいますが、
どなたかパイプを使ってnode.jsにコンソールメッセージを渡す方法について教えて頂けないでしょうか。

2013年4月8日月曜日 22時39分10秒 UTC+9 Tomoya Shimaguchi:

Shigeki Ohtsu

unread,
Apr 8, 2013, 8:10:19 PM4/8/13
to node...@googlegroups.com
大津です。

>> えー、 worker の中から uv_async_send() してるんですかぁ。
>> これ async ハンドラが thread safe じゃないような気がしますが、大丈夫なん
>> かなぁ?
>
> 大丈夫だと思います。
>
> uv_async_send()はworkerスレッドで実行しますが、
> イベントキューにパラメータを書き込むだけで、
> ハンドラは実行せずに直ぐに戻ります。
> ハンドラはメインループスレッド(Node.jsのスレッド)で実行されます。
> なので、ハンドラで、Node.jsのコールバック関数を呼べます。
>
> 渡されたパラメータについては、thread safeか注意が必要ですね。

worker 内で thread local でない async.data をロックせずに操作しています
が、本当に大丈夫なんでしょうか?

>
> テストプログラムで実験したいところです。
>
> |д゜)チラッ 誰か、検証してくれないかしら…

検証しました。

https://gist.github.com/shigeki/5341670

worker 内の sleep(1) を外して、固定回ループにしてます。
どうでしょうか?

>> 私だったら condition variable を使います。。
>>
>> 参考:「libuvで消費者ー生産者問題を解く(Condition Variableを使う)」
>> http://d.hatena.ne.jp/jovi0608/20121009/1349755819
>
> condition variableは便利そうですね。
>
> 竹内さん(質問者さん)の応用の場合、63行目consumer()関数内の74行目辺りで
> メッセージ出力するNode.jsコールバック関数を呼ぶ必要がありますが、
> consumer()はメインスレッドではないので、
> このままでは、コールバック関数を呼べないと思うのですが、
> いかがでしょうか。
>
> メインスレッドでconsumer()を実行すると、ブロックしてしまうし。。。
> 結局、uv_async_send()のような、condVarとは別のスレッド間通信関数を呼ぶ必
> 要があるのでは。

メインスレッドでは uv_prepare() か uv_check() でイベントループ毎に queue
を吐き出すように処理すればいいと思います。
(node-v0.10 の setImmediate() では、同様の処理になるようにPRで変更しまし
た。)

>>> 2.ネイティブアプリへNode.jsを埋め込み実行する
>>>     → この場合は特にライブラリ化してなくても良いのでは。
>>>
>>> ……と思ったのですが、
>>> ぐぐっても2の例やドキュメントが見つかりませんでした。
>>> 現状、Node.jsは、アプリへの埋め込みは想定していないようですね。
>> ライブラリリンクで以外の「アプリへの埋め込み」が何を意味しているのか
>> ちょっとわかりませんが、アプリ単体が実行オブジェクトとしてしか存在しえな
>> いなら、Nodeと別プロセスでの起動になりますね。
>>
>
> 1.Node.jsのアドオン と 2.アプリへのNode.js埋め込み の違いは
> 簡単にいうと main() がどこにあるかです。
>
> 1.では main()はNode.jsにありますね。
> 2.では main()はアプリにあります。
> アプリのmain()の中でNode.js初期化関数を呼び出す形です。
>
> なので、どちらも同一プロセスになります。

Node.js初期化関数って何を指して仰ってるのかわかりませんが、
仮に node::Start() を指すとしたら uv_run() でブロックしちゃいますけど…

Node自体はライブラリ提供するように設計されていないので、Nodeのソースの一
部(もしくは大半)を自前のネィテブアプリに組み込んでビルドし、単一プロセ
スで実行することを考えているなら、もはやNodeとは違うもんじゃないかと思い
ます。


Shigeki Ohtsu

unread,
Apr 8, 2013, 8:13:22 PM4/8/13
to node...@googlegroups.com
大津です。

printf("%s\n", asctime(date));

の後に

fflush(stdout);

を付けて試してもらえますでしょうか?

open.tommie

unread,
Apr 9, 2013, 6:45:32 AM4/9/13
to node...@googlegroups.com
tommieです。

(2013/04/09 9:10), Shigeki Ohtsu wrote:
> 大津です。
>
>
> worker 内で thread local でない async.data をロックせずに操作しています
> が、本当に大丈夫なんでしょうか?

キュー経由でメッセージ送信するようにしました。

> 検証しました。
>
> https://gist.github.com/shigeki/5341670
>
> worker 内の sleep(1) を外して、固定回ループにしてます。
> どうでしょうか?

お手間取らせて申し訳ないです。

forkして変更しました。

https://gist.github.com/open-tommie/5342559

変更点、ログの解説などをreadme.txtに書きました。
以下、readme.txtから。

●main.c変更点

・ワーカー関数をひとつにしました。
 fake_download1(), after1()を削除。

・fake_download0()内で使用していたmsgは
 スタック変数であり、スレッドセーフではないので
 malloc()で取得したヒープ上メモリ領域を使うように変更しました。(main.c
48行目)
 使用済みメモリ領域はprint_progress()内でfree()しています。(main.c 33行目)

・uv_async_send()を実行してからprint_progress()が呼ばれる間に
 複数回uv_async_send()が呼ばれても、print_progress()の実行は1回だけ
 です(uv_async_send()の仕様)。
 この場合、全てのmsgが処理されるわけではない、ということになります。
 これを防ぐ為に、async.dataでmsgを送信するのをやめて、
 代わりにmsg用のキューg_msgQueueを使いました。(main.c 26~28, 53行目)
 uv_async_send()はprint_progress()呼び出しタイミングを通知するだけにな
りました。

・STLのqueueはスレッドセーフではないので、queue操作の前後に
 uv_mutex_lock(), uv_mutex_unlock()を加えて、
 スレッドセーフにしています。



ご意見募集です。

> Node.js初期化関数って何を指して仰ってるのかわかりませんが、
> 仮に node::Start() を指すとしたら uv_run() でブロックしちゃいますけど…

C++部分も、Node.js同様にイベント駆動でプログラムを書きます。
libuvを用いたC++プログラム同様、
node::Start()のuv_run()のイベント処理でC++関数が呼ばれるようにします。
そうれば、Node.jsとC++が混在できますよね。
もちろん、アドオンのように、C++ ⇔ Node.jsの型変換や
Node.js関数としての登録などは必要になります。

以上。

竹内佑介

unread,
Apr 9, 2013, 10:56:25 AM4/9/13
to node...@googlegroups.com
質問者の竹内です。

>printf("%s\n", asctime(date)); 
>の後に 
>fflush(stdout); 大津さん

ありがとうございます、この方法で目的が達成できました。

今回のケースから既存アプリにWebブラウザへのログ出力機能を追加する場合には、
パイプ処理だけではなく既存アプリにもてを入れる必要があることが分かりました。

現在手を入れようとしているアプリは、コンソールメッセージ表示系関数がラップされているので、
そこに手を入れるだけで対応ができそうです。

ただ、既存アプリに手を入れないで、Webブラウザへのログ出力機能が実現できたら、
結構色々な用途で使えて便利だなと思いました。
この手の問題は環境に依存する部分が強そうなので答えが一つに定まらないと思いますが、
何かよい方法があれば教えて頂けないでしょうか。


2013年4月9日火曜日 19時45分32秒 UTC+9 tommie:

Shigeki Ohtsu

unread,
Apr 9, 2013, 11:44:00 PM4/9/13
to node...@googlegroups.com
大津です。

> ご意見募集です。

まだ worker 内の sleep(1) に依存した実装になってますよね。
(sleep(1) を外すと uv_async_send() が正常に動作しなくなる)

これ、 sleep(1) を入れないと、イベントループが io poll で call back を処
理する前に各スレッドが async fd を上書きしちゃうからだと思います。(ちゃ
んと調べてないですが)

先に述べたよう uv_check() を使った実装だとこんな感じになります。
(これは worker 内に sleep(1) は必要ないです)

https://gist.github.com/shigeki/5351299

あと pthread_self() の替りに uv_thread_self() も使えるのでこっちを使って
ます。

まぁ、こんなことのオンパレードになるし、あのころは libuv の pthread 対応
も実装されてないので、 Node の Isolate 対応が複雑で取り止めになったとい
うことはホント理解できますな。

Shigeki Ohtsu

unread,
Apr 10, 2013, 12:08:34 AM4/10/13
to node...@googlegroups.com
大津です。

> ありがとうございます、この方法で目的が達成できました。

良かったです。

> ただ、既存アプリに手を入れないで、Webブラウザへのログ出力機能が実現でき
> たら、
> 結構色々な用途で使えて便利だなと思いました。
> この手の問題は環境に依存する部分が強そうなので答えが一つに定まらないと思
> いますが、
> 何かよい方法があれば教えて頂けないでしょうか。

うー、「既存の任意のアプリの(どんなのかわからない)出力を、このアプリに
一切手を加えず別のアプリの入力に渡す何か良い方法はないか?」という一般的
な質問になりますよね。ホント環境依存です。

せめてファイル出力に限定されるんなら fs.watch() を使うぐらいですが、
独自画面に出されちゃうようなものなら、スクリーンショットとってCAPTCHA解
読するしかないんじゃないですかねぇ。(もうNodeの話じゃなくなちゃう)




open.tommie

unread,
Apr 10, 2013, 5:04:44 AM4/10/13
to node...@googlegroups.com
tommieです。

(2013/04/10 12:44), Shigeki Ohtsu wrote:
大津です。


まだ worker 内の sleep(1) に依存した実装になってますよね。
(sleep(1) を外すと uv_async_send() が正常に動作しなくなる)

sleep()なしでも正しく動くように直しました。
だいぶゴテゴテになってしまいました。w

https://gist.github.com/open-tommie/5342559



これ、 sleep(1) を入れないと、イベントループが io poll で call back を処
理する前に各スレッドが async fd を上書きしちゃうからだと思います。(ちゃ
んと調べてないですが)

uv_async_tはスレッド毎に必要なようです。
1つのuv_async_tを重複して使っていたのが原因でした。
スレッド毎に用意したら正しく動きました。


先に述べたよう uv_check() を使った実装だとこんな感じになります。
(これは worker 内に sleep(1) は必要ないです)

https://gist.github.com/shigeki/5351299

uv_check()を使ったほうがスッキリしてますね。
uv_check()は イベント処理毎に設定した関数を呼び出すのですね、なるほど。
全イベント処理毎に呼ばれるのでしょうか?


アドオン化して、Node.jsコールバック関数も呼び出せました。
多少、整理して近いうちに投稿します。

Shigeki Ohtsu

unread,
Apr 10, 2013, 5:22:39 AM4/10/13
to node...@googlegroups.com
大津です。

>> これ、 sleep(1) を入れないと、イベントループが io poll で call back を処
>> 理する前に各スレッドが async fd を上書きしちゃうからだと思います。(ちゃ
>> んと調べてないですが)
>
> uv_async_tはスレッド毎に必要なようです。
> 1つのuv_async_tを重複して使っていたのが原因でした。
> スレッド毎に用意したら正しく動きました。

だから最初に書いた通り、

> >> えー、 worker の中から uv_async_send() してるんですかぁ。
> >> これ async ハンドラが thread safe じゃないような気がしますが、大丈
> >> 夫なんかなぁ?

ということなんですよね。

> uv_check()を使ったほうがスッキリしてますね。
> uv_check()は イベント処理毎に設定した関数を呼び出すのですね、なるほど。
> 全イベント処理毎に呼ばれるのでしょうか?

実は、この辺をわかりやすく図にして書いてます。
(node-v0.9の初期の図なので今は若干変わってます。)

http://html5.ohtsu.org/new_setImmediate_semantics.png

この図、結構評判良くて、 isaacs からも使いたいと頼まれてますが、まだ最新
版書いてない状況です。(早くしないと)

> アドオン化して、Node.jsコールバック関数も呼び出せました。
> 多少、整理して近いうちに投稿します。

楽しみにして待ってます。

open.tommie

unread,
Apr 10, 2013, 5:36:47 AM4/10/13
to node...@googlegroups.com
tommieです。度々すみません。

(2013/04/10 18:22), Shigeki Ohtsu wrote:
> 大津です。
>
>>> これ、 sleep(1) を入れないと、イベントループが io poll で call back を処
>>> 理する前に各スレッドが async fd を上書きしちゃうからだと思います。(ちゃ
>>> んと調べてないですが)
>> uv_async_tはスレッド毎に必要なようです。
>> 1つのuv_async_tを重複して使っていたのが原因でした。
>> スレッド毎に用意したら正しく動きました。
> だから最初に書いた通り、
>
>>>> えー、 worker の中から uv_async_send() してるんですかぁ。
>>>> これ async ハンドラが thread safe じゃないような気がしますが、大丈
>>>> 夫なんかなぁ?
> ということなんですよね。

いえ、workerスレッドからuv_async_send()するのは
通常の使用法(とうか、目的そのもの)だと思いますよ。


>> uv_check()を使ったほうがスッキリしてますね。
>> uv_check()は イベント処理毎に設定した関数を呼び出すのですね、なるほど。
>> 全イベント処理毎に呼ばれるのでしょうか?
> 実は、この辺をわかりやすく図にして書いてます。
> (node-v0.9の初期の図なので今は若干変わってます。)
>
> http://html5.ohtsu.org/new_setImmediate_semantics.png
>
> この図、結構評判良くて、 isaacs からも使いたいと頼まれてますが、まだ最新
> 版書いてない状況です。(早くしないと)

勉強会で見せて頂きましたね、今思い出しました。
これTシャツのデザインとしても良いですねー



竹内佑介

unread,
Apr 10, 2013, 6:08:44 AM4/10/13
to node...@googlegroups.com
質問者の竹内です。

大津さん、質問に対応して頂きありがとうございました。


>うー、「既存の任意のアプリの(どんなのかわからない)出力を、このアプリに 
>一切手を加えず別のアプリの入力に渡す何か良い方法はないか?」という一般的 
>な質問になりますよね。ホント環境依存です。 

やはり、そんな虫のいい話はないようですね。

>せめてファイル出力に限定されるんなら fs.watch() を使うぐらいですが、

この方法が一番現実的な気がしてきました。
どちらにしても既存アプリにWeb経由のコンソールメッセージ出力を組み込むのは難しいようですね。


2013年4月10日水曜日 18時04分44秒 UTC+9 tommie:

竹内佑介

unread,
Apr 13, 2013, 11:25:52 AM4/13/13
to node...@googlegroups.com
竹内です。

>>せめてファイル出力に限定されるんなら fs.watch() を使うぐらいですが、
>この方法が一番現実的な気がしてきました。
>どちらにしても既存アプリにWeb経由のコンソールメッセージ出力を組み込むのは難しいようですね。

ネットを探してみると同じことを考えている人はいるものですね。
node.jsでリアルタイムにログファイルを表示するシステムがありました。

「紹介記事」
http://blog.glidenote.com/blog/2012/07/02/supergrep/

これを使えば、手軽にコンソール画面をWebブラウザ上で表示できそうです。

2013年4月7日日曜日 17時04分24秒 UTC+9 竹内佑介:

open.tommie

unread,
Apr 13, 2013, 1:38:39 PM4/13/13
to node...@googlegroups.com
tommieです。

> 竹内です。
>
> node.jsでリアルタイムにログファイルを表示するシステムがありました。
>
> 「紹介記事」
> http://blog.glidenote.com/blog/2012/07/02/supergrep/
>
> これを使えば、手軽にコンソール画面をWebブラウザ上で表示できそうです。

このツール、便利そうですね。



(2013/04/10 18:22), Shigeki Ohtsu wrote:
> 大津です。
>> アドオン化して、Node.jsコールバック関数も呼び出せました。
>> 多少、整理して近いうちに投稿します。
> 楽しみにして待ってます。

同期的な関数からNode.js関数を呼び出す実験の検証プログラムです。

https://gist.github.com/open-tommie/5347832

readme.txtにインストール手順、テスト手順を書きました。

内容は後回しとして、とりあえず、
動いたかどうかだけ先に報告して頂けると助かります。

iPhone5のsafariブラウザのスクリーンショットを添付しました。

動作確認の後、ご意見を募集です。

以上。


node.png

竹内佑介

unread,
Apr 21, 2013, 10:45:17 AM4/21/13
to node...@googlegroups.com
お久しぶりです、竹内です。


あれからプロセス間通信を用いて、
ログを表示するサンプルを作成してみました。
動作的には以下のようになっています。

C++アプリ
↓ TCPソケット通信
node.js
↓ webソケット
ブラウザ

もう少し細かい挙動を説明しますと、今回作成したプログラムは、
(1)C++アプリから受け取ったメッセージをnode.jsを保存
(2)node.jsは2秒おきにC++アプリから受け取ったメッセージをブラウザに送信
という動作をしています。
今回は(2)の一定時間置きにブラウザに対してメッセージを送信する、
というところで詰まってしまいました。

このコードを動作させると、コンソールメッセージから
タイムインターバルのコールバック関数が呼ばれていることは分かりますが、
sendUTF()が呼ばれたタイミングでブラウザ側にメッセージが送信されません。
また、ブラウザとnode.jsの接続が切れたタイミングで、sendUTF()にたまっていたメッセージが
全て送信されていました。
定期的にイベントを呼び出している部分は、wsServer.on('request')のsetInterval()になります。

今回、質問をさせて頂きたいのは、以下の2点です。
(1)setIntervalでconn.sednUTF()を呼び出してもサーバ側にすぐに送信されない理由
(2)サーバとブラウザの接続が切れたタイミングで今までのメッセージが全送信される理由


このプログラムはC++アプリがなくても、受け取りメッセージにはデフォルト値が格納されているため
問題なく動作します。




********************************************
node.jsコード
serve.js
********************************************

//* Grobal Value
var net = require('net');
var http = require('http');
var WebSocketServer = require('websocket').server;

var AcceptConsoleMessagePort = 8124;
var WebSocketServerListenPort = 8080;
var index = require('fs').readFileSync('logview.html');
var mes = 'default message';

//Accept Console Message Server
var acceptMessageServer = net.createServer(function(socket) { //'connection' listener
  console.log('Accept Message Server connected');
  
  socket.on('end', function() {
    console.log('Accept Message Server disconnected');
  });
  
  socket.on('data', function(data) {
    mes = data.toString();
    console.log('Accept Data Process :' + mes);
  });
  
});
//Listen
acceptMessageServer.listen(AcceptConsoleMessagePort, function() {
  console.log('Accept Message Server Listen : ' + AcceptConsoleMessagePort );
});


//Websocket Listen Server
var server = http.createServer(function(req, res) {
  if (req.url === '/') {
    res.writeHead(200, { 'content-type': 'text/html'});
    res.end(index);
  } else {
    res.writeHead(404);
    res.end();
  }
});
//Listen
server.listen(WebSocketServerListenPort, function() {
  console.log('Listening on ' + WebSocketServerListenPort);
});


//Websocket Server
var wsServer = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: false
});
 
wsServer.on('request', function(req) {
  var conn = req.accept('stdin-to-ws', req.origin);
  console.log((new Date()) + ' Peer ' + conn.remoteAddress + ' connected.');
  
  //この部分で定期的にブラウザへメッセージ送信をしている。
  setInterval(function(){
    console.log('start sned client');
    conn.sendUTF(mes);
    console.log('end sned client : ' + mes);
    
  },2000);
  
  conn.on('close', function(reasonCode, description) {
    console.log((new Date()) + ' Peer ' + conn.remoteAddress + ' disconnected.');
  });
  
});


********************************************
クライアント側コード
logview.html
********************************************
<!DOCTYPE html>
<html>
<head>
<title>logview</title>
</head>

<body>
<div id="msg"></div>
<script>
  var msg = document.getElementById("msg");
  var ws = new WebSocket("ws:localhost:8080/", "stdin-to-ws");

  ws.onmessage = function(e) {
    var data = document.createElement("div");
    data.innerHTML = e.data;
    msg.appendChild(data);
  };
</script></body></html>




********************************************
C++アプリのコード
main.cpp
********************************************
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>


SOCKET InitializeClientWinsock(LPSTR lpszServerName, LPSTR lpszPort);

//---------------------------------------------------------
//
// main
//
//---------------------------------------------------------
void main() {
char mes[256]="gundamX";
char hostName[256]="localhost";
char port[16]="8124";
bool loopFlag = true;
SOCKET soc;

printf("Welcome to ScarletRain\n");
printf("Start Connecting\n");
soc=InitializeClientWinsock("localhost","8124");
printf("Connection Complate\n");

while(loopFlag) {
int ret;
scanf("%s",mes);
ret = send(soc,mes,strlen(mes),0);
printf("send %d byte success\n",ret);
}

printf("Bye\n");
}


//---------------------------------------------------------
//
// ソケットの初期化(クライアント)
//
//@param LPSTR lpszServerName 接続先サーバのホスト名
//@param LPSTR lpszPort 接続先サーバポート番号
//@return SOCKET サーバのリッスンソケット
//---------------------------------------------------------
SOCKET InitializeClientWinsock(LPSTR lpszServerName, LPSTR lpszPort)
{
WSADATA    wsaData;
ADDRINFO   addrHints;
LPADDRINFO lpAddrList;
SOCKET     soc;
//ソケットのスタートアップ
WSAStartup(MAKEWORD(2, 2), &wsaData);
//ソケット接続情報設定
ZeroMemory(&addrHints, sizeof(addrinfo));
addrHints.ai_family   = AF_INET;
addrHints.ai_socktype = SOCK_STREAM;
addrHints.ai_protocol = IPPROTO_TCP;

//ホスト名からIPを取得
if (getaddrinfo(lpszServerName, lpszPort, &addrHints, &lpAddrList) != 0) {
MessageBox(NULL, TEXT("ホスト情報からアドレスの取得に失敗しました。"), NULL, MB_ICONWARNING);
WSACleanup();
return INVALID_SOCKET;
}

//ソケットの作成
soc = socket(lpAddrList->ai_family, lpAddrList->ai_socktype, lpAddrList->ai_protocol);

if (connect(soc, lpAddrList->ai_addr, (int)lpAddrList->ai_addrlen) == SOCKET_ERROR) {
int nError = WSAGetLastError();
if (nError == WSAECONNREFUSED)
MessageBox(NULL, TEXT("サーバーがリッスンしていません。"), NULL, MB_ICONWARNING);
else if (nError == WSAEHOSTUNREACH)
MessageBox(NULL, TEXT("サーバーが存在しません。"), NULL, MB_ICONWARNING);
else
MessageBox(NULL, TEXT("サーバーへの接続が失敗しました。"), NULL, MB_ICONWARNING);
freeaddrinfo(lpAddrList);
closesocket(soc);
WSACleanup();
return INVALID_SOCKET;
}
freeaddrinfo(lpAddrList);

return soc;
}

2013年4月7日日曜日 17時04分24秒 UTC+9 竹内佑介:

竹内佑介

unread,
Apr 22, 2013, 12:20:32 AM4/22/13
to node...@googlegroups.com
竹内です。

C++アプリのコードについてですが、ビルドするには ws2_32.libを追加する必要があります。
詳細は参考ページをご確認下さい。



2013年4月7日日曜日 17時04分24秒 UTC+9 竹内佑介:

竹内佑介

unread,
Apr 24, 2013, 10:24:19 AM4/24/13
to node...@googlegroups.com
竹内です。

先日あげた問題についてですが、自己解決しました。

単にアンチウイルスソフトを切り忘れてたために起きた問題でした。
色々とお騒がせしてしまい、申し訳ありませんでした。

2013年4月7日日曜日 17時04分24秒 UTC+9 竹内佑介:
Reply all
Reply to author
Forward
0 new messages