WebSocket の概要 - ウェブへのソケットの導入

問題: 低レイテンシのクライアント サーバーおよびサーバー クライアント接続

ウェブは主に、いわゆる HTTP のリクエスト/レスポンス パラダイムを中心に構築されています。クライアントがウェブページを読み込み、ユーザーが次のページをクリックするまで何も起こりません。2005 年頃、AJAX はウェブをよりダイナミックなものにし始めました。それでも、すべての HTTP 通信はクライアントによってステアリングされるため、サーバーから新しいデータを読み込むには、ユーザーの操作や定期的なポーリングが必要でした。

新しいデータが利用可能になったことをサーバーが認識した瞬間に、サーバーがクライアントにデータを送信できるようにするテクノロジーは、かなり以前から存在しています。「プッシュ」や「Comet」などの名前で呼ばれています。サーバーが接続しているように見せかける最も一般的なハッキングの 1 つに、ロング ポーリングがあります。ロング ポーリングでは、クライアントがサーバーへの HTTP 接続を開き、レスポンスが送信されるまで開いたままにします。サーバーは実際に新しいデータがあるたびにレスポンスを送信します(FlashXHR マルチパート リクエスト、htmlfiles などを使用する手法もあります)。 ロング ポーリングやその他の手法はうまく機能します。Gmail チャットなどのアプリケーションで毎日使用します。

ただし、これらすべての回避策には 1 つの問題があります。それは、HTTP のオーバーヘッドがあるためであり、低レイテンシのアプリケーションには適していません。たとえば、ブラウザで利用するマルチプレーヤー型ファースト パーソン シューティング ゲームや、リアルタイム コンポーネントを使用するその他のオンライン ゲームなどです。

WebSocket の概要: ウェブへのソケットの導入

WebSocket 仕様は、ウェブブラウザとサーバー間の「ソケット」接続を確立する API を定義します。つまり、クライアントとサーバーの間に永続的な接続があり、両者はいつでもデータの送信を開始できます。

はじめに

WebSocket 接続は、WebSocket コンストラクタを呼び出すだけで開始できます。

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

ws: に注目してください。これは、WebSocket 接続用の新しい URL スキーマです。https: が安全な HTTP 接続に使用されるのと同様に、安全な WebSocket 接続用の wss: もあります。

いくつかのイベント ハンドラを接続に即座にアタッチすると、接続が開かれたとき、着信メッセージを受信したとき、エラーが起きたときを知ることができます。

2 番目の引数には、オプションのサブプロトコルを指定できます。文字列または文字列の配列です。各文字列はサブプロトコル名を表す必要があり、サーバーは配列で渡されたサブプロトコルのうちの 1 つのみを受け入れます。受け入れられるサブプロトコルは、WebSocket オブジェクトの protocol プロパティにアクセスすることで確認できます。

サブプロトコル名は、IANA レジストリに登録されているサブプロトコル名のいずれかである必要があります。2012 年 2 月現在、登録されているサブプロトコル名(soap)は 1 つのみです。

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

サーバーとの通信

サーバーへの接続を確立したら(open イベントが発生したとき)、接続オブジェクトで send('your message') メソッドを使用して、サーバーへのデータ送信を開始できます。以前は文字列のみをサポートしていましたが、最新の仕様ではバイナリ メッセージを送信できるようになりました。バイナリデータを送信するには、Blob または ArrayBuffer オブジェクトを使用します。

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

同様に、サーバーはいつでもメッセージを送信できます。この場合、onmessage コールバックが呼び出されます。コールバックはイベント オブジェクトを受け取ります。実際のメッセージには、data プロパティを介してアクセスできます。

WebSocket は、最新の仕様のバイナリ メッセージを受信することもできます。バイナリ フレームは Blob 形式または ArrayBuffer 形式で受信できます。受信バイナリの形式を指定するには、WebSocket オブジェクトの binaryType プロパティを「blob」または「arraybuffer」に設定します。デフォルトの形式は「blob」です。(送信時に binaryType パラメータを揃える必要はありません)。

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

