連載
» 2016年07月04日 05時00分 公開

Socket.IO開発時に役立つツール4選とroom、namespaceライブラリの使い方Socket.IOで始めるWebSocket超入門(終)(2/3 ページ)

[猪熊朔也,三菱総研DCS]

namespaceを使った別機能の実装

 次に、namespaceを使って、チャット以外の機能を実装します。今回は例として、接続したクライアントに今日の運勢を表示する機能を追加します。イメージ図を以下に記載します。

今日の運勢機能追加イメージ図

 それでは、実装です。

サーバ側の実装

 room解説時とは異なり、サーバサイドから説明します。

// S01. 必要なモジュールを読み込む
var http = require('http');
var socketio = require('socket.io');
var fs = require('fs');
// S02. HTTPサーバを生成する
var server = http.createServer(function(req, res) {
    res.writeHead(200, {'Content-Type' : 'text/html'});
    res.end(fs.readFileSync(__dirname + '/index.html', 'utf-8'));
}).listen(3000);  // ポート競合の場合は値を変更
 
// S03. HTTPサーバにソケットをひも付ける(WebSocket有効化)
var io = socketio.listen(server);
 
// チャット機能
// S04. connectionイベントを受信する
var chat = io.of('/chat').on('connection', function(socket) {
    var room = '';
    var name = '';
 
    // roomへの入室は、「socket.join(room名)」
    socket.on('client_to_server_join', function(data) {
        room = data.value;
        socket.join(room);
    });
    // S05. client_to_serverイベント・データを受信する
    socket.on('client_to_server', function(data) {
        // S06. server_to_clientイベント・データを送信する
        chat.to(room).emit('server_to_client', {value : data.value});
    });
    // S07. client_to_server_broadcastイベント・データを受信し、送信元以外に送信する
    socket.on('client_to_server_broadcast', function(data) {
        socket.broadcast.to(room).emit('server_to_client', {value : data.value});
    });
    // S08. client_to_server_personalイベント・データを受信し、送信元のみに送信する
    socket.on('client_to_server_personal', function(data) {
        var id = socket.id;
        name = data.value;
        var personalMessage = "あなたは、" + name + "さんとして入室しました。"
        chat.to(id).emit('server_to_client', {value : personalMessage});
    });
    // S09. dicconnectイベントを受信し、退出メッセージを送信する
    socket.on('disconnect', function() {
        if (name == '') {
            console.log("未入室のまま、どこかへ去っていきました。");
        } else {
            var endMessage = name + "さんが退出しました。"
            chat.to(room).emit('server_to_client', {value : endMessage});
        }
    });
});
 
// 今日の運勢機能
var fortune = io.of('/fortune').on('connection', function(socket) {
    var id = socket.id;
    // 運勢の配列からランダムで取得してアクセスしたクライアントに送信する
    var fortunes = ["大吉", "吉", "中吉", "小吉", "末吉", "凶", "大凶"];
    var selectedFortune = fortunes[Math.floor(Math.random() * fortunes.length)];
    var todaysFortune = "今日のあなたの運勢は… " + selectedFortune + " です。"
    fortune.to(id).emit('server_to_client', {value : todaysFortune});
});
app.js(14、16、28、39、47、52〜60行目が追加・変更部分)

 サーバサイドでは、チャット用・今日の運勢用それぞれのWebSocketコネクションをnamespace(名前空間)を区切って定義しています。チャットは/chat、今日の運勢は/fortuneです。namespaceを指定するには、「of(/namespace名)」を使用します。

 追加実装前は、ioという変数を介してデータの送受信を行っていました。しかし、追加実装後は名前空間が指定されたコネクションを、それぞれchat、fortune変数を介して使用します。そのため、データのやりとりを行うためにはio変数部分をそれぞれchat、fortuneに変更することが必要です。

 ただし、データの受信とbroadcast送信は、名前空間を定義した場合でもsocket変数を介して行います。socket.onとsocket.broadcast部分には変更がないことに注目してください。今日の運勢機能では、運勢データをランダムでクライアントに送信する処理を追加しています。

