ニコルンアプリの通信パケットからコメントの送受信方法を調べる延長で作ってみることにした。
以下は公開した時のツイート。
ニコニコの謎な新サービス「ニコルン」の叡王戦チャットroomのビューワを作ってみました。ビューワなので見れるだけです。。。 https://t.co/SX6QSJ0dwf #shogi #将棋
— ロフトくん (@loftkun) 2015, 11月 11
サーバの実装
チャットビューワのサーバ側では以下2プロセスを動作させるようにした。
1 ニコルンのチャットを受信し標準出力に出力するアプリ(NicolunCommentViewer.exe)。
.NetFramework(Mono)上で動作するC#アプリ。
2 標準入力を読んでビューワクライアント(ブラウザ)とのソケット通信を行うHTTPサーバ(NicolunServer.js)。
node.js上で動作する。
上記を以下のようにパイプでつないでチャットビューワサーバを構成する。
動作環境はCentOS7.1
今回は簡単のために受信するチャットroomは固定にしている。
mono NicolunCommentViewer.exe | node ./NicolunServer.js
NicolunServer.js
標準入力に書き込まれた文字をクライアントに送信するHTTPサーバ。
(標準入力には前述の(NicolunCommentViewer.exe)が受信したチャット文字列がパイプ経由で書き込まれる。)
なお、クライアントからの初回接続時は過去ログ(comment.txt)を読んでクライアントに送信する。
参考)のサイトのソースを改造させて頂いた。
console.log('NicolunServer start.'); //標準入力を読む //参考) http://mironal-memo.blogspot.jp/2012/08/node.js-pipe.html process.stdin.resume(); process.stdin.setEncoding('utf8'); var fragment = ""; //読み込み開始 process.stdin.on('data', function(chunk){ if (chunk == "") { return ;} var lines = chunk.split("\n"); lines[0] = fragment + lines[0]; fragment = lines.pop(); lines.forEach(function(line){ console.log('stdin line : ' + line); socketSend(line); }); }); //読み込み終了 process.stdin.on('end', function(){ }); //socket通信できるHTTPサーバ //参考) http://qiita.com/hosomichi/items/66b309a6c3c20d910218 var fs = require("fs"); var server = require("http").createServer(function(req, res) { res.writeHead(200, {"Content-Type":"text/html"}); var output = fs.readFileSync("./index.html", "utf-8"); res.end(output); }).listen(8080); var io = require("socket.io").listen(server); // ユーザ管理ハッシュ var userHash = {}; //接続 io.sockets.on("connection", function (socket) { console.log("on connection"); //connectedイベント socket.on("connected", function (name) { console.log("on connected name=" + name); userHash[socket.id] = name; //通知する //io.sockets.emit("publish", {value: msg}); //過去ログを読んで送信する var output = fs.readFileSync("./comment.txt", "utf-8"); var lines = output.split("\n"); lines.forEach(function(line){ //io.sockets.emit("publish", {value: line});//これは全員に送信してしまう //io.sockets.socket(socket.id).emit('publish',{value:line});//これは文法が古い io.sockets.to(socket.id).emit('publish',{value:line});//socket.idに対してだけ送れる }); }); //publishイベント socket.on("publish", function (data) { console.log("on publish data=" + data); //io.sockets.emit("publish", {value:data.value}); }); //disconnectイベント socket.on("disconnect", function () { if (userHash[socket.id]) { //通知する //var msg = userHash[socket.id] + "が退出しました"; delete userHash[socket.id]; //io.sockets.emit("publish", {value: msg}); } }); }); function socketSend(str){ io.sockets.emit("publish", {value: str}); }
クライアントの実装
socket.ioのお約束として、htmlのヘッダに以下記述が必要。
xxx.xxx.xxx.xxx:xxxxはサーバのIP、ポート
<script src="http://xxx.xxx.xxx.xxx:xxxx/socket.io/socket.io.js"></script>
Nicolun.js
以下のようにinit()メソッドの引数にcallbackFuncを渡すと、
サーバからチャット受信時にcallbackFuncを呼んでくれるクラスとして用意した。
new Nicolun().init(callbackFunc);
callbackFuncの第1引数にチャット文字列が入っているので画面に表示することでチャットビューワサイトとなる。
Nicolun.jsの実装は以下
//******************************************************************************************** /** * @brief ニコルン情報取得 * @author @loftkun */ //******************************************************************************************** var Nicolun = (function() { //******************************************************************************************** /** * @brief コンストラクタ */ //******************************************************************************************** var Nicolun = function() { }; var nicolun = Nicolun.prototype; //******************************************************************************************** /** * @brief ソケット初期化 */ //******************************************************************************************** nicolun.init = function(onPublish){ var socketio = io.connect('http://xxx.xxx.xxx.xxx:xxxx/');//サーバのIP、ポート socketio.on("connected", function(name) {}); socketio.on("publish", function (data) { onPublish(data.value); }); socketio.on("disconnect", function () {}); //開始 socketio.emit("connected", "test_user"); }; return Nicolun; })();