はじめる
このガイドでは、基本的なチャットアプリケーションを作成します。Node.JSやSocket.IOの事前知識はほとんど必要ありませんので、あらゆるレベルのユーザーにとって理想的です。
はじめに
LAMP (PHP)などの一般的なWebアプリケーションスタックを使用してチャットアプリケーションを作成することは、従来非常に困難でした。サーバーの変更をポーリングしたり、タイムスタンプを追跡したりする必要があり、本来あるべき速度よりもはるかに遅くなります。
ソケットは、従来から多くのリアルタイムチャットシステムのアーキテクチャの中心となるソリューションであり、クライアントとサーバー間の双方向通信チャネルを提供します。
これは、サーバーがメッセージをクライアントにプッシュできることを意味します。チャットメッセージを作成すると、サーバーがそれを取得し、接続されている他のすべてのクライアントにプッシュするというアイデアです。
Webフレームワーク
最初の目標は、フォームとメッセージのリストを提供するシンプルなHTMLウェブページをセットアップすることです。この目的のために、Node.JSのWebフレームワークである`express`を使用します。Node.JSがインストールされていることを確認してください。
まず、プロジェクトを記述する`package.json`マニフェストファイルを作成しましょう。専用の空ディレクトリに配置することをお勧めします(私の場合は`chat-example`と呼びます)。
{
"name": "socket-chat-example",
"version": "0.0.1",
"description": "my first socket.io app",
"dependencies": {}
}
"name"プロパティは一意である必要があります。「socket.io」や「express」のような値は使用できません。npmは依存関係のインストール時にエラーを報告します。
必要なものを`dependencies`プロパティに簡単に追加するには、`npm install`を使用します。
npm install express@4
インストールが完了したら、アプリケーションをセットアップする`index.js`ファイルを作成できます。
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
app.get('/', (req, res) => {
res.send('<h1>Hello world</h1>');
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
つまり、
- Expressは`app`を関数ハンドラーとして初期化し、HTTPサーバーに提供できます(4行目参照)。
- ウェブサイトのホームにアクセスしたときに呼び出されるルートハンドラー`/`を定義します。
- 3000番ポートでHTTPサーバーをリスンします。
`node index.js`を実行すると、次のようになります。

ブラウザで`https://:3000`にアクセスすると

HTMLの提供
これまでの`index.js`では、`res.send`を呼び出してHTML文字列を渡しています。アプリケーション全体のHTMLをここに配置するだけではコードが非常に分かりにくくなるため、代わりに`index.html`ファイルを作成して提供します。
`sendFile`を使うようにルートハンドラーをリファクタリングしましょう。
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
`index.html`ファイルに次のように記述します。
<!DOCTYPE html>
<html>
<head>
<title>Socket.IO chat</title>
<style>
body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
#form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
#input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
#input:focus { outline: none; }
#form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }
#messages { list-style-type: none; margin: 0; padding: 0; }
#messages > li { padding: 0.5rem 1rem; }
#messages > li:nth-child(odd) { background: #efefef; }
</style>
</head>
<body>
<ul id="messages"></ul>
<form id="form" action="">
<input id="input" autocomplete="off" /><button>Send</button>
</form>
</body>
</html>
(Control+Cを押して`node index.js`を再度実行することで)プロセスを再起動し、ページを更新すると、次のようになります。

Socket.IOの統合
Socket.IOは2つの部分で構成されています。
- Node.JS HTTPサーバーと統合(またはマウント)するサーバー socket.io
- ブラウザ側にロードされるクライアントライブラリ socket.io-client
開発中は、後ほど説明するように`socket.io`が自動的にクライアントを提供するため、現時点では1つのモジュールのみをインストールする必要があります。
npm install socket.io
これにより、モジュールがインストールされ、`package.json`に依存関係が追加されます。次に、`index.js`を編集して追加します。
const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
io.on('connection', (socket) => {
console.log('a user connected');
});
server.listen(3000, () => {
console.log('listening on *:3000');
});
`server`(HTTPサーバー)オブジェクトを渡すことで`socket.io`の新しいインスタンスを初期化します。次に、受信ソケットの`connection`イベントをリッスンし、それをコンソールにログ出力します。
次に、`index.html`の`</body>`(ボディタグの終了)の前に次のスニペットを追加します。
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
</script>
`socket.io-client`をロードするだけで済みます。これにより、グローバルな`io`(およびエンドポイント`GET /socket.io/socket.io.js`)が公開され、接続されます。
クライアント側のJSファイルのローカルバージョンを使用する場合は、`node_modules/socket.io/client-dist/socket.io.js`にあります。
ローカルファイルの代わりにCDNを使用することもできます(例:`<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>`)。
`io()`を呼び出す際にURLを指定していません。これは、ページを提供するホストへの接続を試行するようにデフォルト設定されているためです。
ApacheやNginxなどのリバースプロキシの背後にいる場合は、そのためのドキュメントをご覧ください。
ウェブサイトのルートではないフォルダ(例:`https://example.com/chatapp`)にアプリをホストしている場合、サーバーとクライアントの両方でpathを指定する必要があります。
プロセスを再起動し(Control+Cを押して`node index.js`を再度実行)、ウェブページを更新すると、コンソールに「ユーザーが接続しました」と表示されます。
複数のタブを開くと、複数のメッセージが表示されます。

各ソケットは、特別な`disconnect`イベントも発生させます。
io.on('connection', (socket) => {
console.log('a user connected');
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
タブを複数回更新すると、それが動作しているのがわかります。

イベントの送出
Socket.IOの中心的な考え方は、任意のイベントを、任意のデータとともに送受信できることです。JSONとしてエンコードできるオブジェクトであれば何でも使用でき、バイナリデータもサポートされています。
ユーザーがメッセージを入力すると、サーバーがそれを`chat message`イベントとして取得するようにしましょう。`index.html`の`script`セクションは、次のようにする必要があります。
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var form = document.getElementById('form');
var input = document.getElementById('input');
form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
</script>
`index.js`では、`chat message`イベントを出力します。
io.on('connection', (socket) => {
socket.on('chat message', (msg) => {
console.log('message: ' + msg);
});
});
結果は次の動画のようになります。
ブロードキャスト
次の目標は、サーバーから他のユーザーにイベントを送出することです。
すべてのユーザーにイベントを送信するには、Socket.IOは`io.emit()`メソッドを提供します。
io.emit('some event', { someProperty: 'some value', otherProperty: 'other value' }); // This will emit the event to all connected sockets
特定の送信ソケットを除くすべてのユーザーにメッセージを送信する場合は、そのソケットからの送信に`broadcast`フラグを使用します。
io.on('connection', (socket) => {
socket.broadcast.emit('hi');
});
ここでは、単純化のために、送信者を含むすべてのユーザーにメッセージを送信します。
io.on('connection', (socket) => {
socket.on('chat message', (msg) => {
io.emit('chat message', msg);
});
});
クライアント側では、`chat message`イベントをキャプチャするときに、それをページに含めます。クライアント側のJavaScriptコード全体は次のようになります。
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io();
var messages = document.getElementById('messages');
var form = document.getElementById('form');
var input = document.getElementById('input');
form.addEventListener('submit', function(e) {
e.preventDefault();
if (input.value) {
socket.emit('chat message', input.value);
input.value = '';
}
});
socket.on('chat message', function(msg) {
var item = document.createElement('li');
item.textContent = msg;
messages.appendChild(item);
window.scrollTo(0, document.body.scrollHeight);
});
</script>
これで、約20行のコードでチャットアプリケーションが完成しました!見た目は次のようになります。
宿題
アプリケーションを改善するためのアイデアをいくつか紹介します。
- 誰かが接続または切断したときに、接続しているユーザーにメッセージをブロードキャストします。
- ニックネームのサポートを追加します。
- 同じメッセージを送信者自身に送信しません。代わりに、Enterキーを押すとすぐにメッセージを追加します。
- 「{ユーザー}が入力しています」機能を追加します。
- オンラインユーザーを表示します。
- プライベートメッセージングを追加します。
- 改善点を共有してください!
このサンプルの入手
GitHubでこちらから入手できます。
git clone https://github.com/socketio/chat-example.git