WebSocket
最近、WebサーバからWebクライアントにデータをPushする技術としてWebSocketが脚光を浴びています。従来だと、Comet(AJAXの返答を引き延ばす技法=ロングポーリング)を用いて擬似的にPushしていたのですが、(サーバサイドもクライアントサイドも)プログラムが面倒だったり、クロスドメインへの対応がイマイチだったり、リアルタイム性が乏しかったりなどの理由で、だんだんとWebSocketに移ってきています。気づくと、サーバ側ではpywebsocketやnode.websocket.js、Jetty、mojoなどの実装が出てきましたし、クライアント側ではChromeやFirefoxが対応しており、そろそろ手を出してみても良い頃合いになってきました。ruby好きなので、サーバ側にrev-websocketを使ってみたいと思います。rev-websocketの準備
今回は、OSにDebian Squeezeを使用します。正直、aptの便利さと、Debianのパッケージの多彩さとGUIいらずで軽量なヴァーチャルマシンを多数用意できるポータビリティにメロメロです。まずはruby関連でインストールするパッケージです。入れたパッケージのメモを取り忘れたので、dpkg -l | grep rubyした結果を示します。不要なモノもありますが、リストに載せておきます。
libruby1.8
libsqlite3-ruby1.8
ruby
ruby-dev
ruby1.8
ruby1.8-dev
rubygems1.8
次に、rubygem関連でインストールするパッケージです。確かこれだけで良かったと思います。
rev
rev-websocket
revを検索したところ「Rev is a high performance event library for Ruby built on top of libev.」「libevのrubyバインディング」「Rubyの高速なイベント駆動IOライブラリ」などと書かれていました。便利そうなので使います。
revのサンプルプログラムを読む
まずは、revのサンプルコードを試してみます。/usr/lib/ruby/gems/1.8/gems/rev-0.3.2/README.textileを覗いてみると、echo serverの例が載ってます。gemを使ってインストールしたので、そのままではrevが無いと言われ、1行目にrubygemsのrequireを追加してあります。require 'rubygems' require 'rev' HOST = 'localhost' PORT = 4321 class EchoServerConnection < Rev::TCPSocket def on_connect puts "#{remote_addr}:#{remote_port} connected" end def on_close puts "#{remote_addr}:#{remote_port} disconnected" end def on_read(data) write data end end server = Rev::TCPServer.new(HOST, PORT, EchoServerConnection) server.attach(Rev::Loop.default) puts "Echo server listening on #{HOST}:#{PORT}" Rev::Loop.default.runこのソースを見ると、(1)Rev::TCPSocketを継承したクラスを作り、(2)接続されたり、接続が切れたりしたら「on_connect」「on_close」が呼び出され、データを受信したら「on_read」が呼び出され、(3)20行目にあるようにRev::TCPServerのオブジェクトを作る際にポート番号や作成したクラスを登録すれば良いことなどが分かります。あと細かいことでは、wirteメソッドを使うとクライアントにデータを送れることも分かります。 実際に「telnet localhost 4321」で接続してみると、確かに入力した文字列がそのまま返されることが分かります。また、最終行の「Rev::Loop.default.run」によりイベントループが回り出すのですが、プログラムの実行してはこれより下には行きません。もっと複雑なプログラムを組もうとしたら、スレッド化が必要かも知れません。
WebSocketのサンプルプログラムを読む
revのソースコードの書き方が分かったところで、WebSocketのサンプルも試してみます。こちらは/usr/lib/ruby/gems/1.8/gems/rev-websocket-0.1.3/README.rdocの中にサンプルがあります。require 'rubygems' require 'rev/websocket' class MyConnection < Rev::WebSocket def on_open puts "WebSocket opened" send_message("Hello, world!") end def on_message(data) puts "WebSocket data received: '#{data}'" send_message("echo: #{data}") end def on_close puts "WebSocket closed" end end host = '0.0.0.0' port = '8080' server = Rev::WebSocketServer.new(host, port, MyConnection) server.attach(Rev::Loop.default) Rev::Loop.default.runechoサーバのプログラムと比べても、ほとんど違いがありません。構造的に違うのは、2行目のrequireと、4行目で作成しているクラスのスーパークラスがRev::WebSocketであることと、23行目でRev::WebSocketServer.newとしている箇所です。 サーバができたので試してみましょう。ただし、telnetコマンドではWebSocketサーバと通信できません。いや、正確には可能なのですが、繋がった後に渡すパラメータが複雑なので現実的ではありません。そこで、とりあえずChromeのJavaScriptコンソールから接続してみたいと思います。下記のプログラムを入力してみて下さい。接続先ホスト名は必要に応じて変更して下さい。うまく動作すれば、3行目で送信したメッセージが、アラートウィンドウに表示されます。
var ws = = new WebSocket( 'ws://localhost:8080/' ); ws.onmessage = function(event){ alert( event.data) ; } ws.send( "This is a test message." );
重要な部分は、WebSocketのオブジェクトを生成すること、データを受信した際にonmessageが実行されること、受信したデータはevent.dataで取得できること、データ送信はsendメソッドであることです。
このように、WebSocketを使うことは難しくありません。皆さんもお試し下さい。 ツイート