[解決済み] サーバー側でWebSocketメッセージを送受信するにはどうすればよいですか?
質問
-
WebSocket を使用して、プロトコルに従ってサーバー側でメッセージを送受信するにはどうすればよいですか。
-
ブラウザからサーバーにデータを送信するとき、なぜサーバーで一見ランダムなバイトが得られるのでしょうか?データは何らかの方法でエンコードされているのでしょうか?
-
サーバー→クライアント、クライアント→サーバーの両方向で、フレーミングはどのように行われるのでしょうか?
どのように解決するのですか?
注: これは、決定的なフレーミング形式に従って WebSocket メッセージの受信と送信を処理できる、非常につまらないサーバーを実装する方法に関する説明と擬似コードです。これには、ハンドシェーキング プロセスは含まれていません。さらに、この回答は教育目的のために作成されたものであり、全機能を備えた実装ではありません。
メッセージの送信
(言い換えれば、サーバー → ブラウザ)
送信するフレームは、WebSocket のフレーム形式に従ってフォーマットされる必要があります。メッセージを送信する場合、この形式は次のようになります。
- データの種類を含む 1 バイト (および些細なサーバーの範囲外であるいくつかの追加情報)
- 長さを含む1バイト
- 長さが2バイト目に収まらない場合は、2または8バイト(2バイト目は、長さに何バイトが使用されるかを示すコードとなります)
- 実際の (生の) データ
最初のバイトは
1000 0001
(または
129
) を使ってテキストフレームを作ることができます。
2バイト目は最初のビットが
0
に設定されています (サーバーからクライアントへのエンコーディングは必須ではありません)。
長さバイトを正しく送信するために、生データの長さを決定することが必要です。
-
もし
0 <= length <= 125
の場合、追加のバイトは必要ありません。 -
もし
126 <= length <= 65535
の場合、さらに2バイトが必要で、2バイト目は126
-
もし
length >= 65536
の場合、さらに8バイトが必要で、2バイト目は127
長さは別々のバイトにスライスされなければなりません。つまり、(8ビットの量で)右にビットシフトし、最後の8ビットだけを保持するために、次のようにする必要があるということです。
AND 1111 1111
(となります)。
255
).
長さのバイトの後に、生データが来ます。
これは次のような疑似コードになります。
bytesFormatted[0] = 129
indexStartRawData = -1 // it doesn't matter what value is
// set here - it will be set now:
if bytesRaw.length <= 125
bytesFormatted[1] = bytesRaw.length
indexStartRawData = 2
else if bytesRaw.length >= 126 and bytesRaw.length <= 65535
bytesFormatted[1] = 126
bytesFormatted[2] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[3] = ( bytesRaw.length ) AND 255
indexStartRawData = 4
else
bytesFormatted[1] = 127
bytesFormatted[2] = ( bytesRaw.length >> 56 ) AND 255
bytesFormatted[3] = ( bytesRaw.length >> 48 ) AND 255
bytesFormatted[4] = ( bytesRaw.length >> 40 ) AND 255
bytesFormatted[5] = ( bytesRaw.length >> 32 ) AND 255
bytesFormatted[6] = ( bytesRaw.length >> 24 ) AND 255
bytesFormatted[7] = ( bytesRaw.length >> 16 ) AND 255
bytesFormatted[8] = ( bytesRaw.length >> 8 ) AND 255
bytesFormatted[9] = ( bytesRaw.length ) AND 255
indexStartRawData = 10
// put raw data at the correct index
bytesFormatted.put(bytesRaw, indexStartRawData)
// now send bytesFormatted (e.g. write it to the socket stream)
メッセージの受信
(つまり、ブラウザ→サーバ)
取得するフレームは以下のような形式です。
- データの種類を含む1バイト
- 長さを表す1バイト
- 長さが2バイト目に収まらない場合は、2バイトまたは8バイトを追加する
- 4バイトはマスク(=デコードキー)です。
- 実際のデータ
最初のバイトは通常重要ではありません。もしテキストを送信するだけであれば、テキストタイプのみを使用することになります。それは次のようになります。
1000 0001
(または
129
) を使用することができます。
2 バイト目と追加の 2 または 8 バイト目は何らかの解析が必要です。なぜなら、長さに何バイトが使われているかを知る必要があるからです(実際のデータがどこから始まるかを知る必要があります)。長さそのものは、すでにデータを持っているので、通常は必要ありません。
2 バイト目の最初のビットは常に
1
となっており、データがマスクされている(=エンコードされている)ことを意味します。クライアントからサーバーへのメッセージは常にマスクされています。この最初のビットを削除するために
secondByte AND 0111 1111
. 結果として得られるバイトが2バイト目に収まらなかったために長さを表さないというケースが2つあります。
-
の2バイト目が
0111 1110
または126
は、次の2バイトが長さに使用されることを意味します。 -
の2バイトを
0111 1111
または127
は、次の8バイトが長さに使用されることを意味します。
4つのマスクバイトは、実際に送信されたデータをデコードするために使用されます。デコードのアルゴリズムは次のとおりです。
decodedByte = encodedByte XOR masks[encodedByteIndex MOD 4]
ここで
encodedByte
はデータの元バイトです。
encodedByteIndex
は最初のバイトから数えたバイトのインデックス(オフセット)です。
は実データの
であり、インデックス
0
.
masks
は4つのマスクバイトを含む配列です。
これは次のようなデコードのための疑似コードにつながります。
secondByte = bytes[1]
length = secondByte AND 127 // may not be the actual length in the two special cases
indexFirstMask = 2 // if not a special case
if length == 126 // if a special case, change indexFirstMask
indexFirstMask = 4
else if length == 127 // ditto
indexFirstMask = 10
masks = bytes.slice(indexFirstMask, 4) // four bytes starting from indexFirstMask
indexFirstDataByte = indexFirstMask + 4 // four bytes further
decoded = new array
decoded.length = bytes.length - indexFirstDataByte // length of real data
for i = indexFirstDataByte, j = 0; i < bytes.length; i++, j++
decoded[j] = bytes[i] XOR masks[j MOD 4]
// now use "decoded" to interpret the received data
関連
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン