読者です 読者をやめる 読者になる 読者になる

socket.ioの習作解説その参

socket.ioの具体的なサービスコードは、例えるならばトランシーバーみたいな考え方です。自分が通話するときはボタン押してしゃべって、あとは手を離しておけば聞こえるという。

//サーバ側
chatroom.on('connection', function(socket) {
    heartbeat(); 
    socket.on('upstream', function(data) {
        chatroom.emit('downstream', data);
        heartbeat();
    });
});

名前空間「/chatroom」で立ち上げたこいつはWebSocketのI/Oを握ってるEventEmitterなので、それぞれ送受信のハンドラを書いていきます。まずconnectionイベントのハンドラ。ここに設定するコールバックはfunction(socket)とサーバ - ブラウザの個々の接続が渡されてきますので、もらったsocketに受信ハンドラを設定します。「upstream」とあるのはこのサンプルで定義した独自イベント名です。ブラウザからの上りメッセージなので「upstream」としましたが、これはサーバ側とブラウザ側でそろえればどんな名前でもいい。こちらで設定するコールバックはfunction(data)と引数に具体的なデータが入ってきますので、これを用いてサービスのロジックを作ります。サンプルでは個別ブラウザから上がってきたこのdataを名前空間に接続している全ブラウザにプッシュ配信しています。名前空間たるchatroomのemitメソッドで。こちらも第一引数には独自イベント名を定義しました。サーバからの下りメッセージなので「downstream」としています。もちろんこれもサーバ側とブラウザ側でそろえればどんな名前でもいい!

<!--ブラウザ側-->
                chatroom.on('downstream', function (data) {
                    var data$ = $('<div>').text(new Date() + ': ' + data);
                    $('#messageArea').append(data$);
                });
                $('#sendBtn').click(function() {
                    chatroom.emit('upstream', $('#textBox').val());
                    $('#textBox').val('');
                });

ブラウザ側のハンドラでは、サーバからの下りメッセージを捉えるべく「downstream」を押さえます。サーバ側とまったく同じ記述方法で、dataを取り扱うコード。ここではjQueryを使ってメッセージに受信日付をつけてdivタグに囲って表示行をappendしています。
ブラウザからの上りメッセージは、テキストボックスとボタンを配置したうえ、ボタンのclickハンドラに送信コードを仕込みました。こちらもサーバ側とおなじ見かけのコードで、名前空間たるchatroomのemitメソッドで。「upstream」イベント名でサーバ側とそろえて送信しています。
送信ロジックはサーバ側とブラウザ側でおんなじように書けますが、よく考えるとサーバとクライアントで立場違いますので、ちょっと違いますね。サーバ側は一つではなく多数のブラウザと同時に接続しますが、ブラウザ側から見るとサーバは一つです。よって同じchatroom.emit()でもサーバ側は全ブラウザ向け配信になるし、クライアント側はサーバ一つへのメッセージアップロードです。サーバ側を全ブラウザ向け配信ではなく、個別ブラウザへの狙い撃ちメッセージにするには以下。

//サーバ側
    socket.on('upstream', function(data) {
        socket.emit('downstream', data); // chatroom -> socketへの変更
        heartbeat();
    });

chatroom.emit()ではなく、socket.emit()。このsocketはconnectionイベントに設定するコールバックの引数としてもらうsocketのことです。このsocketはクロージャで参照されているので、WebSocketの接続が維持されている間はいつでも使えます。

//サーバ側
var timeoutID = 0;
function heartbeat() {
    clearTimeout(timeoutID);
    timeoutID = setTimeout(function() {
        chatroom.emit('downstream', '沈黙しないで!');
        heartbeat();
    }, 10000);
}

この関数はおまけ。10秒後にタイマーをセットして上りメッセージが途絶えると「沈黙しないで!」と書き込みを促すようにしました。Java的脳だと大丈夫かこれ?と思っちゃいますよね。でもNode.jsは単一ループだから絶対的に後がちルール。非同期実行なのにコンカレントな配慮はまったく必要なし。だから(たぶん)これでイイ。