2011年10月5日水曜日

WebSocketってなんじゃ?(Node編2 Socket.IOでプッシュ通信)

前回Node編1で、スタンドアローンのHTTPサーバーをつくり、HTTP通信を行いました。

今回は、WebSocketを使用した通信を行ってみます。 WebSocketとは、ブラウザとサーバーの双方向通信のプロトコル規格で、現在標準化団体が仕様を策定中の技術です。chromeやsafariなど、いくつかのモダンブラウザがサポートしており、これが一般的に使用されると、いままでのサーバーアプリケーションの作り方が大きく変わってくる可能性がある、注目の技術です。

ここで使用するのは、Socket.IO というnode.jsのモジュールです。 node.jsはモジュールという単位でライブラリを管理しており、npmというモジュール管理ツールで簡単にいろいろなモジュールを追加削除できます。これはrubyでいうgemのようなものです。
Socket.IOはただnode.jsでWebSocketを実装しているだけではなく、モダンブラウザ以外のWebSocketを使用できないブラウザでも、AjaxやFlashなど他の代替手段があるかどうかを自動的に検知して通信方式を自動で選択されるので、ブラウザの対応をあまり気にしなくてもよくなっています。
また、Socket.IOは WebSocketとその代替手段において、クロスドメイン通信を可能にしています。

早速実践してみます。 まず最初にnpmをインストールします。
$ curl http://npmjs.org/install.sh | sh
$ npm install -g socket.io


socket.ioのインストールでエラーが出る場合がありますが、その場合はtarが古いので、tarを最新にしておきます。
$ cd /usr/local/src
$ wget http://ftp.gnu.org/gnu/tar/tar-1.26.tar.gz
$ tar xzvf tar-1.26.tar.gz
$ cd tar-1.26
$ ./configure
$ make
# make install


socket.ioをインストールします。
$ curl http://npmjs.org/install.sh | sh
$ npm install -g socket.io

※エラーになる場合は curl -L http://npmjs.org/install.sh | sh とやると良いようです。

これで実装の準備が整いました。
それでは実装を始めます。
今回は、メッセージを送信したら見ている全員にメッセージを表示させる、簡単なチャットの仕組みを作ってみます。

サーバー側

/opt/memorycraft/node/socketsample.js
require.paths.push('/usr/local/lib/node_modules');

var server = require('http').createServer(function(req, res){
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end('server connected');
});
server.listen(1337);

var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
  socket.emit('info', { msg: 'welcome' });
  socket.on('msg', function (msg) {
    io.sockets.emit('msg', {msg: msg});
  });
  socket.on('disconnect', function(){
    socket.emit('info', {msg: 'bye'});
  });
});


サーバー側では任意のポート(ここでは1337)を開放したhttpサーバーにsocket.ioをつなげてリスン状態にします。
通信の確立(connection)や切断(disconnect)などのビルトインイベントや、自分のアプリケーションで使用したいオリジナルのイベント(ここではmsg)のイベントハンドラを定義します。
このコードでは接続したときに"welcome"、切断したときに"bye"をそのユーザーに、あるユーザーから"msg"というイベントが送られたら全員に"msg"というイベントを、それぞれプッシュ送信しています。

クライアント側

/var/html/www/test/node/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
  <script src="http://xxx.xxx.xxx.xxx:1337/socket.io/socket.io.js"></script>
  <script type="text/javascript">
  $(function(){
    var socket = io.connect('http://xxx.xxx.xxx.xxx:1337/');
    socket.on('connect', function() {
      $("#log").html($("#log").html() + "<br />" + 'connected');
      socket.on('info', function (data) {
        $("#log").html($("#log").html() + "<br />" + data.msg);
      });
      socket.on('msg', function(data){
        $("#log").html($("#log").html() + "<br />" + "<b>" + data.msg + "</b>");
      });
      $("#send").click(function(){
        var msg = $("#msg").val();
        if(!msg){
          alert("input your message");
          return;
        }
        socket.emit('msg', msg);
      });
    });
  });
  </script>
  <title>nodetest</title>
</head>
<body>
  <input id="msg" type="text" style="width:400px;"></input>
  <input id="send" type="button" value="send" /></br >
  <div id="log" style="width:400px;height:400px;overflow:auto;border:1px solid #000000;"></div>
</html>

クライアント側からはサーバー側の1337ポートに接続しますが、サーバー側は実際にファイルを自分で配置しなくてもSocket.IOが勝手にクライアント側のライブラリを/socket.io/socket.io.jsとして配信してくれます。
そのファイルをscriptファイルでロードするようにし、あとは接続部分とサーバーからのイベントのハンドラを定義します。また、ボタンを押したらテキストをサーバー側に送信するように記述します。


それでは、サーバー側のコードを実行してみます。
$ node /opt/memorycraft/node/socketsample.js
   info  - socket.io started

ブラウザでhtmlを表示します。

サーバー側からの"welcome"メッセージが表示されていることがわかります。
また、メッセージを入力して送信すると、

メッセージ欄にサーバーから送られてきているのがわかります。

別のブラウザでも開いておき、そちらからメッセージを送信するともう一方のブラウザにメッセージが表示されることが分かります。


このように、socket.ioを利用することで多くのブラウザで簡単にWebSocketを実現することができます。
WebSocketは通信内容が少なく、コネクションやリソースの消費が少ないので、ゲームやソーシャルサイトなどのマルチユーザーアプリや、クライアントのログ集計など、様々な分野での利用が期待できます。
今日はここまで。