Node.js + Socket.IOでチャットシステム (第2回)

さて、前回の記事では、node.js (+ express)の環境構築と、チャット画面の出力を行いました。

前回の記事はこちらからご覧になれます。

さて、今回はいよいよSocket.IOにご登場いただき、インタラクティブ・ウェブのドアを叩きたいと思います。


Step 4: socket.ioのテスト

まずはローカル環境にSocket.IOのインストールを行います。前回同様に

$ npm install --save socket.io

とコマンドを打ってpackage.jsondependenciesも同時に更新してあげます。

次にindex.jsの更新です。まずはソースコードを載せます。

// index.js
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

app.get('/', function(req, res) {
    res.sendFile(__dirname + '/index.html');
});

io.on('connection', function(socket) {
    console.log('a user connected');
});

http.listen(3000, function() {
    console.log('listening on *:3000');
});

新しく追加されたのはSocket.IOモジュールの呼び出しとio.onです。

Socket.IOモジュールはサーバーオブジェクトを引数にとる関数を返してきますので、その関数にサーバーオブジェクト(コード中ではhttp)を渡してあげます。

Socket.IOにはいくつかのイベントがあらかじめ用意されていて、その一つがconnectionです。イベントへのコールバック関数の登録は**on(eventName, callback)**という関数で行います。

今回のコードでは、クライアントがサーバーに接続した際にlogを出力するという単純なものです。

さて、これでリモート側はOKなのですが、クライアントが開くindex.html側でconnectionイベントが起こったことをindex.jsに知らせる必要があります。

そのためにindex.htmlの****の直前に次のコードを追加します。


<script type="text/javascript">
    var socket = io();
</script>

このコードでの注目点は2つです。

1つ目はsocket.io.jsというファイルが新しくロードされていること。このファイルは自前で用意して適切な位置に配置する必要はなくindex.jsを呼び出すと自動的にサーバーのこの位置にファイルが自動生成されます。

次にioオブジェクトのインスタンス化ですが、このインスタンス化によりconnectionイベントが呼び出されます。

この変更点を加えると、ブラウザでページを開いたときに**“a user connected”という文字列が「コンソール上」**に表示されるようになります。

またindex.jsio.onを次のように書き換えることで、クライアントが接続を切ったイベントを補足することもできます。

io.on('connection', function(socket) {
    console.log('a user connected');
    socket.on('disconnect', function() {
        console.log('user disconnected');
    });
});

Step 5: emitイベント

次にSocket.IOのとても大事な機能であるemitイベントを使ってみます。このイベントはクライアント側で呼び出されると、それがnode.jsのサーバーで捕捉されて、何等かの処理を行うことができます。

まずindex.htmlを次のように書き換えて、buttonが押されたときに、emitイベントが発生するようにします。

ここではjQueryを使っているのと、ボタンを押すと同時にチャット用inputの内容がクリアしていることに注意してください。


<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
  var socket = io();
  $('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
  });
</script>

一方index.jsの方ではsocket.onでhtml側から投げられる**‘chat message’**というイベントを補足して処理します(この’chat message’という名前は任意です)。

io.on('connection', function(socket) {
    console.log('a user connected');
    socket.on('disconnect', function() {
        console.log('user disconnected');
    });

    socket.on('chat message', function(msg) {
        console.log('message: ' + msg);
    });
});

すると、メッセージを入力して、Sendボタンを押すたびに「コンソール上」にメッセージが表示されるようになります(あまり嬉しくない)。


Step 6: ブラウザ上でのメッセージ表示

さて、ここまでひたすらコンソール上に文字を出力してきたわけですが、実際にブラウザ上で文字が出力されなければ全くうれしくないわけです。

先ほどはクライアント側からio.emitで**‘chat message’というイベントを投げましたが、実はio.emit**は逆方向、すなわちリモートからクライアントへイベントを投げることも可能です。

そこで、クライアントから**‘chat message’イベントを捕捉したら、送られてきたメッセージを現在つないでいるクライアントに対して同じく‘chat message’イベントとしてemit**します。

io.on('connection', function(socket) {
    console.log('a user connected');
    socket.on('disconnect', function() {
        console.log('user disconnected');
    });

    socket.on('chat message', function(msg) {
        console.log('message: ' + msg);
        io.emit('chat message', msg);
    });
});

そうしたらindex.html側では**‘chat message’イベントを捕捉して、ul#messagesに対してメッセージをappend**します。


    var socket = io();
    $('form').submit(function() {
        socket.emit('chat message', $('#m').val());
        $('#m').val('');
        return false;
    });

    socket.on('chat message', function(msg) {
        $('#messages').append($('<li>').text(msg));
    });
</script>

これでようやく以下のようなチャット画面を得ることができます。めでたしめでたし。

nodejs_chat_demo

ちなみに、1つのブラウザでやってもあまり面白くないですが、複数のブラウザや複数のタブでチャットをすると、ちゃんと他の発言が全体に対して送信されていることが分かります。

さて、ここまではSocket.IOのチュートリアルをなぞっているだけでしたが、次回からは少しこのチャットシステムをいじって遊んでみます。

それでは、今回も最後までお読みいただき、ありがとうございました。