WebSocket ってなに?を解決しながら双方向通信について学ぶ。

WebSocket ってなに?を解決しながら双方向通信について学ぶ。

こんばんは。七色メガネです。

最近購入した本を見ながらチャットアプリを作っていたんですが、その中で WebSocket という用語が使われていました。が、メガネはそんなものよく知りません。ということで、今回 WebSocket について調べてみました。

WebSocket ってなに?

WebSocket とはネットワークの通信規格の一つで、ウェブアプリケーションに置いて双方向通信を実現するために生まれた技術です。新しいプロトコルと言えます。

普通Webといったら、プロトコルにはHTTPが使用されます。
例えばホームページを作ってそれを公開したとします。その時ユーザはそのページへのアクセス要求(リクエスト)をサーバに投げて、サーバはそれを受けてページ情報をユーザに返却(レスポンス)します。
この時、レスポンスが生じた時点でユーザの要求は満たされるので、一旦接続は切断されます。違うページが見たくなったら再度リクエストを作成してサーバに投げます。

しかしこれだと、毎回リクエストとレスポンスのやりとりをしなければいけないのでリアルタイム性に欠けてしまったり、サーバ手動でユーザに情報を送信したりすることができませんでした。

それらの問題を解決するために生まれたプロトコルが、WebSocket です。

 

WebSocket ってどんな技術?

※画像引用 https://www.school.ctc-g.co.jp/columns/masuidrive/masuidrive12.html

 

概要

WebSocket では、クライアントとサーバのコネクションを成立させたあと、そのコネクションを切断せずに維持し続けます。確立されたコネクション上では、クライアント、サーバ相互に情報を相手に送信することが出来るようになります(双方向通信の実現)。

またリクエスト・レスポンスを毎回行わず、情報の送受信をコネクション上で小さなフレーム(というデータ単位)のやりとりによって行うため、HTTPに比べて送受信にかかるデータの総量が減少します。

なお、WebSocketはプロトコルですが、HTTPと全く異なるプロトコルかと言えばそういう訳でも無いようです。次のヘッダの内容をみてもらえればわかりますが、通常のHTTP通信におけるヘッダもWebSocketを使用した時のヘッダにも大きな違いはありません。これは、WebSocketがHTTPをベースにした技術であるということを示しています。

ハンドシェイクにおける通信方法はHTTPをベースにして、その先で双方向通信を実現するプロトコル、それがWebSocketであるようです。

コネクションの張り方

コネクションの張り方自体は、HTTPとそれほど変わりません。HTTPではコネクション形成のためにスリーウェイ・ハンドシェイクという手法をとりますが、WebSocketの場合もそれとほぼ同様で、WebSocket opening ハンドシェイクという手法をとります。こちら側で意識するのはハンドシェイクの1回目と2回目、すなわちリクエストの内容とレスポンスの内容です。

なおコネクションを管理するということから、WebSocketはTCPの技術です。UDPはコネクション志向型ではないのでWebsocketとは関係がありません。

HTTPの場合

このブログにアクセスするときのリクエストとレスポンスをみてみます。

  1. リクエストヘッダの形式

一応内容を確認しておきます。

  • Accept
    クライアント・ブラウザが受信可能なファイル形式をサーバに伝えます。ブラウザで読み込めないファイルがサーブされたら、嫌ですからね。
  • Accept-Encoding
    クライアント・ブラウザが受信可能なエンコード方式をサーバに伝えます。
  • Accept-Language
    クライアント・ブラウザが受信可能な文字セットをサーバに伝えます。
  • Connection
    HTTP1.1 で追加されたコネクション維持機能(keep-alive)を利用する場合、その情報をサーバに伝えます。
  • Cookie
    クライアント・ブラウザに保持されているHTTPクッキーをサーバに伝えます。
  • Host
    クライアント・ブラウザからサーバ名をサーバに伝えます。
  • Referer
    クライアント・ブラウザがこのページに遷移する前のURL情報(どこからきたのか)をサーバに伝えます。
  • Upgrade-Insecure-Requests
    HTTPの仕組みをHTTPSに作り変えることをサーバに通知する情報です。(詳細調査中)
  • User-Agent
    クライアント・ブラウザ自身の情報をサーバに伝えます。

 

2. レスポンスヘッダの形式

  • Keep-Alive
    keep-aliveが有効な時、コネクションのタイムアウトの時間と1回の接続で処理可能な要求数をクライアントに伝えます。

他は大体見てわかる感じなので割愛。

 

WebSocketの場合

ローカルでwebsocketを使用したサーバを立ててみて、そのリクエストとレスポンスをみてみます。

  1. リクエストの形式

大体が通常のHTTP通信と同じですが、注目したいのは次の項目です。

  • Connection
    通常のHTTP通信ではないということを明示するため、Upgrade の値を指定します。
  • Upgrade
    WebSocket プロトコルを使用することを明示します。

 

2. レスポンスの形式

こちらもリクエストと同じように、ConnectionとUpgradeの値に特徴が現れていますね。

 

どんな時に使うの?

WebSocket は双方向通信を実現します。双方向通信とは、クライアントからサーバへ情報をpushするだけではなく、サーバからクライアントへ情報をpush出来るようになることを指します。

単方向の通信について考えてみましょう。

