はじめに
Part 1では、WebSocketの基本概念とEchoサーバーの実装を通じて、双方向通信の基礎を学んだ。
Part 2では、実践的なアプリケーションとして「リアルタイムチャット」を構築する。
Echoサーバーとチャットサーバーの違いは、「1対1」か「1対多」か という点となる。
今回は、接続している全クライアントにメッセージを配信する「ブロードキャスト」の仕組みと、ユーザー識別の実装方法について整理する。
環境
Part 1と同様の環境を使用する。
Node v14以上
ライブラリ ws
ブラウザ Chrome, Edge, Firefox等ブロードキャストの実装
Echoサーバーでは、メッセージを送ってきた ws (特定のクライアント) に対してのみ ws.send() を行っていた。
しかし、チャットアプリでは「Aさんの発言を、BさんやCさんにも届ける」必要がある。
これを ブロードキャスト と呼ぶ。
実装のポイント
ws ライブラリでは、wss.clients というプロパティに、現在接続中の全てのクライアント情報が格納されている。
これをループ処理することで、全員への送信が可能となる。
// 接続中の全クライアントに送信する関数
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});概念図: ブロードキャスト
Aさんが発言すると、サーバーはそれをコピーして、Aさん自身とBさんの両方に送信する。
ユーザー識別 (ID管理)
誰が発言したのかを区別するために、各クライアントにIDを割り当てる必要がある。
WebSocketの接続オブジェクト ws は、通常のJavaScriptオブジェクトと同様に、任意のプロパティを追加できる。
これを利用して、接続時にランダムなIDを付与する。
wss.on('connection', (ws) => {
// UUIDの生成
ws.userId = crypto.randomUUID();
console.log(`ユーザー ${ws.userId} が接続しました`);
});切断の検知
チャットアプリでは「誰かが退出した」ことを知ることも重要である。
WebSocketでは close イベントを監視することで、切断を検知できる。
ws.on('close', () => {
console.log(`ユーザー ${ws.userId} が切断しました`);
// 必要に応じて、他のユーザーに「退出通知」をブロードキャストする
});完成コード
これまでの要素を組み合わせた、チャットサーバーの完成コードは以下の通りである。
サーバー側 (server.js)
const WebSocket = require('ws');
const crypto = require('crypto'); // UUID生成用
const wss = new WebSocket.Server({ port: 8080 });
// 全員にメッセージを送る関数
function broadcast(message) {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
wss.on('connection', (ws) => {
// 1. IDの割り当て
ws.userId = crypto.randomUUID();
const joinMsg = `システム: ユーザー ${ws.userId} が入室しました`;
console.log(joinMsg);
broadcast(joinMsg);
// 2. メッセージ受信時の処理
ws.on('message', (message) => {
const msg = `ユーザー ${ws.userId}: ${message}`;
console.log(`受信: ${msg}`);
broadcast(msg);
});
// 3. 切断時の処理
ws.on('close', () => {
const leaveMsg = `システム: ユーザー ${ws.userId} が退出しました`;
console.log(leaveMsg);
broadcast(leaveMsg);
});
});
console.log('チャットサーバーが起動しました (ws://localhost:8080)');クライアント側
クライアント側は、HTMLファイル (index.html) と JavaScriptファイル (client.js) に分けて記述する。
UI部分 (index.html)
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Chat</title>
</head>
<body>
<h2>WebSocket Chat</h2>
<input type="text" id="messageInput" placeholder="メッセージを入力">
<button onclick="sendMessage()">送信</button>
<div id="chatLog"></div>
<script src="client.js"></script>
</body>
</html>ロジック部分 (client.js)
const socket = new WebSocket('ws://localhost:8080');
const chatLog = document.getElementById('chatLog');
// 接続完了
socket.onopen = () => {
addLog('システム: サーバーに接続しました');
};
// メッセージ受信
socket.onmessage = (event) => {
addLog(event.data);
};
// 切断
socket.onclose = () => {
addLog('システム: サーバーから切断されました');
};
function sendMessage() {
const input = document.getElementById('messageInput');
const message = input.value;
if (message) {
socket.send(message);
input.value = '';
}
}
function addLog(text) {
const div = document.createElement('div');
div.textContent = text;
chatLog.appendChild(div);
}参考
MDN Web Docs: WebSocket API
https://developer.mozilla.org/ja/docs/Web/API/WebSocketWebSocket入門 - Zenn記事
https://zenn.dev/nameless_sn/articles/websocket_tutorialCrypto: randomUUID() メソッド
https://developer.mozilla.org/ja/docs/Web/API/Crypto/randomUUID
おわりに
Part 1とPart 2を通じて、WebSocketを用いたリアルタイムアプリケーションの基礎を構築した。
意外とシンプルに実装できることが分かったので、これでWebSocketは怖くない!
この基礎があれば、特定のグループだけにメッセージを送る「ルーム機能」や、メッセージをデータベースに保存する「履歴機能」などもこの辺のソースを改造することで実装できるだろう。
余裕があれば、Part 3で 〇×ゲームを作りたい。