2011-05-31
Node.jsでWebSocketを試してみる
javascript | |
![]()
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.0make 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.jstelnetコマンドにより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の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, '<').replace(/>/g, '>'); }; // 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://nodejs.jp/nodejs.org_ja/
http://nodejs.jp/nodejs.org_ja/api/index.html
- Node.jsでサーバサイドJavaScript開発入門
http://www.atmarkit.co.jp/fwcr/index/index_nodejs.html
- npm registry
- 246 https://www.google.co.jp/
- 209 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&ved=0CDMQFjAA&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ctbs=lr:lang_1ja&ei=cbJIT9DiGuPJmQXZos2NDg&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw&sig2=euRs6ZXOoihbjB_f6
- 132 http://www.la-nouveau.mydns.jp/wordpress/?p=1280
- 127 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=23&ved=0CDEQFjACOBQ&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=InY7T8_MH9DOmAXF5-nTCg&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw&sig2=x7XpO3_1gyx8MEEHBedCfg
- 88 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=3&cts=1330951621667&ved=0CDsQFjAC&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=vrVUT4DaGsqZmQXE8NSfCg&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw&sig2=qKUEiaIrrb4l-blYOy
- 48 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CEEQFjAD&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=xJpuT9-aJYmOmQXM-_y8Bg&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw&sig2=-GNLAzQBkJyNXWA3PIUBZQ
- 27 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=5&ved=0CFAQFjAE&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=eeZyT6-6C47UmAWKkbSyDw&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw
- 25 http://www.google.co.jp/url?sa=t&rct=j&q=&esrc=s&source=web&cd=6&ved=0CEgQFjAF&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=xgRnT-6TEObImQWGrK2mCA&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw
- 24 http://www.google.co.jp/url?sa=t&rct=j&q=node.js websocket 試す&source=web&cd=1&sqi=2&ved=0CB4QFjAA&url=http://d.hatena.ne.jp/yutakikuchi/20110531/1306798231&ei=X0SmToXSHrChmQXF4fHMDw&usg=AFQjCNHjdloIuR8gHNWG24XovJG8KfEkDw
- 18 http://node-js.info/?p=556