例えばネット掲示板などでは、クライアントがブラウザから掲示板に書き込みを行った時、その書き込み情報はサーバに送られます。
書き込みはサーバに保持されますが、その情報は次にサーバが情報取得のリクエストを受け取って初めてクライアントにサーブされます。つまり、書き込みがリアルタイムで他のクライアントのブラウザに反映されることはありません(=サーバ主導で情報をpush出来ない)。

対して双方向の通信の場合です。例えばWebSocketを使用したチャットアプリについて考えてみます。

チャットルームに既に複数人のユーザが居ると仮定します。一人のユーザが書き込みを行った時、その情報は掲示板の時と同じようにサーバに送られ、サーバで保持されます。ここでサーバは能動的に情報をpushする機能を持ち合わせているため、サーバに情報が届いた時点で他のクライアントにその情報をサーブすることが出来ます。つまり、書き込みがリアルタイムで他のクライアントのブラウザに反映されることになります(=サーバ主導で情報をpush出来る)。

 

このチャットの例のように、即時性を求めるようなアプリケーションにおいてWebSocketの実現する双方向通信はとても有用なものだということが出来ます。

 

HTTPじゃダメなの?

しかしチャットのような昨日は昔からありましたし、HTTPでも双方向通信は出来るように思われます。

WebSocket が実現する機能を次の二つに分けて、分析してみます。

 

コネクションの維持について

HTTP1.1 では keep-alive という機能が追加されており、一度形成したコネクションをタイムアウトなどが起きるまで維持することが出来ます。

WebSocket もコネクションを形成した後にそれを維持し、その中で双方向通信を行うのですから一見HTTPのkeep-aliveと等価であるように思えます。

しかしHTTPのkeep-aliveはコネクションを維持するだけで、サーバからのpush機能は実現していません。
この機能は「通信のたびにコネクションを貼り直す」というHTTPの負荷デメリットを解消するものです。例えば1ページの取得に当たって数百のリクエストが必要であるような場合、リクエストの数だけコネクションを形成しなければならなかったHTTPの問題が、このkeep-aliveによって解決されます。

ですが、双方向通信の実現にはkeep-aliveは寄与しません。

双方向通信について

HTTP上でも双方向通信を実現することが出来ます。が、それぞれデメリットを抱えており、WebSocketの方がベターであるようになっています。

HTTPで双方向通信を実現する方法は主に次の二つです。

Ajax(ポーリング)

※画像引用 https://www.slideshare.net/mawarimichi/push-37869433

Ajaxはクライアントとサーバ間で非同期の通信を行う方法です。非同期とは、クライアントとサーバで全く同じ状態を常に保っていないということです。

Ajaxでは、Webページの一部を動的に更新し続けることが出来ます。これは、ページ全体ではなく一部に対してサーバとの通信を命じているからです。この時何が行われているかというと、ページの一部だけが定期的にサーバにリクエストを送り続け、サーバがそれに対して毎回レスポンスを返し、ページの一部を常に塗り替えるようなことが為されています。

これにより擬似的にリアルタイムのようにサーバの情報をクライアントが取得することが出来ます。

しかし厳密に言えば、あくまでクライアントのリクエストに対してサーバが応答しているだけであり、サーバが保持する情報に変更があった時にサーバ主導で情報をpushしている訳ではありません(=双方向通信ではない)。またサーバが保持する情報に変更がなかったとしてもクライアントはリクエストを送り続けなければいけないため、その点でも負荷があります。

Comet

※画像引用 https://www.slideshare.net/mawarimichi/push-37869433

Comet は擬似的にサーバpushを実現する方法です。

クライアントがサーバにリクエストを送った時、通常はサーバがそれに対するレスポンスをすぐにサーブします。
が、Cometではそれを行いません。Cometはクライアントからリクエストを受け取った時、リクエスト対象の情報に変更があるまでレスポンスを保留し、情報に変更が生じた時に初めてレスポンスをサーブします。

情報の変更がトリガーとなってサーバがレスポンスをサーブするので、ポーリングよりも高速に情報の返却が可能であり、厳密にはサーバpushではないものの、ほぼリアルタイムに応答することが出来ます。

ただし、レスポンスがサーブされた後に再度クライアントがリクエストを送りますが、この間にサーバが保持する情報に変更があった場合、うまく処理できない可能性があります。また仕組みとしてCometを使用する分、ポーリング方式よりも負荷が高くなってしまいます。

 

まとめ

  • WebSocket はクライアント・サーバ間の双方向通信を実現する。
  • WebSocket はハンドシェイクの方式がHTTPとほぼ同じで、いくつかの項目を付け加えるだけで実現できる。
  • HTTP1.1 ではコネクションの維持ができるが、サーバpush機能はサポートしていない。
  • HTTPでもポーリングやCometを使用することで、擬似的な双方向通信が可能である。ただし負荷や即時性といった点でWebSocketには劣る。

 

以上です。ここまでご覧いただき、ありがとうございました!

参考

https://ja.wikipedia.org/wiki/WebSocket

https://qiita.com/south37/items/6f92d4268fe676347160

https://qiita.com/chihiro/items/9d280704c6eff8603389

https://thinkit.co.jp/story/2011/03/01/2025

http://www.tohoho-web.com/ex/http.htm#Connection

https://www.slideshare.net/mawarimichi/push-37869433

https://kiririmode.hatenablog.jp/entry/20180501/1525100400

「マスタリングTCP/IP」

NetWorkカテゴリの最新記事