euniclus基本的にwebサーバーがデータを送信するのはクライアントであるブラウザからリクエストを受け付けた時ですが、リクエストなしにサーバーからブラウザへデータを送りたいときもあると思います。これを実現するためにいろいろな仕組みが用意されているようですが、その中でも期待されてるのがWebSocketらしいです。そこでWebSocketがどんなことしているのかプログラムを交え見ていきたいと思います。ハンドシェイク接続を確立してデータを送受信するためにクライアントとサーバーはハンドシェイクを行う必要があります。クライアントが以下のようなハンドシェイクリクエストを送信します。client_max_window_bitsこれを受け取ったサーバーは以下の形式のレスポンスをします。z7AQHuYX5rEzNHac/56FiVv4dV0=「z7AQHuYX5rEzNHac/56FiVv4dV0=」という文字列はリクエストにあったSec-WebSocket-Keyと258EAFA5-E914-47DA-95CA-C5AB0DC85B11というマジックナンバーのハッシュをbase64エンコードしたものです。レスポンスの内容に特に問題がなければ接続が確立します。確立が完了すればフレームという決まったパケット形式に従ってデータの送受信が行えます。フレームWebSocketでのやり取りされるパケットの形式は以下のページに定義されています。https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Exchanging_data_framesさらに非公式とされてはいますが、日本語でかかれたマニュアルもありました。https://triple-underscore.github.io/RFC6455-ja.htmlスクラッチでサーバーを実装する場合はこの形式を意識してパケットを構築する必要があります。実際にやってみましょう。{console.log(e.data)}</script></head><body></body></html>WebSocketのインスタンスを作成するわけですが、引数に以下の形式の文字列を渡します。プロトコルにwsが指定されていますが、ここがwssの場合セキュアな通信をするという意味になります。httpsで運用されているシステムであればwssを指定するようです。onopenは接続が確立されたときに実行される関数で、onmessageはデータを受信したときに実行される関数です。ws://{ホスト}:{ポート}/1ws://{ホスト}:{ポート}/サーバーWebSocketサーバーはどんな言語で実装しても問題ありません。今回はスクラッチでPythonを使って実装します。ライブラリーを使えばハンドシェイクやパケットの構築など手間なことをやってくれますが、それが使えない環境の場合は実装する必要があります。addr)send_recv(acc)先述したハンドシェイクはhandshake関数内で行われています。フレームの構築はcreate_frame関数が行います。フレームについてはそこそこボリュームがあるので今回の実装に関係のある部分について触れておきます。https://triple-underscore.github.io/RFC6455-ja.html#section-5.6statusという変数に129という数字が割り振られていますが、これによりフレームのFINbitに1が指定され、opecodeにも1が指定されます。bit列にすると10000001です。これはこのフレームはメッセージの最後であること、メッセージの内容がテキストであることを表します。ちなみにopecodeに2を指定するとメッセージがバイナリを表すことになります。今回の場合以下の内容になります。1opecodeの次にMASKフィールドとpayload長フィールドが続きます。サーバーからのフレームはマスクできないらしいためMASKには0を指定します。そのためdlenに送るデータの長さを格納するだけで問題ありません。データの長さが125より長い場合、ペイロード長フィールドに入る値が変わってきます。データの長さが0xffff以下なら126が入り、それよりも大きい場合は127が入ります。いうとこのフィールドに126の場合後続の2byteがペイロード長を表し、127の場合は後続の8byteがペイロード長を表すようになるからです。※長さはいずれも符号なしです。クライアントから送られてきたフレームを見て、このフィールドに書くのされている値が126の場合後続の2byteを、127の場合後続の8byteを取り出しそれをペイロード長とすればいいということになります。今回は簡単にするため長さが125以下のデータを送受信するようにしています。今回の場合以下の内容になります。データの長さ(dlenの内容)以上を踏まえてstruct.packのフォーマットはBBをしています。動作確認まずサーバーを起動します。wss.pyそしえブラウザからWebSocketクライアントを実装したページにアクセスします。この例ではハンドシェイクの確立後サーバーからエコーメッセージが一秒ごとに送信されてきます。この例ではサーバーからのデータを受信するだけですが、もちろんサーバーデータを送信することもできます。先のhtmlファイルを以下のように修正します。"+e.data)}データを受信したらその内容にサーバーに送り返します。サーバーは受信するわけですが、クライアントから送られてきたメッセージがエンコードされているため、キー(4byte)とペイロードをxorデコードしてメッセージを取り出します。サーバープログラムに以下の関数を追加します。どこかのタイミングでデータを受信して以下の関数を呼び出します。1」のようなメッセージを取り出すことができます。"".join(dec)ペイロード長が125より大きい場合はキーの開始オフセットが変わります。今回は話を単純にするため送受信するペイロード長は125以下にします。そのためマスクを解除するためのキーを3バイト目から4byte分取り出しています。今回作ったサンプルではクライアントから送られてくるフレームの内容は以下のようになります。12byte目MASKが1の場合メッセージがエンコードされていることを表している。データの長さ(データの長さ)3byte目Masking-Key(16bit)キー7byte目以降はデータが格納されています。実際にはWebSocketで通信するには少しだけ面倒なことをしなければいけないようです。ライブラリーを使えばこれらのほとんどをやってくれますので、本格的なシステムを構築をする場合はライブラリーを使ったほうが簡単に実装できると思います。スクラッチで実装した理由は「どんなやり取りしているかを知りたかった」というのは建前で、実際はライブラリーの存在の実装の途中まで認識していなかったことです。勉強になったからいいんですけどねぇ。コメントをキャンセルメールアドレスが公開されることはありません。転置インデックスを用いた全文検索の実装2019年11月2日c言語c言語で文字列を区切り文字で配列に2019年10月25日RustRustの所有権2019年10月19。