Commits

Yohei Sasaki  committed fff7dda

[add] websocket done.

  • Participants
  • Parent commits 2086bf5

Comments (0)

Files changed (2)

File source/webapp/http_server.rst

 
 筆者的には https://github.com/janl/mustache.js がお気に入りです.
 
+フレームワーク
+-------------------------------------------------
+
+Webアプリケーションを開発するためには,いわゆるフレームワークと呼ばれるものが必須の時代になってきています.
+
+node.js の上でもいくつかのWebフレームワークが開発されています [#f2]_ .
+
+`Express <http://expressjs.com/>`_
+   Ruby Sintra にインスパイアされたフレームワークです. `Connect <https://github.com/senchalabs/Connect>`_ と呼ばれるミドルウェアフレームワークの上に,リクエストルーティングやセッション管理,テンプレートエンジンを組み込んだ構成になっています.
+
+`Geddy <http://geddyjs.org/>`_
+   Merb,Rails,Pylons,あるいは Django に似たような機能を提供するフレームワーク,とのことです.
+
+
+
 .. [#f1] DOMを使うものでもjsdom と呼ばれるライブラリを使えば(苦労はするものの)大抵は動きます.
-
-
+.. [#f2] https://github.com/ry/node/wiki/modules

File source/webapp/websocket.rst

 WebSocket を利用したリアルタイムWebアプリケーション
 ======================================================
+
+:doc:`http_server` の章で node.js を使ったWebアプリケーションの作成方法を学びました.既存のアプリケーションでもマルチスレッド/マルチプロセスモデルを活用することで並列/並行実行可能なWebアプリケーションを組んでいました.node.js は単一プロセス,単一スレッドで動作しますが,IOをブロックせずに次々と処理していくため,複数のリクエストを効率的に並列処理することが可能です.
+
+
+この特長が生かされるのは次のケースでしょう.
+
+- 多数のバックエンドIOを消費するケース
+- 多数のフロントエンドIOを消費するケース
+
+つまり,Webアプリケーションです.
+
+前者は,多数のDBやWeb APIをたたいてページを組み立てるケースです.そして後者はAjaxなどを使って動的な逐次描画を行うケースでしょう.
+
+さて,後者の話題について,近未来のWebの姿と噂されている WebSocket と呼ばれるドラフト仕様が期待されています.
+
+WebSocket とは ... 誤解を恐れずにいえば,ブラウザが使う TCP over HTTPみたいなものです.WebSocket の詳細は他の記事にゆだねるとして,この章ではとりあえずWebSocketをnode.jsで使ってみようと思います.
+
+ステップ1: twbot のインストール
+-------------------------------------------------------
+
+WebSocket を使うサンプルとして Twitter の UserStream を使ってみます.まず拙作のtwitterボットツール twbot をインストールします [#f1]_ .
+
+::
+
+    $ npm install twbot
+
+その後,OAuth のConsumerKey, ConsumerSecret を twitter のサイトで作ってください.その上で twbot:config {ConsumerKey} {ConsumerSecret} コマンドを実行してストリームを流すアカウントの AccessKey, AccessSecret を入手します.
+
+::
+
+    $ twbot\:config sl95SqKcJEVSSEUqPHLmNQ kl01lJgUzJzbvdxSZr12PfF3vvnCPSNa1wdzsBBzI
+    The 'sys' module is now called 'util'. It should have a similar interface.
+    please visit http://twitter.com/oauth/authorize?oauth_token=XXXXXX to get verification code.
+    input verification code: {上記URLにアクセスしてVerificationコードを取得して入力}
+
+
+AccessKey, AccessSecret の入手に成功すれば次のように結果が表示されます.
+
+::
+
+    *********************************************************************
+    Access key/secret have been successfully retrieved from Twitter
+    You can use Bot application by following constructions.
+    *********************************************************************
+
+    var TwBot = require("twbot").TwBot
+    var bot = new TwBot({"consumerKey":"XXXXXXXXXX","consumerSecret":"XXXXXXXXXXXXXXX","accessKey":"XXXXXXXXXXXXXXX","accessSecret":"XXXXXXXXXXXXXXX"})
+
+ステップ2: ストリームの動作確認
+-------------------------------------------------------
+
+twbot を使うと簡単にユーザーストリームを取得できます.まず普通に動作することを確認しましょう.debug プラグインを使うとすべてのストリームを標準出力にダンプします.
+
+::
+    
+    var TwBot = require("twbot").TwBot
+    var bot = new TwBot({"consumerKey":"XXXXXXXXXX","consumerSecret":"XXXXXXXXXXXXXXX","accessKey":"XXXXXXXXXXXXXXX","accessSecret":"XXXXXXXXXXXXXXX"})
+    
+    bot.loadPlugin('twbot/plugins/debug');
+    bot.startUserStream();
+
+これを app.js として保存して起動します.接続がうまくいくとストリームの先頭の friends 一覧が表示されるはずです.
+
+::
+
+    $ node app.js
+    { friends: 
+      [ .... ]
+
+うまくいったら次は,debug プラグインをコメントアウトして次のようにしてください.
+
+::
+
+    // bot.loadPlugin('twbot/plugins/debug');
+    bot.on('status', function(tweet){
+        console.log(tweet.user.screen_name + ': ' + tweet.text);
+    }); 
+    bot.startUserStream();
+
+こうすると,status の更新イベントだけを拾って標準出力に垂れ流します.では,この垂れ流し先をブラウザにしましょう.
+
+ステップ3: Webページの用意
+-------------------------------------------------------
+
+app.js に HTTP Server のコードを追加します.startUserStream 自体も非同期メソッドなのでそのまま下に追記していく形でかまいません.
+
+::
+
+    var http = require('http'),
+        fs = require('fs');
+
+    var server = http.createServer(function(req, res){
+       res.writeHeader(200, {'Content-Type': 'text/html'});
+       var read = fs.createReadStream(__dirname + '/websocket.html');
+       read.on('error', function(err){
+          res.end(err.stack);
+       });
+       read.on('data', function(data){
+          res.write(data);
+       });
+       read.on('end', function(){
+          res.end();
+       });
+    });
+    server.listen(3000);
+
+__dirname + /websocket.html でHTMLファイルをホストするようにしているので次のように websocket.html を app.js と同じディレクトリに起きます.
+
+::
+
+    <html>
+      <body>
+        <h1>Twitter UserStream with WebSocket</h1>
+        <p>
+          Hello WebSocket Streaming!
+        </p>
+      </body>
+    </html>
+
+まずはこの状態 app.js を起動し,静的なファイルが正しくホストできていることを確認してください.
+
+
+ステップ4: socket.io で垂れ流す
+-------------------------------------------------------
+
+node.js で WebSocket を手軽に使う方法は socket.io というモジュールを使うことです.例によって npm でインストールします.
+
+::
+
+     $ npm install socket.io
+
+インストールが成功したら app.js にWebSocket用のコードを追加します.bot.on('status', function(tweet){...}) のイベントは移動させた上でブラウザに対してWebSocketを通じてメッセージを垂れ流すようにします.
+
+::
+
+    var io = require('socket.io');
+    var socket = io.listen(server);
+    socket.on('connection', function(client){
+       var fun = function(tweet){
+          console.log(tweet.user.screen_name + ': ' + tweet.text);
+          client.send(JSON.stringify(tweet));
+       };
+       bot.on('status', fun);
+       client.on('disconnect', function(){
+          // クライアントが消えたらイベントを外す
+          bot.removeListener('status', fun);
+       });
+    });
+
+サーバー側にWebSocketの実装を追加するのは簡単です. io.listen(httpServer) するだけです.あとは connection イベントを拾ってクライアントオブジェクトを取得したら,クライアントに対するメッセージ配信のルールを追加していきます.
+
+今回の場合は bot がメッセージをtwitterから受け取ったら即座にclientに配信する,ということで bot の status イベント時に client.send() を使ってメッセージを送信しています.
+
+クライアント側でWebSocketに接続し [#f2]_ ,メッセージを受信するには websocket.html を次のように書き換えます.
+
+::
+
+    <html>
+      <body>
+        <h1>Twitter UserStream with WebSocket</h1>
+        <p>
+          Hello WebSocket Streaming!
+        </p>
+        <div id="statuses"></div>
+      </body>
+      <script src="/socket.io/socket.io.js"></script> 
+      <script> 
+        var socket = new io.Socket();
+        socket.connect();
+        socket.on('connect', function(){
+           // 接続完了時
+        });
+        socket.on('message', function(msg){
+           // メッセージ受信時
+           var status = eval('(' + msg + ')');
+           var elm = document.createElement('p');
+           elm.innerHTML = status.user.screen_name + ': ' + status.text;
+           document.getElementById('statuses').appendChild(elm);
+        });
+      </script>
+    </html>
+
+
+
+ステップ4: アプリケーションをリアルタイムに共有する
+-------------------------------------------------------
+
+WebSocket のすごいところはこれだけではありません.
+
+- クライアント側からもメッセージが送れる.
+- socket.broadcast 関数を用いると,接続中の全クライアントに対してメッセージを配信することができます.
+
+1. は当たり前といえば当たり前の機能ですし,(WebSocketに比べて非効率ですが)普通にHTTPを使って実現できます.2. の機能を見てみましょう.
+
+接続がある度に接続数をカウントして,全配信を行うことにします.そしてクライアント側ではリアルタイムに接続数を更新します.
+
+まずサーバー側のコードです.WebSocketの部分のみを変更しています.twitter のストリームもbroadcastで配信するように変更しています.
+
+::
+
+    var io = require('socket.io');
+    var socket = io.listen(server);
+    var count = 0;
+    bot.on('status', function(tweet){
+       console.log(tweet.user.screen_name + ': ' + tweet.text);
+       socket.broadcast(JSON.stringify(tweet));
+    });
+    socket.on('connection', function(client){
+       // 全クライアント配信
+       count = count + 1;
+       socket.broadcast(count);
+       client.on('disconnect', function(){
+          count = count - 1;
+          socket.broadcast(count);
+      });
+    });
+
+クライアント側のコードでは,number型のメッセージを受け取る度に接続数の表示をアップデートするようなコードを加えます.
+
+::
+
+    <html>
+      <body>
+        <h1>Twitter UserStream with WebSocket</h1>
+        <p>
+          Hello WebSocket Streaming! (<span id="connections"></span>)
+        </p>
+        <div id="statuses"></div>
+      </body>
+      <script src="/socket.io/socket.io.js"></script> 
+      <script> 
+        var socket = new io.Socket();
+        socket.connect();
+        socket.on('message', function(msg){
+          // メッセージ受信時
+           var status = eval('(' + msg + ')');
+           if( typeof status == 'object' ){
+              var elm = document.createElement('p');
+              elm.innerHTML = status.user.screen_name + ': ' + status.text;
+              document.getElementById('statuses').appendChild(elm);
+           }else if( typeof status == 'number' ){
+              document.getElementById('connections').innerHTML = msg;
+           }else{
+              alert(msg);
+           }
+        });
+      </script>
+    </html>
+
+実装が完了したら,ブラウザで複数タブを開くなどして localhost:3000 に同時接続を試みてください.接続数を変更する度に,既存のすべての画面が更新されることが確認できると思います.
+
+broadcast 機能を使うと,サイトに集まるユーザー同士が密に連携するようなそんなアプリケーションを作ることができるのです.
+
+.. [#f1] http://github.com/yssk22/node-twbot にソースコードはおいてあるので何かあれば issue にあげるか twitter などで連絡ください.
+
+.. [#f2] /socket.io/socket.io.js は socket.io モジュールによって自動的にホストされます.このJSを使うことで WebSocket の実装されていないブラウザではFlashを使って同じ挙動にするという実装になり,ブラウザ感の差異を気にする必要がなくなります.