Home » Web » WebSocketサーバを作る(2)

WebSocketサーバを作る(2)

ブラウザからの入力文字列を受信する

その(1)で作ったサーバは、ハンドシェイクが終わった後、接続を維持します。
ブラウザ側もエラーや終了以外でサーバとの接続を切りません。
お互いに、どちらが先にでも、自由にデータを送り合える状態となったわけです。

それでは、本当にちゃんとお互いにデータがやり取りできるのか、確認してみます。
実験として、先ほどのサーバにエコー機能をつけてみましょう。
エコーとは、こだまのことで、キーボードで打ったものがそのまま返ってきて画面に反映される仕組みのことです。

ちょっと待てよ、それじゃあブラウザからデータを送信して、サーバから応答をもらうHTTP通信と、やってることが同じじゃないかと思うかも知れません。
まあその通りなんですが、今回は、WebSocketの通信経路を使うことに意味があるので、つまりエコーができれば、その延長線上でプッシュもできるってことで、理解してください。

まずはブラウザからデータを送る仕組みですが、ブラウザにはWebSocketを利用する仕組みがすでに実装されていますから、サーバほど大変ではありません。
今回の実験用に以下のようなHTMLファイルを使います。

テキストボックスに文字を入力して、「入力」ボタンを押すと、ws.send()が実行されて、サーバへ文字列が送信されます。
この文字列データはフレームヘッダが付加されて、以下のような形でサーバに送られます。

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-------+-+-------------+-------------------------------+
 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
 | |1|2|3|       |K|             |                               |
 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
 |     Extended payload length continued, if payload len == 127  |
 + - - - - - - - - - - - - - - - +-------------------------------+
 |                               |Masking-key, if MASK set to 1  |
 +-------------------------------+-------------------------------+
 | Masking-key (continued)       |          Payload Data         |
 +-------------------------------- - - - - - - - - - - - - - - - +
 :                     Payload Data continued ...                :
 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
 |                     Payload Data continued ...                |
 +---------------------------------------------------------------+

ブラウザから入力された文字列を取り出すには、このフレームを分解していかなくてはいけません。

各フレームにどんなデータが入っているのか、確認してみましょう。

まず先頭bitにFIN、予約1~3の4bitが取られています。
ここは通常、FINのみ1、他は0となっていて、特に予約bitに1を立てるのはNGのようです。
少なくとも受信時にここを見る必要はありませんので、無視します。

次のopcodeは、送信されたデータの種類です。
データの種類には、textやbinaryといったものがあります。
今回はテキストボックスに入力された文字列を送っているので、opcodeはtextを表す’1’が入っています。

maskは、送信データがマスキング(暗号化)されているかどうかを示すフラグです。
ブラウザからの送信データは暗号化されるので、ここは’1’が入っています。

payload_lenは、送信データ本体(入力文字列)の長さです。
7bitしかないので、127までしか表現できませんが、では127byteまでのデータ長しか送れないのかと言うと、そうではありません。
送信データが125byte以下の場合は、ここの値がそのまま送信データの長さになりますが、送信データが65535を超える場合、送信データサイズを表すデータ領域は、拡張された64bit分の”Extended payload length”を使用するようになります。
この場合、payload_len自体の値は127になります。

また、送信データが65535以下で、125を超える場合は、やはり拡張領域の”Extended payload length”を使用しますが、その大きさは16bitにとどまります。
この時のpayload_len自体の値は、126です。

少なくとも想定上は、最大では64bitの整数値で表現できるだけのデータ長を扱うつもりのようです。
ちなみに64bitの整数値とは、16進数では 7FFF FFFF FFFF FFFFとなり、10進数だと、最大で8,191ペタバイトとなります。
こう書くと、やりすぎだろうとも思うのですが、そうは言っても32bitではたかだか2GBが限界ですし、64bitは時代の流れの上では必然なのかも知れませんね。

データ長領域が終わると、maskキーが4byte格納されている領域があります。

maskキーとは、送信データを暗号化するために使用されるビット列で、maskフラグが’1’の場合のみ存在します。
これはいわゆるバーナム暗号と思われ、暗号化された送信データは、このmaskキーとXORを取ることで復号できます。

ここまでで、フレームヘッダは終わりで、以降が入力文字列本体です。
maskフラグが’1’の場合はここに対して復号化処理を行い、maskされていない場合は、そのまま入力文字列として扱います。

以下、送信されてきたフレームデータから、入力文字列を取り出す処理です。

※今回はテストなので、データ長が64bitの場合は上位32bitをバッサリ切り捨てて32bit変数に押し込んでます。

その(3)

Post Tag With :

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です