Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

Node.jsでWebSocketを試してみる

Nodejs

  • サーバサイドJavascript
  • V8 Javascriptを利用。
  • シングルスレッドの非同期処理環境。
  • 処理を待たずにcallbackを実行するイベントループ、ノンブロッキングI/Oを実装。
  • nodejsの設定は簡単。パッケージ化されているし、buildしてもそれほど時間がかからない。

設定

macでinstallしてみる。以下のどちらか一方を行えば設定は可能だがportのversionは0.2.0、buildの最新は0.4.8。post installは少し時間がかかる。

port install
$ sudo port install nodejs
$ node -v
v0.2.0
make install
$ fetch http://nodejs.org/dist/node-v0.4.8.tar.gz
$ tar -xzf node-v0.4.8.tar.gz
$ cd node-v0.4.8
$ ./configure
$ sudo make install
$ ./node -v
v0.4.8

実行

日本語ドキュメントにあるサンプルをそのまま実行してみる。サンプルはHttp,Socketサーバ。以下のそれぞれのJavascriptっぽいコードをsample.jsなどのファイルとして保存してnodeコマンドにより実行する。

Http

スクリプト

var http = require('http');
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');

起動

$ node http.js

アクセス
ブラウザでhttp://127.0.0.1:1337/にアクセスしてみると Hello Worldが表示される。

Socket

スクリプト

var net = require('net');

var server = net.createServer(function (socket) {
  socket.write("Echo server\r\n");
  socket.pipe(socket);
});

server.listen(1337, "127.0.0.1");

起動

$ node socket.js

アクセス
telnetコマンドによりlistenしているポートにアクセスし、文字列を打つとechoされる。

$ telnet 127.0.0.1 1337 
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Echo server
NodeJS Test
NodeJS Test

npm

  • npmとはNode.js用のパッケージマネージャ。Pythonのeasy_installみたいなもの。
  • npmコマンドによりnode.jsのパッケージをinstallできる。
npmのinstall

まずはnpmコマンドのinstall。

$ fetch http://npmjs.org/install.sh
$ chmod +x ./install.sh
$ sudo ./install.sh
$ npm -v
1.0.6
パッケージのinstall

npmコマンドによりWebSocket実装可能なパッケージ(Socket.io)をinstallする。

$ sudo npm install socket.io

WebSocket

nodejsをつかってWebSocketを試してみる。

WebSocketとは
  • Cometに取って代わる技術と言われており、専用プロトコルでクライアント/サーバ間のConnectionを張りっぱなしにできる。
  • HTML5の仕様に組み込まれる予定だったが、現在では独自のプロトコルとして仕様が策定されている。
  • 対応ブラウザ:FF4、safari5、Chrome4、Opera11、IE(HTML5Labs)
  • nodejsだけでなくPHPでもWebSocketサーバは設定できる。http://code.google.com/p/phpwebsocket/
Socket.ioのサンプルを見てみる

npmによりinstallしたsocket.ioパッケージのsampleはnode_modules/socket.io/example/に配置される。node_modules/socket.io/example/server.jsというサーバのサンプルプログラムを見てみる。初心者で全くnodejsに詳しくないが、だいたい以下のような感じだと思う。

  • http.createServerでサーバオブジェクト作成。
  • server.listen(8080)でポートを指定して起動。
  • client.sendでclientにメッセージを送信。
  • client.broadcastで全てのclientにメッセージを送信。
/**
 * Important note: this application is not suitable for benchmarks!
 */

// 必要なモジュールのrequire
var http = require('http')
  , url = require('url')
  , fs = require('fs')
  , io = require('../')
  , sys = require(process.binding('natives').util ? 'util' : 'sys')
  , server;

//serverのcreate
server = http.createServer(function(req, res){
  // your normal server code
  var path = url.parse(req.url).pathname;
  switch (path){
    case '/':
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write('<h1>Welcome. Try the <a href="/chat.html">chat</a> example.</h1>');
      res.end();
      break;
      
    case '/json.js':
    case '/chat.html':
      fs.readFile(__dirname + path, function(err, data){
        if (err) return send404(res);
        res.writeHead(200, {'Content-Type': path == 'json.js' ? 'text/javascript' : 'text/html'})
        res.write(data, 'utf8');
        res.end();
      });
      break;
      
    default: send404(res);
  }
}),

//caseに当てはまらなかった時の404処理
send404 = function(res){
  res.writeHead(404);
  res.write('404');
  res.end();
};

//8080ポートで起動
server.listen(8080);

// socket.io, I choose you
// simplest chat application evar
var io = io.listen(server)
  , buffer = [];
 
