ゼロから始めるSocket.IO - 最小構成でリアルタイムチャットを作成する
前書き
Socket.IOとは?
Socket.IOはブラウザとサーバー間を、双方向にリアルタイムの通信を可能にするライブラリです。
サーバー側からブラウザに通知することができる ので、リアルタイムでクライアント間が通信する チャット や オンラインゲーム などが実現可能です。
Socket.IOを始める
簡易的なリアルタイムチャットを作成するまで解説します。このチャットの内容は以下の通りです。
- 自分および他のユーザーの入退室を通知する
- 文字を送信し、送信された表示する
忙しい方のためのソースコード
この記事で扱うプロジェクトのディレクトリ構成、ソースコードはこちらで確認できます。
事前準備
前述の通り、Socket.IOはNode.jsで動作するのでNode.jsのインストールが必要です。
まだインストールしていない場合は、こちらの記事を参考してインストールを行ってください。
1. プロジェクトの作成 ~ Socket.IOのインストール
// プロジェクトディレクトリを作成し移動
$ mkdir c:\socketio-lesson
$ cd c:\socketio-lesson
// プロジェクトの初期化(今回は全てEnterで設定)
$ npm init
// Socket.IOのインストール
$ npm install socket.io --save
2. サーバーを起動する
CORS
プロジェクトディレクトリにindex.js
を作成し、以下のコードをコピーします。
// httpは、Node.jsの標準のモジュールなのでインストール不要
const httpServer = require('http').createServer()
const io = require('socket.io')(httpServer, {
cors:{} // CORSを有効にする
})
// 接続時に実行される
io.on('connection', (socket) => {
console.log('入室しました')
})
// httpServerの起動
httpServer.listen(3001, () => {
console.log('listening on *:3001')
})
CORSの設定の詳細を知りたい場合は、Handling CORS を参照してください。
HTTPサーバーを起動しクライアント側から接続されると、「入室しました」とログに出力するというところまで実装できています。
実際にこのサーバーを起動してみましょう。
$ node index.js
// 実行結果
listening on *:3001
3. クライアント側からサーバーに接続する
接続するためには、クライアント用のSocket.ioのライブラリが必要です。
今回はCDNでライブラリを読み込みます。
プロジェクトディレクトリにclient.html
を作成して、サーバーに接続する処理まで記述します。
<!DOCTYPE html>
<html>
<head>
<title>ゼロから始めるSocket.IO</title>
<!-- クライアント用のSocket.IOを読み込む -->
<script src="https://cdn.socket.io/4.1.1/socket.io.min.js" integrity="sha384-cdrFIqe3RasCMNE0jeFG9xJHog/tgOVC1E9Lzve8LQN1g5WUHo0Kvk1mawWjxX7a" crossorigin="anonymous"></script>
</head>
<body></body>
<script>
const socket = io('http://localhost:3001') // サーバーに接続
</script>
</html>
このHTMLをブラウザで開くと、先ほどの起動したサーバーに接続します。
サーバー側のconnection
の部分が実行されるので、ログが出力されます。
// 出力結果
入室しました
4. 入室および退室を監視する
今のままだと誰が入室したか分かりません。とりえあずSocket.IOで接続時に自動的に振られるIDを使用します。
ついでに退出した時の処理も記述します。
// 接続時の処理
io.on('connection', (socket) => {
console.log(`${socket.id}さんが入室しました`)
// 切断時の処理
socket.on('disconnect', () => {
console.log(`${socket.id}さんが退出しました`)
})
})
サーバーを再起動して、試してみましょう。
ブラウザで2つのタブからHTMLを実行して、片方を閉じてみてください。
// server.jsのログ
6IT9pL2PYwM9MJpWAAABさんが入室しました
VW6wK84nTX37IJ_2AAADさんが入室しました
6IT9pL2PYwM9MJpWAAABさんが退出しました
このように表示されていれば成功です。
5. チャットの送信を実装する
client.html
チャットを入力し送信するフォームをHTML側で作成します。
HTMLの説明は省略します。もし分からなければ、HTMLの学習を行ってください。
<form id="form">
<input id="chat-text" type="text">
<input type="submit" value="チャット送信">
</form>
const socket = io('http://localhost:3001')
const form = document.getElementById('form')
const input = document.getElementById('chat-text')
form.addEventListener('submit', e => {
e.preventDefault()
if (input.value) {
// server.jsのchat messageを実行する
socket.emit('chat message', input.value)
input.value = ''
}
})
socket.emit('chat message', input.value)
でサーバーに入力した内容を送信します。
server.js
io.on('connection', (socket) => {
// 送信されたイベントを受け取る
socket.on('chat message', (chatText) => {
console.log(chatText)
})
})
これでクライアント側でチャットを送信したときに、その入力内容をサーバー側で出力できます。
試しにやってみましょう。次の画像のように、チャットを送信してログが出力されていれば成功です。
6. チャット及び入室、退出の受信を実装する
client.html
チャット及び入室、退出の受信をしてHTMLに出力する処理を記述します。
<ul id="logs"></ul>
const logs = document.getElementById('logs')
// chat logイベントを受け取ります
socket.on('chat log', (log) => {
let item = document.createElement('li')
item.textContent = log
logs.appendChild(item)
})
server.js
console.log()
でログを出力していたところを、そのままブラウザで表示させるので以下のように書き換えます。
io.on('connection', (socket) => {
io.emit('chat log', `${socket.id}さんが入室しました`)
socket.on('chat message', (chatText) => {
io.emit('chat log', chatText)
})
socket.on('disconnect', () => {
io.emit('chat log', `${socket.id}さんが退出しました`)
})
})
io.emit('chat log')
でブラウザのイベントを発火します。同時にパラメータによってログ内容も渡します。これで、チャット送信、入室、退出の3つのログが出力できます。
それでは動作確認をしてみましょう。下の動画と同じ動きをすれば成功です。
ディレクトリ構成とソースコードなど
今回の記事で作成したプロジェクトの全貌です。
ディレクトリ構成
socketio-lesson
├── node_modules
├── package.json
├── server.js
└── client.html
client.html
<!DOCTYPE html>
<html>
<head>
<title>ゼロから始めるSocket.IO</title>
<script src="https://cdn.socket.io/4.1.1/socket.io.min.js" integrity="sha384-cdrFIqe3RasCMNE0jeFG9xJHog/tgOVC1E9Lzve8LQN1g5WUHo0Kvk1mawWjxX7a" crossorigin="anonymous"></script>
</head>
<body>
<!-- チャット送信フォーム-->
<form id="form">
<input id="chat-text" type="text">
<input type="submit" value="チャット送信">
</form>
<!-- ログ -->
<ul id="logs"></ul>
</body>
<script>
// サーバーに接続
const socket = io('http://localhost:3001')
const form = document.getElementById('form')
const input = document.getElementById('chat-text')
// チャットの送信
form.addEventListener('submit', e => {
e.preventDefault()
if (input.value) {
socket.emit('chat message', input.value)
input.value = ''
}
})
// チャット、入室、退出の受信
const logs = document.getElementById('logs')
socket.on('chat log', (log) => {
let item = document.createElement('li')
item.textContent = log
logs.appendChild(item)
})
</script>
</html>
server.js
const httpServer = require('http').createServer()
const io = require('socket.io')(httpServer, {
cors:{}
})
io.on('connection', (socket) => {
io.emit('chat log', `${socket.id}さんが入室しました`)
socket.on('chat message', (chatText) => {
io.emit('chat log', chatText)
})
socket.on('disconnect', () => {
io.emit('chat log', `${socket.id}さんが退出しました`)
})
})
httpServer.listen(3001, () => {
console.log('listening on *:3001')
})
コマンド
$ cd c:\socketio-lesson
$ node server.js
まとめ
作成したリアルタイムチャットには、ユーザー名を付けたり、ルーム機能を作ったりと改善の余地はたくさんあるので、挑戦してみると良いかもしれません。
今回は最小構成でリアルタイムチャットを実現する内容でしたが、Socket.IOにはまだまだいろいろな機能があります。