クライアント側の実装

 次に、クライアントサイドです。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>websocket-chat</title>
    <link rel="stylesheet"
    href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <!-- C01. Socket.IOクライアントライブラリの読込み -->
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
</head>
<body>
    <div class="container">
        <h1>WebSocket-Chat</h1>
        <form class="form-inline">
            <div class="form-group">
                <label class="roomLabel" for="rooms">部屋:</label>
                <select class="form-control" id="rooms">
                    <option value="room01">部屋01</option>
                    <option value="room02">部屋02</option>
                </select>
                <label class="nameLabel" for="msgForm">名前:</label>
                <input type="text" class="form-control" id="msgForm">
            </div>
            <button type="submit" class="btn btn-primary" id="sendButton">入室</button>
        </form>
        <br>
        <div id="chatLogs">
            <p>=====チャットログ=====</p>
        </div>
        <br>
        <div id="fortune">
            <p>======今日の運勢======</p>
        </div>
    </div>
 
    <script type="text/javascript">
        var chat = io('http://localhost:3000/chat');
        var fortune = io('http://localhost:3000/fortune');  // C02. ソケットへの接続
        var isEnter = false;
        var name = '';
 
        // C04. server_to_clientイベント・データを受信する
        chat.on("server_to_client", function(data){appendMsg(data.value)});
        fortune.on("server_to_client", function(data) {appendFortune(data.value)});
 
        function appendMsg(text) {
            $("#chatLogs").append("<div>" + text + "</div>");
        }
 
        function appendFortune(text) {
            $("#fortune").append("<div>" + text + "</div>");
        }
 
        $("form").submit(function(e){
            var message = $("#msgForm").val();
            var selectRoom = $("#rooms").val();
            $("#msgForm").val('');
            if (isEnter) {
              message = "[" + name + "]: " + message;
                // C03. client_to_serverイベント・データを送信する
                chat.emit("client_to_server", {value : message});
            } else {
                name = message;
                var entryMessage = name + "さんが入室しました。";
                chat.emit("client_to_server_join", {value : selectRoom});
                // C05. client_to_server_broadcastイベント・データを送信する
                chat.emit("client_to_server_broadcast", {value : entryMessage});
                // C06. client_to_server_personalイベント・データを送信する
                chat.emit("client_to_server_personal", {value : name});
                changeLabel();
            }
            e.preventDefault();
        });
 
        function changeLabel() {
            $(".nameLabel").text("メッセージ:");
            $("#rooms").prop("disabled", true);
            $("button").text("送信");
            isEnter = true;
        }
    </script>
</body>
</html>
index.html(27、31〜34、38、39、44、45、51〜53、62、66〜70行目が追加・変更部分)

 クライアントサイドでは、ソケット接続処理を変更しています。これは、サーバサイドで/chatと、/fortune2つのWebSocketコネクションを定義したためです。

 namespaceが与えられたソケット接続には「io(connectionURL)」を使用します。引数なしの「io()」や「io.connect()」使用時には、デフォルトで「/」という名前空間を持つWebSocketコネクションが生成されます。チャット用のイベントはchat変数を、今日の運勢用のイベントはfortune変数を介して発火されます。

動作確認

 コードの変更・保存が完了したら、アプリケーションの動作を確認します。

 nodeプロセスを再起動した後に、ブラウザウィンドウでアプリケーションを開いてください。チャットログエリアの下に、今日の運勢が表示されていることが確認できると思います。

今日の運勢機能動作確認

 クライアントサイド、サーバサイドそれぞれ独自に定義した変数を使って処理が実行されます。このようにnamespaceを使用することで、機能追加でコードの量が増えても、1つ1つの処理がどの機能のものか分かりやすい形で追加開発を進めることが可能です。

namespaceの定義:of(namespace名)

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。