//clientとconnectionが張られているとき
io.on('connection', function(client){
  //clientにbufferを送信
  client.send({ buffer: buffer });
  //全てのclientにclientidとつながっていることをアナウンス
  client.broadcast({ announcement: client.sessionId + ' connected' });
  
  //clientからメッセ時を受け取ったとき
  client.on('message', function(message){
    //メッセージ情報をbufferに格納。
    var msg = { message: [client.sessionId, message] };
    buffer.push(msg);
    if (buffer.length > 15) buffer.shift();
    //メッセージをその他clientにブロードキャストする
    client.broadcast(msg);
  });

  //clientとの接続が切れたとき
  client.on('disconnect', function(){
    //clientが切断されたことをその他clientにブロードキャストする。
    client.broadcast({ announcement: client.sessionId + ' disconnected' });
  });
});
クライアントhtml

json.jsとsocket.io/socket.io.jsを読み込む必要がある。以下はおれおれ解説。

  • new io.SocketでSocketオブジェクト作成。
  • socket.connectでサーバとのconnectionを確立。
  • socket.on('connect',(省略)でサーバとconnectionが確立されたらハンドリングする。
  • socket.on('message',(省略)でサーバからメッセージを受信した時にハンドリングする。
  • socket.onのその他 disconnect(切断)、reconnect(再接続後)、reconnecting(再接続中)の時のハンドリング。
<!doctype html>
<html>
  <head>
    <title>socket.io client test</title>
        
    <script src="/json.js"></script> <!-- for ie -->
    <script src="/socket.io/socket.io.js"></script>
  </head>
  <body>
    
    <script>
      //メッセージ出力関数。
      function message(obj){
        var el = document.createElement('p');
        if ('announcement' in obj) el.innerHTML = '<em>' + esc(obj.announcement) + '</em>';
        else if ('message' in obj) el.innerHTML = '<b>' + esc(obj.message[0]) + ':</b> ' + esc(obj.message[1]);
        
        if( obj.message && window.console && console.log ) console.log(obj.message[0], obj.message[1]);
        document.getElementById('chat').appendChild(el);
        document.getElementById('chat').scrollTop = 1000000;
      }
      
      //メッセージ送信
      function send(){
        var val = document.getElementById('text').value;
        socket.send(val);
        message({ message: ['you', val] });
        document.getElementById('text').value = '';
      }
      
      //HTML escape
      function esc(msg){
        return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;');
      };
      
      // socketオブジェクト作成
      var socket = new io.Socket(null, {port: 8080, rememberTransport: false});
      // サーバとのconnection確立
      socket.connect();
      //メッセージ受信ハンドリング
      socket.on('message', function(obj){
        if ('buffer' in obj){
          document.getElementById('form').style.display='block';
          document.getElementById('chat').innerHTML = '';
          
          for (var i in obj.buffer) message(obj.buffer[i]);
        } else message(obj);
      });
      
      //connection確立ハンドリング
      socket.on('connect', function(){ message({ message: ['System', 'Connected']})});
      //connection切断ハンドリング
      socket.on('disconnect', function(){ message({ message: ['System', 'Disconnected']})});
      //connection再確立ハンドリング
      socket.on('reconnect', function(){ message({ message: ['System', 'Reconnected to server']})});
      //connection再接続中ハンドリング
      socket.on('reconnecting', function( nextRetry ){ message({ message: ['System', 'Attempting to re-connect to the server, next attempt in ' + nextRetry + 'ms']})});
      socket.on('reconnect_failed', function(){ message({ message: ['System', 'Reconnected to server FAILED.']})});
    </script>
    
    <h1>Sample chat client</h1>
    <div id="chat"><p>Connecting...</p></div>
    <form id="form" onSubmit="send(); return false">
      <input type="text" autocomplete="off" id="text"><input type="submit" value="Send">
    </form>
    
    <style>
      #chat { height: 300px; overflow: auto; width: 800px; border: 1px solid #eee; font: 13px Helvetica, Arial; }
      #chat p { padding: 8px; margin: 0; }
      #chat p:nth-child(odd) { background: #F6F6F6; }
      #form { width: 782px; background: #333; padding: 5px 10px; display: none; }
      #form input[type=text] { width: 700px; padding: 5px; background: #fff; border: 1px solid #fff; }
      #form input[type=submit] { cursor: pointer; background: #999; border: none; padding: 6px 8px; -moz-border-radius: 8px; -webkit-border-radius: 8px; margin-left: 5px; text-shadow: 0 1px 0 #fff; }
      #form input[type=submit]:hover { background: #A2A2A2; }
      #form input[type=submit]:active { position: relative; top: 2px; }
    </style>
    
  </body>
</html>

サンプルのSocketサーバの起動

$ node node_modules/socket.io/example/server.js

アクセス
複数のWebブラウザでアクセスしてコメントを投稿してみるとWebSocketのチャットが利用可能。
http://127.0.0.1:8080/chat.html

リファレンス

  • 日本語ドキュメント

http://nodejs.jp/nodejs.org_ja/

  • API日本語マニュアル

http://nodejs.jp/nodejs.org_ja/api/index.html

  • Node.jsでサーバサイドJavaScript開発入門

http://www.atmarkit.co.jp/fwcr/index/index_nodejs.html

  • npm registry

http://search.npmjs.org/#/_browse/all