WebSocket入門 〜チャットアプリも作れるリアルタイム双方向通信を学ぼう〜

公開日:2022-07-31
WebSocket
JavaScript

https://www.youtube.com/watch?v=9D-SD77xeCI

はじめに

リアルタイムの双方向の通信技術であるWebSocketについて説明します。内容は次の通りです。

WebSocketとは

WebSocketとは、Webにおいて双方向通信を低コストで行うためのプロトコルです。WebSocketの必要性について考える前に、HTTPプロトコルについて復習しましょう。

HTTPプロトコル

HTTPプロトコルでは、まずクライアント側からサーバー側へリクエストを送り、そのリクエストに応じてサーバー側がレスポンスを返します。この方法の問題点は、リクエストはクライアント側からしか送ることができないことです。

例えば、天気情報を提供するサーバーへ1分間隔でリクエストを送れば最新の天気情報のレスポンスを得ることができます。天気は1分以内の変化はそれほど激しくないためこれで十分ですが、株の相場情報のようにリアルタイム性を求める場合はどうでしょうか。

高頻度で変化する相場情報を提供するサーバーへ1分間隔でリクエストを送りレスポンスを受け取っていては、途中の値動きを見逃す可能性があります。さらにリクエストの頻度を上げることもできますが、サーバー側の負荷が大きくなります。このような、相場やオンライン対戦ゲームやビデオ会議チャットアプリといったリアルタイム性が求められる場面では、変化があるたびにサーバー側からクライアント側へデータを送れると便利ですね。そこで生まれたのがWebSocketプロトコルです。

WebSocketとHTTP通信のデモ

HTTP通信とWebSocket通信の違いを具体的なデモを通して比較してみましょう。ここでは、仮想通貨の取引所であるGMOコインが提供するAPIを用います。APIドキュメントをご覧ください。

HTTP通信のデモ

まずはHTTP通信から見ていきましょう。先ほどのドキュメントのPublic API内にある「最新レート」を見ましょう。右側でNode.jsのサンプルコードを見ることができます。

gmo-http.js
var request = require('request');

var endPoint = 'https://api.coin.z.com/public';
var path     = '/v1/ticker?symbol=BTC';

request(endPoint + path, function (err, response, payload) {
    console.log(JSON.stringify(JSON.parse(payload), null, 2));
});

最新レート – APIドキュメント| GMOコイン

ここでは、最初にrequestモジュールを使用しています。そのためnpm installコマンドでrequestモジュールをインストールする必要があります。

npm install request

次に、APIのエンドポイントとパスを変数に代入します。最後にrequestを呼び出し、コールバック関数のpayload引数で結果を受け取ります。

それでは実行してみましょう。ファイルをgmo-http.jsという名前で保存しnode gmo-http.jsとターミナルで実行してみます。取引所のサーバーへリクエストを送信することで、現在のレートが返されます。

HTTP通信では1回リクエストを送ると1回結果が返ってくることがわかります。

WebSocket通信のデモ

同様に、WebSocket通信を見ていきましょう。APIドキュメントのPublic WebSocket API内にある「最新レート」を見ましょう。右側でNode.jsのサンプルコードを見ることができます。

gmo-websocket.js
const WebSocket = require("ws");
const ws = new WebSocket("wss://api.coin.z.com/ws/public/v1");

ws.on("open", () => {
    const message = JSON.stringify(
    {
        "command": "subscribe",
        "channel": "ticker",
        "symbol": "BTC"
    });
    ws.send(message);
});

ws.on("message", (data) => {
    console.log("WebSocket message: ", data);
});

最新レート – APIドキュメント| GMOコイン

こちらでは、wsモジュールを使用しています。HTTP通信の時と同様にnpm installコマンドを使用すれば良いですね。2行目では接続先のURLを引数に渡してインスタンス化しています。

サーバーと接続できたら、必要な情報をmessage変数に格納します。これを取引所のサーバーへ送信します。

最後にサーバーからデータを受け取ったらコンソールへ表示します。ただし、このまま出力するとバッファデータしか表示されないため、data.toString()として文字列に変換して出力すると良いでしょう。

それでは実行してみましょう。ファイルをgmo-websocket.jsという名前で保存しnode gmo-websocket.jsとターミナルで実行してみます。レートが変わるタイミングでサーバー側から最新レートが送られていることが確認できます。

WebSocket通信ではサーバー側からクライアント側へ繰り返しデータを送ることができるというのが分かります。

WebSocketで作るチャットアプリ

WebSocketを用いたグループチャットアプリの実装を考えてみましょう。実装全体ではなく通信に関わる部分を説明します。動作の様子は6分17秒あたりで解説しています。

クライアント側の実装

クライアント側ではSocket.IOというライブラリを用います。

https://socket.io/https://socket.io/

まずは<script>タグでライブラリをロードします。

index.html
<script src="/socket.io/socket.io.js"></script>

次のようにして、インスタンス化します。

index.html
const sockcet = io();

チャットの送信時の処理を見てみましょう。emitメソッドで送信します。第1引数にはイベント名を、第2引数にはnamemesageを含む送信したいデータを指定します。イベント名は自由に指定できますが、サーバー側で受け取る処理を記述する際にイベント名を一致させる必要があります。

index.html
function postMsg(name, message) {
  const data = {
    name,
    msg,
  };
  socket.emit("msgPost", data)
}

次に、サーバーから受信する際の処理を見てみましょう。onメソッドの第1引数にイベント名を、第2引数にはデータを受け取るコールバック関数を指定します。このイベント名も、送信側のサーバー側で指定するものと一致している必要があります。

index.html
socket.on("msgGet", (data) => {
  // 画面に表示する処理
})

サーバー側の実装

サーバー用のSocket.ioライブラリを読み込み、インスタンス化します。

server.js
import { Server } from 'socket.io';
const io = new Server(server);

新しいクライアントが接続した際、最初に発生するのがconnectionイベントです。コールバック関数が受け取るsocketは個々のクライアントです。

server.js
io.on("connection", (socket) => {
  // 各クライアントに対する処理
})

さて、各クライアントからデータが送られてくるので、socket.onメソッドでイベントをリッスンします。ここでイベント名msgPostは先ほどクライアント側で指定した名前と対応しています。送られてきたデータをコールバック関数のdata引数で受け取ります。

最後にio.emitメソッドによって全てのクライアントにメッセージを送信します。msgGetというイベント名もクライアント側で指定したイベント名と一致するようにします。

server.js
  socket.on("msgPost", (data) => {
    const msgs = {
      name: data.name,
      msg: data.msg,
    }
    io.emit("msgGet", msgs);
  })

これによって、特定のユーザーが送信したメッセージを他のユーザーも受信できるようになり、グループチャット機能が実現できます。

以上がsocket.ioを用いたチャットアプリの実装でした。

まとめ

今回は今回紹介した取引所のAPIやチャットアプリの実装をみながらWebSocketについて学習しました。
1回のリクエストに対し1回のレスポンスを行うHTTP通信に対し、WebSocketは一度接続すると繰り返しデータを送受信できました。リアルタイム性が求められる場面でぜひ使ってみてください。