次に、個別送信について説明します。Socket.IOでアクセスしたクライアントには必ず一意のIDが割り当てられます。このIDを利用することで、特定のクライアントに対してのみサーバからデータを送信できます。個別送信では、「io.to(ID).emit(送信イベント名, 送信データ)」を使用します。
今度は、入室時のユーザー名を、入力したユーザーだけに表示する処理を実装します。
<!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 for="msgForm">名前:</label>
<input type="text" class="form-control" id="msgForm">
</div>
<button type="submit" class="btn btn-primary" id="sendButton">入室</button>
</form>
<div id="chatLogs"></div>
</div>
<script type="text/javascript">
var socket = io.connect(); // C02. ソケットへの接続
var isEnter = false;
var name = '';
// C04. server_to_clientイベント・データを受信する
socket.on("server_to_client", function(data){appendMsg(data.value)});
function appendMsg(text) {
$("#chatLogs").append("<div>" + text + "</div>");
}
$("form").submit(function(e){
var message = $("#msgForm").val();
$("#msgForm").val('');
if (isEnter) {
message = "[" + name + "]: " + message;
// C03. client_to_serverイベント・データを送信する
socket.emit("client_to_server", {value : message});
} else {
name = message;
var entryMessage = name + "さんが入室しました。";
// C05. client_to_server_broadcastイベント・データを送信する
socket.emit("client_to_server_broadcast", {value : entryMessage});
// C06. client_to_server_personalイベント・データを送信する
socket.emit("client_to_server_personal", {value : name});
changeLabel();
}
e.preventDefault();
});
function changeLabel() {
$("label").text("メッセージ:");
$("button").text("送信");
isEnter = true;
}
</script>
</body>
</html>
名前欄に入力された値をサーバに送信する処理をclient_to_server_personalイベントとして追加しています。
// 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イベント・データを受信する
io.sockets.on('connection', function(socket) {
var name;
// S05. client_to_serverイベント・データを受信する
socket.on('client_to_server', function(data) {
// S06. server_to_clientイベント・データを送信する
io.sockets.emit('server_to_client', {value : data.value});
});
// S07. client_to_server_broadcastイベント・データを受信し、送信元以外に送信する
socket.on('client_to_server_broadcast', function(data) {
socket.broadcast.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 + "さんとして入室しました。"
io.to(id).emit('server_to_client', {value : personalMessage})
});
});
データを送信する前に、各クライアントに一意に割り当てられるIDを取得します。「socket.id」によって取得することが可能です。そのIDに対して、つまり特定のクライアントに対してのみデータを送信しています。
コードの変更・保存が完了したら、再度アプリケーションの動作を確認します。“node app.js”を実行し、複数のブラウザウィンドウでアプリケーションを開いてください。
先ほどと同様に、どれか1つのブラウザの名前欄に値を入力し、入室ボタンを押します。入力ウィンドウには、「あなたは、〜さんとして入室しました。」というメッセージが表示され、それ以外のウィンドウには「〜さんが入室しました。」というメッセージが表示されることが確認できると思います。
このように、socket.idを指定してデータを送信すると、特定のクライアントのみにデータを送信することができます。
io.to(id).emit('server_to_client', {value : personalMessage})
最後にクライアントとのコネクションが切断したときの処理を実装します。ブラウザウィンドウを閉じたユーザーがいたら、その他のユーザーに退出メッセージを表示する処理を追加します。
// 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イベント・データを受信する
io.sockets.on('connection', function(socket) {
var name;
// S05. client_to_serverイベント・データを受信する
socket.on('client_to_server', function(data) {
// S06. server_to_clientイベント・データを送信する
io.sockets.emit('server_to_client', {value : data.value});
});
// S07. client_to_server_broadcastイベント・データを受信し、送信元以外に送信する
socket.on('client_to_server_broadcast', function(data) {
socket.broadcast.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 + "さんとして入室しました。"
io.to(id).emit('server_to_client', {value : personalMessage});
});
// S09. disconnectイベントを受信し、退出メッセージを送信する
socket.on('disconnect', function() {
if (name == 'undefined') {
console.log("未入室のまま、どこかへ去っていきました。");
} else {
var endMessage = name + "さんが退出しました。"
io.sockets.emit('server_to_client', {value : endMessage});
}
});
});
クライアントサイドのindex.htmlへのコードは不要です。Socket.IOはクライアントが切断すると、自動的にdisconnectイベントを発生させます。つまり、コネクション切断時の処理はサーバサイドにてdisconnectイベント受信後のコールバック関数を定義するだけで実装できます。
socket.on('disconnect', function() {
// この中に切断時の処理を記述する
});
再度、同じ要領で動作確認を行います。複数のブラウザウィンドウを開き、1つのウィンドウを閉じてみてください。
「〜さんが退出しました。」というメッセージが残りの開かれているウィンドウに表示されたと思います。
また、入室しないままウィンドウを閉じると、以下のようにターミナル上にメッセージが表示されることも確認してみてください。
いかがでしたでしょうか。今回説明した内容を表形式でまとめます。
| 処理概要 | クライアントサイド | サーバサイド |
|---|---|---|
| WebSocket通信準備 | ・モジュール読み込み (/socket.io/socket.io.js) ・ソケット接続(io.connect) |
・モジュール読み込み(require) ・HTTPサーバ生成(createServer) ・ソケットのひも付け(listen) |
| データ送信 | emit:一斉送信 | |
| ― | broadcast:自分以外 | |
| ― | to(id):自分のみ | |
| データ受信 | on:イベント・データ受信 | |
| 切断時の処理 | ― | disconnectイベント定義 |
次回は、Socket.IOライブラリのうち、データ送信対象をグループ化できる「room」と、サーバサイドの機能を分割できる「namespace」に焦点を当てて解説します。
WebSocketが一番速いアプリケーションサーバはどれだ?
Play2+nginx/Akka/WebSocketで高速双方向通信
Socket.IOでセンサー&MongoDB〜AngularJSアプリ間の通信を行うCopyright © ITmedia, Inc. All Rights Reserved.