ExpressとSocket.IOを使ったカウンターのサンプル

このエントリは、「東京Node学園 4時限目」の資料その3です。

Expressを使ったWebアプリケーションの動きが大体つかめたところで、次はSocket.IOを使ったリアルタイムWebアプリケーションを作ってみましょう。
Socket.IOとはWebSocketをNodeで扱うためのサーバ及びクライアントのライブラリです。WebSocketが使える環境であればWebSocketを、そうでなければ自動的にXHR-polling等に切り替えてくれるのが最大の特徴です。

まずは、Socket.IOをインストールします。package.jsonを以下の様に書き換えてください。

 {
     "name": "application-name"
   , "version": "0.0.1"
   , "private": true
   , "dependencies": {
       "express": "2.5.8"
     , "jade": ">= 0.0.1"
+    , "socket.io": "*"
   }
 }

socket.ioの記述を追加しました。"*"は最新バージョンを意味します。それではsampleディレクトリで再度 npm install をしましょう。

$ npm install

Socket.IOがインストールされたはずです。このように、使用するパッケージをpackage.jsonに記述し管理することで、node_modules ディレクトリをバージョン管理システムの対象外にしておいても同じ環境をコマンド一発で再現することができます。
それでは実際にSocket.IOを使って、現在ページを見ている人数をリアルタイムに表示するプログラムを書いてみましょう。まず、app.jsでSocket.IOを読み込みます。

 var express = require('express')
+  , socketio = require('socket.io')
   , routes = require('./routes');

またファイルの末尾に以下のコードを追加します。

var io = socketio.listen(app);
var count = 0;
io.sockets.on('connection', function(socket) {
  //connect
  count++;
  io.sockets.emit('count change', count);
  socket.on('disconnect', function() {
    //disconnect
    count--;
    socket.broadcast.emit('count change', count);
  });
});

クライアント(ブラウザ)から接続がきたらconnectに書かれた処理が、切断されたらdisconnectに書かれた処理がそれぞれ実行されます。今回の場合、

  1. countの初期値を0にしておく
  2. 接続がきたらcountを+1して、その値を自分を含めた接続している全員に'count change'というイベントとして通知する
  3. 切断したらcountを-1して、その値を自分以外の全員に通知する

という動作になります。

クライアント側にも処理を記述しましょう。./views/layout.jadeを以下のように修正します。

 !!!
 html
   head
     title= title
     link(rel='stylesheet', href='/stylesheets/style.css')
+    script(type='text/javascript', src='http://code.jquery.com/jquery.min.js')
+    script(type='text/javascript', src='/socket.io/socket.io.js')
+    script(type='text/javascript')
+      var socket = io.connect();
+      socket.on('count change', function(count) {
+        // event
+        $('#count').text(count);
+      });
 body!= body

jQueryとSocket.IOのクライアントライブラリを読み込んでいます。Socket.IOのクライアントライブラリは、app.js内でlisten()したことによって自動的に上記のパスに提供されます。
io.connect()をした段階で、サーバ側の'connection'イベントが発生します。また、'count change'というイベントがきたら、eventに記述された処理を実行します。今回の場合、人数が変わるたびにjQueryを用いてcountというidの要素のテキストを書き換えています。
今回の場合、記述するJavaScriptのコード量が少ないため直接layout.jadeに記述しましたが、JavaScriptは本来別ファイルに切り出して、public/javascripts/内に置くほうが良いでしょう。

最後に views/index.jade を以下のように修正します。

 h1= title
 p Welcome to #{title}
+p 現在このページを見ている人は
+  span#count
+  人います。

それでは実行してみましょう。

$ node app.js

で起動し、http://localhost:3000/ にアクセスすると、「現在このページを見ている人は1人います。」と表示されるはずです。現在のタブを開いたまま他のタブでもアクセスしてみましょう。「現在このページを見ている人は2人います」と表示されるはずです。
さらに、最初に「1人」と表示されていたタブも、リロードしなくてもすでに「2人」になっているはずです!タブを増やすたびに人数が増えて行き、閉じるたびに減っていくことを確認してください。

今回は、接続イベントと切断イベント以外のイベントを発生させるのはサーバ側でイベントを受信するのはクライアント側のみという作りになっていますが、もちろん、クライアント側で任意のイベントを発生(emit)させることや、サーバ側で受信(on)することができます。また、Socket.IOにはnamespaceという概念があり、特定のnamespaceに属する相手にのみ通知することも可能です。その例として、その4では昨年の「東京Node学園祭」のWebサイトで用いたマウスカーソルの共有を紹介します。