WebSocket のもう 1 つの新機能は拡張機能です。拡張機能を使用すると、圧縮多重化などのフレームを送信できるようになります。オープン イベント後に WebSocket オブジェクトの extensions プロパティを調べることで、サーバーで承認された拡張機能を確認できます。2012 年 2 月時点で、公式に公開されている拡張機能の仕様はありません。

// Determining accepted extensions
console.log(connection.extensions);

クロスオリジン通信

最新のプロトコルであるクロスオリジン通信は、WebSocket に直接組み込まれています。それでも、信頼できるクライアントとサーバーのみと通信するように注意する必要がありますが、WebSocket を使用すると、あらゆるドメインのパーティ間で通信できるようになります。サーバーは、サービスをすべてのクライアントで利用可能にするか、明確に定義された一連のドメインに存在するクライアントのみで利用可能にするかを決定します。

プロキシ サーバー

新しいテクノロジーには、必ず新しい問題が伴います。WebSocket の場合、ほとんどの企業ネットワークで HTTP 接続を仲介するプロキシ サーバーとの互換性があります。WebSocket プロトコルは、HTTP アップグレード システム(通常は HTTP/SSL に使用されます)を使用して、HTTP 接続を WebSocket 接続に「アップグレード」します。一部のプロキシ サーバーはこれを好まず、接続を切断します。そのため、特定のクライアントが WebSocket プロトコルを使用しても、接続を確立できないことがあります。これにより、次のセクションがさらに重要になります。

今すぐ WebSocket を使用する

WebSocket はまだ新しいテクノロジーであり、すべてのブラウザに完全に実装されているわけではありません。ただし、WebSocket が利用できない場合は常に、上記のフォールバックのいずれかを使用するライブラリでは、現在 WebSocket を使用できます。この分野で人気を集めているライブラリは socket.io です。このプロトコルのクライアントとサーバー実装が付属し、フォールバックが含まれています(2012 年 2 月現在、socket.io はバイナリ メッセージングをまだサポートしていません)。また、PusherApp などの商用ソリューションもあります。これは、WebSocket メッセージをクライアントに送信するための HTTP API を提供することで、あらゆるウェブ環境に簡単に統合できます。純粋な WebSocket に比べて、HTTP リクエストが増えるため、常に追加のオーバーヘッドが発生します。

サーバーサイド

WebSocket を使用すると、サーバーサイド アプリケーションにまったく新しい使用パターンが生まれます。LAMP などの従来のサーバー スタックは、HTTP リクエスト/レスポンス サイクルを中心に設計されていますが、多くの場合、大量のオープン WebSocket 接続にはうまく対応できません。多数の接続を同時に開いたままにしておくには、低いパフォーマンス コストで高い同時実行数を受け取るアーキテクチャが必要です。このようなアーキテクチャは通常、スレッド化またはいわゆる非ブロック IO を中心に設計されています。

サーバーサイドの実装

プロトコルのバージョン

WebSocket のワイヤ プロトコル(クライアントとサーバー間の handshake およびデータ転送)が RFC6455 になりました。最新の Chrome と Chrome for Android は、バイナリ メッセージングを含む RFC6455 に完全に対応しています。また、Firefox はバージョン 11、Internet Explorer はバージョン 10 で動作します。古いバージョンのプロトコルは引き続き使用できますが、脆弱性があることがわかっているため、推奨されません。古いバージョンの WebSocket プロトコルのサーバー実装がある場合は、最新バージョンにアップグレードすることをおすすめします。

使用例

クライアントとサーバーの間で真に低レイテンシのほぼリアルタイムの接続が必要な場合は、WebSocket を使用してください。その際には、イベントキューなどのテクノロジーに重点を置いて、サーバーサイド アプリケーションの構築方法を見直す必要があるかもしれません。ユースケースの例:

  • マルチプレーヤー型オンライン ゲーム
  • チャット アプリケーション
  • ライブ スポーツ ティッカー
  • ソーシャル ストリームのリアルタイム更新

デモ

参照