Let's create Node.js Application

Socket.IOでリアルタイムWebを実感しよう

チャットを作りながら学ぶSocket.IO〈Node.jsシリーズ vol.4〉

Node.jsの基本を順を追って展開していくこの連載。
今回は中級編の第2回目として、リアルタイムWebを実感してみましょう。

Socket.IOでリアルタイムWebを実感しよう

前回はTwitter Streaming APIにアクセスしてそのデータをリアルタイムに表示しました。
今回はSocket.IOというフレームワークを組み合わせ、クライアント同士でリアルタイムな通信を実現します。

リアルタイムWebとは

リアルタイムWebとは、その名の通り、Web上の異なるクライアント間でリアルタイムに通信を行うことです。
現在は主に以下の技術が提唱されています。

  • ポーリング
    クライアントがサーバーに一定間隔で問い合わせ続け、変更があり次第処理を行う

  • ロングポーリング
    サーバーがクライアントからのリクエストを保留し、変更があった場合にレスポンスを返す
    すぐには答えず「何かあったら返事する」という方式

  • ストリーミング
    サーバーがデータを流し続け、クライアントはそれを受け取り続けながら適切なタイミングで処理を行う
    前回、Twitterとの通信に使った方式

  • WebSocket
    JavaScript上でサーバーとの双方向通信を行うために提唱された専用技術
    WebSocketに対応する比較的新しいブラウザ環境が必要

ポーリング、ロングポーリング、ストリーミングはオーソドックスな技術、WebSocketはHTML5の仕様に含まれる比較的新しい技術です。
いずれもシンプルな仕様ではありますが、要件や環境に応じて都度適切な方法を選んで実装する必要があります。

このすべてを実装して切り替える、というのも大変ですね。
そこで活躍するのが、Socket.IOです。

Socket.IOを使う

Socket.IOとは、これらリアルタイムWebの技術を簡単に使うためのフレームワークです。

サーバー側の処理を実現するためのNode.jsライブラリと、クライアント側の処理を実現するためのJavaScriptライブラリで構成されています。

Socket.IOは、アクセス環境に応じて最適なリアルタイムWebの手段を自動的に選択し、同一のインターフェースでリアルタイムWebを提供します。
開発者は裏で走る仕組みを意識することなく、ひとつのコードでリアルタイムWebを実現することができます。

インストール

パッケージがnpm上に用意されているので、インストールは簡単です。
npm install socket.ioとterminalに入力しインストールできます。

Socket.IOでリアルタイムチャットを作る

今回はSocket.IOを使って、異なるクライアント間でリアルタイムなチャットができるシステムを作ってみます。
まずは、ウィンドウに入力フォームと発言ボタンがあるだけの、シンプルなもので試してみましょう。

サーバー側コード

サーバー側では、画面用のHTTPサーバー、そして実際のチャット処理をSocket.IOで定義します。

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var http = require( 'http' ); // HTTPモジュール読み込み
var socketio = require( 'socket.io' ); // Socket.IOモジュール読み込み
var fs = require( 'fs' ); // ファイル入出力モジュール読み込み

// 3000番ポートでHTTPサーバーを立てる
var server = http.createServer( function( req, res ) {
    res.writeHead(200, { 'Content-Type' : 'text/html' }); // ヘッダ出力
    res.end( fs.readFileSync('./index.html', 'utf-8') );  // index.htmlの内容を出力
}).listen(3000);

// サーバーをソケットに紐付ける
var io = socketio.listen( server );

// 接続確立後の通信処理部分を定義
io.sockets.on( 'connection', function( socket ) {

    // クライアントからサーバーへ メッセージ送信ハンドラ(自分を含む全員宛に送る)
    socket.on( 'c2s_message', function( data ) {
        // サーバーからクライアントへ メッセージを送り返し
        io.sockets.emit( 's2c_message', { value : data.value } );
    });

    // クライアントからサーバーへ メッセージ送信ハンドラ(自分以外の全員宛に送る)
    socket.on( 'c2s_broadcast', function( data ) {
        // サーバーからクライアントへ メッセージを送り返し
        socket.broadcast.emit( 's2c_message', { value : data.value } );
    });
});

