ペンギンが技術ブログを書いています

ゼロから始めるSocket.IO - 最小構成でリアルタイムチャットを作成する

初回投稿: 2021年05月26日 / 最終更新: 2021年06月09日

前書き

Socket.IOとは?

Socket.IO公式サイト


Socket.IOはブラウザとサーバー間を、双方向にリアルタイムの通信を可能にするライブラリです。


サーバー側からブラウザに通知することができる ので、リアルタイムでクライアント間が通信する チャットオンラインゲーム などが実現可能です。

Socket.IOを始める

簡易的なリアルタイムチャットを作成するまで解説します。このチャットの内容は以下の通りです。

  1. 自分および他のユーザーの入退室を通知する
  2. 文字を送信し、送信された表示する

忙しい方のためのソースコード

この記事で扱うプロジェクトのディレクトリ構成、ソースコードはこちらで確認できます。

事前準備

前述の通り、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を作成し、以下のコードをコピーします。

server.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')
})

HTTPサーバーを起動しクライアント側から接続されると、「入室しました」とログに出力するというところまで実装できています。


実際にこのサーバーを起動してみましょう。

$ node index.js

// 実行結果
listening on *:3001

3. クライアント側からサーバーに接続する

接続するためには、クライアント用のSocket.ioのライブラリが必要です。
今回はCDNでライブラリを読み込みます。

プロジェクトディレクトリにclient.htmlを作成して、サーバーに接続する処理まで記述します。

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を使用します。


ついでに退出した時の処理も記述します。

server.js
// 接続時の処理
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の学習を行ってください。

client.html(HTML部分)
<form id="form">
  <input id="chat-text" type="text">
  <input type="submit" value="チャット送信">
</form>
client.html(JavaScript部分)
  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

server.js
io.on('connection', (socket) => {
  // 送信されたイベントを受け取る
  socket.on('chat message', (chatText) => {
    console.log(chatText)
  })
})

これでクライアント側でチャットを送信したときに、その入力内容をサーバー側で出力できます。


試しにやってみましょう。次の画像のように、チャットを送信してログが出力されていれば成功です。

chat

6. チャット及び入室、退出の受信を実装する

client.html

チャット及び入室、退出の受信をしてHTMLに出力する処理を記述します。

client.html(HTML部分)
<ul id="logs"></ul>
client.html(JavaScript部分)
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()でログを出力していたところを、そのままブラウザで表示させるので以下のように書き換えます。

server.js
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にはまだまだいろいろな機能があります。