WebSocket クライアントアプリケーションを書く

メモ: この機能はウェブワーカー内で利用可能です。

WebSocket クライアントアプリケーションは WebSocket API を使用して、 WebSocket プロトコルを経由して WebSocket サーバーと通信します。

メモ: この記事のサンプルスニペットは WebSocket チャットクライアント/サーバーサンプルから取得したものです。 コードはこちらからご覧ください

WebSocket オブジェクトの作成

WebSocket プロトコルを使用して通信するには、 WebSocket オブジェクトを作成する必要があります。これにより自動的にサーバーへの接続が開かれます。

WebSocket のコンストラクターは、必須 1 つ、任意 1 つの引数を受け取ります。

js
webSocket = new WebSocket(url, protocols);
url

接続先 URL。これは、 WebSocket サーバーが応答する URL である必要があります。 これは URL スキームに wss:// を使用するべきですが、ソフトウェアによってはローカル接続用に安全ではない ws:// を使用することを許可していることがあります。 相対 URL 値と https:// および http:// スキームも、ほとんどのブラウザーの最新バージョンで許可されています。

protocols 省略可

単一のプロトコル文字列または一連のプロトコル文字列。 これらの文字列はサブプロトコルを示すのに使用されるため、単一のサーバーで複数の WebSocket サブプロトコルを実装できます(たとえば、特定の protocol に応じて 1 つのサーバーで異なる種類の対話を処理できるようにする)。 プロトコル文字列を指定しない場合、空文字列であると仮定されます。

コンストラクターは、接続先がアクセスを許可していない場合に SecurityError 例外を発生させます。 これは安全ではない接続を利用しようとしたときに発生することがあります(ほとんどのユーザーエージェントは、同じ機器か、可能であれば同じネットワークでない限り、すべての WebSocket 接続に安全なリンクを要求します)。

接続エラー

接続を試行中にエラーが発生した場合、最初の error イベントWebSocket オブジェクトに送信され(これにより、すべてのハンドラーが呼び出されます)、接続が閉じられた理由を示す close イベントが続きます。

ブラウザーは CloseEvent 経由で、コンソールにも RFC 6455 第 7.4 節で定義されている終了コードと同時に、もっと説明的なエラーメッセージを出力するができます。

この簡単な例では新しい WebSocket を作成し、 wss://www.example.com/socketserver のサーバーに接続します。 この例では、ソケットのリクエストで "protocolOne" のカスタムプロトコルが指定されていますが、省略することもできます。

js
const exampleSocket = new WebSocket(
  "wss://www.example.com/socketserver",
  "protocolOne",
);

返されると、 exampleSocket.readyStateCONNECTING です。 readyState は接続がデータを転送する準備ができたら OPENになります。

接続を開き、サポートしているプロトコルについて柔軟に対応したい場合は、プロトコルの配列を指定することができます。

js
const exampleSocket = new WebSocket("wss://www.example.com/socketserver", [
  "protocolOne",
  "protocolTwo",
]);

接続が確立されると(つまり readyStateOPEN)、 exampleSocket.protocol は、サーバーが選択したプロトコルを通知します。

WebSocket を確立するには、 HTTP アップグレードメカニズムが必要です。したがって、 HTTP サーバーを ws://www.example.com または wss://www.example.com としてアドレス指定すると、プロトコルのアップグレードのためのリクエストが暗黙的に行われます。

サーバーへのデータの送信

接続を開くと、サーバーにデータを送信することができます。 これを行うには、送信するメッセージごとに WebSocket オブジェクトの send() メソッドを呼び出します。

js
exampleSocket.send("Here's some text that the server is urgently awaiting!");

データは文字列、 BlobArrayBuffer のいずれかで送信することができます。

接続の確立は非同期であり、失敗しやすいため、 send() メソッドの呼び出しが WebSocket オブジェクトの作成直後に成功するという保証はありません。 データの送信を試みるのは、少なくともいったん接続が確立してからでなければならないので、作業を行うための onopen イベントハンドラーを定義してその中で行います。

js
exampleSocket.onopen = (event) => {
  exampleSocket.send("Here's some text that the server is urgently awaiting!");
};

JSON を使用したオブジェクトの送信

サーバーに複雑なデータを合理的に送信するのに手軽な方法の一つとして、JSON を使用する方法があります。 たとえば、チャットプログラムがサーバーとやり取りするのに、 JSON でカプセル化されたデータのパケットを使用して実装されたプロトコルを使用することができます。

js
// Send text to all users through the server
function sendText() {
  // Construct a msg object containing the data the server needs to process the message from the chat client.
  const msg = {
    type: "message",
    text: document.getElementById("text").value,
    id: clientID,
    date: Date.now(),
  };

  // Send the msg object as a JSON-formatted string.
  exampleSocket.send(JSON.stringify(msg));

  // Blank the text input element, ready to receive the next line of text from the user.
  document.getElementById("text").value = "";
}

サーバーからのメッセージの受信

WebSockets はイベント駆動型 API です。メッセージを受信すると、 message イベント WebSocket オブジェクトに送信されます。これを処理するには、 message イベントのイベントリスナーを追加するか、 onmessage イベントハンドラーを使用するかします。受信データの待ち受けを開始するには、次のようにします。

js
exampleSocket.onmessage = (event) => {
  console.log(event.data);
};

JSON オブジェクトの受信と解釈

まずJSON を使用したオブジェクトの送信で述べられているチャットクライアントアプリケーションを考えてみましょう。クライアントが受信するデータパケットの種類は次のとおりです。

  • ログインハンドシェイク
  • メッセージテキスト
  • ユーザーリストの更新

これらの受信メッセージを解釈するコードは、次のようになります。

js
exampleSocket.onmessage = (event) => {
  const f = document.getElementById("chatbox").contentDocument;
  let text = "";
  const msg = JSON.parse(event.data);
  const time = new Date(msg.date);
  const timeStr = time.toLocaleTimeString();

  switch (msg.type) {
    case "id":
      clientID = msg.id;
      setUsername();
      break;
    case "username":
      text = `User <em>${msg.name}</em> signed in at ${timeStr}<br>`;
      break;
    case "message":
      text = `(${timeStr}) ${msg.name} : ${msg.text} <br>`;
      break;
    case "rejectusername":
      text = `Your username has been set to <em>${msg.name}</em> because the name you chose is in use.<br>`;
      break;
    case "userlist":
      document.getElementById("userlistbox").innerText = msg.users.join("\n");
      break;
  }

  if (text.length) {
    f.write(text);
    document.getElementById("chatbox").contentWindow.scrollByPages(1);
  }
};

ここで JSON.parse() を使用して JSON オブジェクトを元のオブジェクトに変換し、その内容を調べて処理します。

テキストデータ形式

WebSocket 接続を介して受信されるテキストは、 UTF-8 形式です。

接続を閉じる

WebSocket 接続の使用を終了したら、 WebSocket のメソッド close() を呼び出します。

js
exampleSocket.close();

接続を閉じようとする前に、ソケットの bufferedAmount 属性を確認して、データがネットワーク上でまだ送信されていないかどうかを判断すると有用かもしれません。 この値が 0 ではない場合、まだ待ち状態のデータがあるので、接続を閉じる前に待ったほうが良いかもしれません。

セキュリティの考慮事項

混合コンテンツ環境では WebSocket を使用しないでください。つまり、 HTTPS を使用もしくはそうでない方法でもロードされたページから、セキュアでない WebSocket 接続を開くべきではありません。 最近のブラウザーは安全な WebSocket 接続のみを許可し、また安全ではないコンテキストでの使用には対応しなくなってきています。