var io = socketio.listen( server );で、予め立てたサーバーを通信の口となるソケットに紐付けています。
そしてio.sockets.on('connection' ...の部分で、ソケットに接続したあとの通信処理を定義しています。

接続の具体的な処理は、ハンドラとして定義します。
ハンドラ名は、予約語である「connection」、「message」、「disconnect」以外であれば、自由につけることができます。
今回は、以下の様な形で定義しました。これは、クライアントからも呼び出すことができます。

今回は、Socket.IOの機能を説明するため、二通りの方法でメッセージの送信先を定義しています。

  • io.socket.emit

    • 通常の送信方法です。
    • 自分を含む全員宛てにメッセージを送信します。通常のチャットの発言に使える処理です。
  • socket.broadcast.emit

    • ブロードキャストと呼ばれる送信方法です。
    • 自分以外の全員宛てにメッセージを送信します。たとえば「○○さんが入室しました」と他の人に伝える処理などに使える処理です。

続いて、クライアント側のコードを書いてみましょう。

クライアント側コード

冒頭で、Socket.IOにはクライアント側の処理を実現するためのJavaScript用ライブラリが含まれているという話をしました。
しかし、そのJavaScript用ライブラリは具体的にどこにあるのかについては触れていませんでした。

じつは、Socket.IOを使ってWebサーバーを立ち上げると、/socket.io/socket.io.jsというパスに、そのライブラリが自動的に生成されます。

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <style>
    #messageForm {
        margin-top: 15px;
    }
    </style>

    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
    <script type="text/javascript" src="/socket.io/socket.io.js"></script>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form>
                    <div class="form-group">
                        <input type="text" class="form-control" id="messageForm">
                    </div>
                    <div class="form-group">
                        <button type="button" class="btn btn-primary" id="sendMessageBtn">自分を含む全員に送信</button>
                        <button type="button" class="btn btn-primary" id="sendMessageBroadcastBtn">自分以外の全員に送信</button>
                    </div>
                </form>
                <div id="messageView"></div>
            </div>
        </div>
    </div>

    <script type="text/javascript">
    var ioSocket = io.connect( "http://localhost:3000" ); // チャットサーバーに接続

    // サーバーからのデータ受け取り処理
    ioSocket.on( "connect", function() {} ); // 接続
    ioSocket.on( "disconnect", function() {} ); // 切断

    // サーバーからクライアントへの送り返し
    ioSocket.on( "s2c_message", function( data ) { appendMessage( data.value ) });

    // 画面にメッセージを追記
    function appendMessage( text ) {
        $("#messageView").append( "<div>" + text + "</div>" );
    }

    // 自分を含む全員宛にメッセージを送信
    $("#sendMessageBtn").click( function() {

        // メッセージの内容を取得し、その後フォームをクリア
        var message = $("#messageForm").val();
        $("#messageForm").val("");

        // クライアントからサーバーへ送信
        ioSocket.emit( "c2s_message", { value : message } );
    });

    // 自分以外の全員宛にメッセージを送信
    $("#sendMessageBroadcastBtn").click( function() {

        // メッセージの内容を取得し、その後フォームをクリア
        var message = $("#messageForm").val();
        $("#messageForm").val("");

        // クライアントからサーバーへ送信
        ioSocket.emit( "c2s_broadcast", { value : message } );
    });
    </script>
</body>
</html>

※注意
上記のコードではXSS対策が施されていないので、JavaScriptのコードなどを送信すると、有効に機能してしまいます。実際に使用する際はセキュリティー対策を万全にしてください。

動作デモ

node chat_server.jsでサーバーを起動します。
すると、ローカルに置かれたindex.htmlが読み込まれ、クライアント画面が立ち上がります。
リアルタイムWebを目視で確認するため、同じサーバーに3つのウィンドウでそれぞれ接続してみましょう。

フォームに文字を入力し、「自分を含む全員に送信」ボタンをクリックすると……

3つすべてのウィンドウに、同じ文字が表示されました。

つづいて今度は「自分以外の全員に送信」ボタンをクリックすると……

今度は、送信者以外のウィンドウに文字が表示されました。

このようにして、細かい仕様に煩わされず、簡単にリアルタイムWebを実現することができます。
次回以降はこれを様々なものと組み合わせながら、もっと高度な領域にチャレンジしていきたいと思います。

Tech Blog

(編集部)

株式会社リクルートライフスタイルのTech Blog編集部です。いま流行りのTechネタやちょっと使えるTipsなどをお届けしていきます。

NEXT