速習!node.js + socket.io

WebSocketの動作を確認するために、以下のサイトで公開されている簡単なチャットルームを動かしてみました。
http://spechal.com/2011/03/19/super-simple-node-js-chatroom/

このサンプルは、node.js、node.jsのモジュールsocket.ioを利用します。

node.js、socket.ioの概要やインストールについては@ITの記事がまとまっています。
http://www.atmarkit.co.jp/fwcr/rensai2/nodejs01/01.html

以下、サンプルを動作させるまでの必要最低限のみの手順を説明します。
サーバーはUbuntu 10.04にセットアップしました。

node.jsのインストール

本家サイト
http://nodejs.org/
本家セットアップ手順
https://github.com/joyent/node/wiki/Installation

前提条件
  • Pythone2.4以上
  • libssl-dev

UbuntuだとPythonはデフォルトで入っています(v2.6.5)、libsslもopensslで使うライブラリなので大抵インストール済みだと思いますがインストールする場合は

$ sudo apt-get install libssl-dev

モジュールの取得とコンパイル(2011/04/12現在)

$ wget http://nodejs.org/dist/node-v0.4.5.tar.gz
$ tar zxvf node-v0.4.5.tar.gz
$ cd node-v0.4.5
$ ./configure
$ make
$ sudo make install

インストールの確認

$ node -v
v0.4.5

※ここまで書いて後で気づきましたが、バイナリパッケージも用意されているのでapt-getでもインストール可能です。

socket.ioのインストール

本家サイト
http://socket.io/

npmのインストール

socket.ioをインストールするにはnpmというnode.jsのパッケージ管理ツールをインストールする必要があります。
インストールはcurlが必要です。インストール済みの場合は省略してください。

$ sudo apt-get install curl
$ curl http://npmjs.org/install.sh | sudo sh

※/usr/local以下への書き込みが発生するので、sudoで実行します。

socket.ioのインストール
$ sudo npm install socket.io 

サンプルの実行

以下をchatroom.jsで保存

var http = require('http'), // HTTP server
  io = require('socket.io'), // Socket.io
  fs = require('fs'); // File System
 
// make a standard server
server = http.createServer(function(req, res){
    res.writeHead(200, {'Content-Type': 'text/html'});
    // read index.html and send it to the client
    var output = fs.readFileSync('./index.html', 'utf8');
    res.end(output);
});
// run on port 8080
server.listen(8080);
 
// listen to the server
var socket = io.listen(server);
// on a connection, do stuff
socket.on('connection', function(client){
        // broadcast the connection
	client.broadcast({message: client.sessionId + ' is now available'});
        // when the server gets a message, during a connection, broadcast the message
	client.on('message', function(msg){ client.broadcast({ message: client.sessionId + ': ' + msg.message }); });
        // when the server gets a disconnect, during a connection, broadcast the disconnection
	client.on('disconnect', function(){ client.broadcast({ message: client.sessionId + ' is no longer available' }); });
});

http://spechal.com/2011/03/19/super-simple-node-js-chatroom/より転記

上記のファイルと同じディレクトリにindex.htmlを作成します。

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Chatroom</title>
  <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
  <script src="http://127.0.0.1:8080/socket.io/socket.io.js"></script> 
  <script> 
    // make a connection to the server
    var socket = new io.Socket('127.0.0.1', {port:8080, connectTimeout:3000});
    // make a message buffer in case things get congested
    var buffer = [];
    socket.connect();
 
    socket.on('connection', function(client){
          // on connection, send the buffer and tell broadcast, to the server, the connection
  	  client.send({buffer: buffer});
  	  client.broadcast({ announcement: client.sessionId + ' connected' });
    });
 
    socket.on('connect_failed', function(){
          alert('The connection to the server failed.');
    });
 
    socket.on('message', function(message){
          // when the client gets a message from the server, 
          // add it to the buffer and display the message
  	  buffer.push(message);
  	  if(buffer.length > 15)
  		  buffer.shift();	  
  	  appendMessage(message.message);
    });
 
    socket.on('disconnect', function(client){ 
      // on disconnect, broadcast it to the server
      client.broadcast({ announcement: client.sessionId + ' disconnected' });
    });
 
    // send a message to the server
    function sendMessage(message){
  	  if(!message){
                // get the message from the input field
  	  	var msg = $("input#message").val(); 
  	  	$("input#message").val('');
  	  } else {
  		var msg = message; 
  	  }
  	  if(msg.length > 0){ // stop annoying empty messages
  	  	if(socket.send({message:msg})) // send the message
      	  	    appendMessage('You: ' + msg); // show the message
  	  }
    }
 
    // show the message on the screen
    function appendMessage(message){
  	  $('div#chat-box').append('<div class="msg">' + message + '</div>'); 
    }
  </script> 
</head>
<body>
<div id="chat-box"></div>
<div id="chat-field">
  <form method="post" action="" onsubmit="return false;" />
    <input type="text" name="message" id="message" value="" /><input id="client" name="client" value="" type="hidden" /><input type="submit" class="button" name="send" id="send" value="Send" onclick="sendMessage();" />
  </form>
</div>
</body>
</html>

http://spechal.com/2011/03/19/super-simple-node-js-chatroom/より転記

サーバーを実行

$ node chatroom.js

http://localhost:8080にアクセス、チャットができてます。

おまけ

tcpflowで中身見てみた。
(tcpflowの使い方: http://d.hatena.ne.jp/tkman/20101203/1291343298)

$ sudo tcpflow -c port 8080 -i lo
tcpflow[6325]: listening on lo
127.000.000.001.48085-127.000.000.001.08080: .~m~20~m~~j~{"message":"........."}.
127.000.000.001.08080-127.000.000.001.60915: .
127.000.000.001.08080-127.000.000.001.60915: ~m~38~m~~j~{"message":"1779476145748049: ........."}
127.000.000.001.08080-127.000.000.001.60915: .
127.000.000.001.08080-127.000.000.001.60915: .
127.000.000.001.08080-127.000.000.001.48085: ~m~5~m~~h~10
127.000.000.001.08080-127.000.000.001.48085: .
127.000.000.001.48085-127.000.000.001.08080: .~m~5~m~~h~10.
127.000.000.001.48085-127.000.000.001.08080: .~m~21~m~~j~{"message":"hello"}.
127.000.000.001.08080-127.000.000.001.60915: .
127.000.000.001.08080-127.000.000.001.60915: ~m~39~m~~j~{"message":"1779476145748049: hello"}

「.~m~20~m~~j~{"message":"........."}.」の.....は日本語のメッセージです。エンコードUTF-8で送信されています。
「あ」を送信したときのダンプ
>| .
0000000F 00 7e 6d 7e 31 38 7e 6d 7e 7e 6a 7e 7b 22 6d 65 .~m~18~m ~~j~{"me
0000001F 73 73 61 67 65 22 3a 22 e3 81 82 22 7d ff ssage":" ..."}.