1.WebSocket仕様解説 実装WebSocketクライアント対応プロトコルバージョン確認編 (2012/09/28 Update)
WebSocket API(日本語訳)は、全二重の双方向通信を可能にするAPIで、これによりリアルタイムなWebアプリケーションを実現することができるようになります。WebSocketのプロトコル側の仕様が半年ほど前にRFCとして策定され、ようやく落ちついたところです。(まだ仕様変更がある可能性がないとは言い切れないけど)
※なお、ブラウザーは現時点(2012/07/26)での各最新のブラウザーを対象とします。(Chrome20,Firefox14,Opera12,Safari5+Safari6)
また、ブラウザーに実装されているWebSocket(API)のことを"WebSocketクライアント"と呼ぶことにします。
また、モバイル(スマートフォン、タブレット)ではiOSのSafari/Chrome、AndroidのChrome/Firefox/Opera MobileがWebsocketクライアントを実装しています。
しかし、現在において対応しているプロトコルバージョンが一部のブラウザーにおいてまだ新しいバージョンに対応していません。
次の項目で対応しているプロトコルバージョンを確認してみます。
そのためにはちょこっとプログラムを組む必要があります。
ハンドシェイクリクエストを取得するアプリを作成する
C#を使用してハンドシェイクリクエストを取得するアプリを作成します。
コンソールアプリケーションプロジェクトを新規に作成し、main関数内に以下のようにコードを記述したら実行します。
実行すると、下記のような警告ダイアログが表示される場合があります。表示された場合は"アクセスを許可する"ボタンをクリックしてください。
実行したら、各ブラウザーからWebsocketの接続を試してみます。(各ブラウザーの開発ツールのコンソール表示までの手順は割愛させていただきます。)
Opera (ver12.00)
Operaはセキュリティの問題があるとしてデフォルトではWebSocketが無効となっていますのでまずはOperaでWebSocketを有効にしましょう。一応セキュリティの問題があることは認識しておいてください。こちらで@komasshuさんがこのセキュリティ問題を詳しく解説しています。(kanasansoftさん情報提供ありがとうございます)
OperaのDragonflyコンソール画面にて new WebSocket('ws://localhost:8181/Opera') と入力してエンターキーを押します。
すると、ハンドシェイク取得アプリのコンソール画面に
見ていただくと分かる通り、ハンドシェイクリクエストはHTTPのGETリクエスト形式で送られてきます。 ハンドシェイクリクエストを見てみると、Sec-WebSocket-Key1/Sec-WebSocket-Key2というフィールドが見られます。これはプロトコルバージョンhybi-00(hixi-76)〜hybi-03で採用されているハンドシェイク方法で使用されるフィールドですので、Operaはhybi-00(hixi-76)〜hybi-03のいずれかに対応したWebSocketクライアントが実装されているということがわかります。(以降、ハンドシェイク取得アプリは実行したまにしてください。)
Safari (ver5.1.7)
同様にSafariの開発ツールのコンソールにて new WebSocket('ws://localhost:8181/Safari') と入力してエンターキーを押します。
Chrome (ver20.0.1132.57 m)
同様に開発ツールのコンソール画面にて new WebSocket('ws://localhost:8181/Chrome') と入力しエンターキーを押します。
プロトコルバージョンhybi-04からハンドシェイク方法の大幅な仕様変更が行われました。この仕様変更により以前のプロトコルバージョンとは互換性がありません。Sec-WebSocket-Key1/Sec-WebSocket-Key2は廃止され、代わりにSec-WebSocket-Keyというフィールドが1つになりました。同様にhybi-04からSec-WebSocket-Versionというフィールドが追加されました。これでWebSocketクライアントがどのプロトコルバージョンに対応している("要求している"といったほうが正しいかな?)かがわかるようになりました。だた、このSec-WebSocket-Versionの値が実際のプロトコルバージョンと1:1に対応しているわけではありません。
これらのことにより、Chromeにはhybi-13〜RFC(hybi-17)のいずれかに対応したWebSocketクライアントが実装されているということがわかります。
Firefox (ver14.0.1)
Firefoxの場合は、Chromeなどの(JavaScriptがデバッグできる)開発ツールが実装されていないため、アドオンをインストールする必要があります。Firefoxの開発ツールアドオンではFirebugが有名でしょう。このFirebugを使用することとして話を進めます。
Firebugをインストールしていない場合は、アドオンマネージャで検索してインストールしてください。インストールするとブラウザーの右上にFirebugのアイコン が追加されます。そのアイコンをクリックするとFirebugが起動します。 起動するとFirebugのメニューに「コンソール」という項目がありますのでそこをクリックします。クリックすると一番下に(>>>)が表示されますので、そこに new MozWebSocket('ws://localhost:8181/Firefox') と入力してエンターキーを押します。
このSafari6のハンドシェイクリクエストも確認してみます。
ついでにモバイル版のハンドシェイクリクエストも見てみましょう。
iOSにもChromeなどの他のブラウザーアプリがありますが、アプリのガイドラインにおいてWebkit Javascriptを使用しなければならいとなっているため、
他のブラウザーアプリでもSafariと同じと思われます。(ちなみにiPhoneのChromeのハンドシェイクリクエストを確認するとSafariと同じでした。(iOS6においても同様でした))
しかし、今回のiOS6に搭載されたApple独自のMapアプリがすこぶる不人気で、iOS6にアップデートするのを控える人たちが少なからずいるようです。
そのため、現在はiOS5とiOS6の2つが存在する状態となり、あまりよろしくない状況となっています。
ご存知の方が多いと思いますが、実はWebSocketのWikiなどにはどのブラウザーがどのプロトコルバージョンに対応したWebSocketクライアントを実装しているのかを表にしてまとめていますので、こちらを参考にしていただければ具体的にどのプロトコルバージョンに対応しているのかがわかります。
更に、@koizukaさんが、プロトコルバージョンごとのデータのやり取りにおける差異を表にまとめてくれています。プロトコルの仕様を理解していないとこの表の見方がわかりづらいと思われますが、パッと見てどういった違いがあるのかはわかるかと思います。hybi-15までで止まっていますがhybi-16以降もハンドシェイクやデータのやり取り(データフレームの仕様)はhybi-13と同じです。
2012/08/04追記 OperaにもRFC版が実装されたことにより(Stable版はまだ)、これでようやく実装されているプロトコルのバージョンの違いがなくなる日がさらにいっそう近くなりました。
※なお、ブラウザーは現時点(2012/07/26)での各最新のブラウザーを対象とします。(Chrome20,Firefox14,Opera12,Safari5+Safari6)
また、ブラウザーに実装されているWebSocket(API)のことを"WebSocketクライアント"と呼ぶことにします。
WebSocketクライアントが実装されているブラウザー
主要なブラウザーのうち現在においてWebSocketクライアントを実装しているのはChrome,Firefox,Opera,Safariの4つです。また、モバイル(スマートフォン、タブレット)ではiOSのSafari/Chrome、AndroidのChrome/Firefox/Opera MobileがWebsocketクライアントを実装しています。
しかし、現在において対応しているプロトコルバージョンが一部のブラウザーにおいてまだ新しいバージョンに対応していません。
次の項目で対応しているプロトコルバージョンを確認してみます。
実装されているWebSocketクライアントが対応しているプロトコルバージョンを実際に確認してみる
さて、実際にブラウザーがどのプロトコルバージョンに対応したものを実装しているのか確認してみましょう。ただし、ブラウザー上で確認する方法がないので、WebSocketで接続を開始するときにクライアントがサーバーに送るハンドシェイクリクエストを見ることで対応している大体のプロトコルバージョンを確認してみます。そのためにはちょこっとプログラムを組む必要があります。
ハンドシェイクリクエストを取得するアプリを作成する
C#を使用してハンドシェイクリクエストを取得するアプリを作成します。
コンソールアプリケーションプロジェクトを新規に作成し、main関数内に以下のようにコードを記述したら実行します。static void Main(string[] args) { // usingに以下の行を追加 // using System.Net.Sockets; // using System.Net; // using System.Text.RegularExpressions; Socket listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 仕様においてWebSocketのポートは80番ポート(wss://(SSL接続)の場合は443番ポート)でなければならないとなっていますが(MUSTではない)、 // 80番ポートが(IISなどで)使用されている場合がありますので今回は8181番ポートを使用することにします。 IPEndPoint ipe = new IPEndPoint(IPAddress.Any, 8181); listenerSocket.Bind(ipe); listenerSocket.Listen(100); while (true) { Socket workerSocket = listenerSocket.Accept(); byte[] buffer = new byte[1024]; int len = workerSocket.Receive(buffer); string handShake = Encoding.UTF8.GetString(buffer, 0, len); // GETリクエストで送られてくるパス部分を取得 string browser = Regex.Match(handShake, @"^GET\s*/([^\s]+)", RegexOptions.IgnoreCase).Groups[1].Value; Console.WriteLine(browser + "のハンドシェイクリクエスト"); Console.WriteLine(handShake); Console.WriteLine(); workerSocket.Close(); } }
実行すると、下記のような警告ダイアログが表示される場合があります。表示された場合は"アクセスを許可する"ボタンをクリックしてください。
実行したら、各ブラウザーからWebsocketの接続を試してみます。(各ブラウザーの開発ツールのコンソール表示までの手順は割愛させていただきます。)
Opera (ver12.00)
Operaはセキュリティの問題があるとしてデフォルトではWebSocketが無効となっていますのでまずはOperaでWebSocketを有効にしましょう。一応セキュリティの問題があることは認識しておいてください。こちらで@komasshuさんがこのセキュリティ問題を詳しく解説しています。(kanasansoftさん情報提供ありがとうございます)OperaのDragonflyコンソール画面にて new WebSocket('ws://localhost:8181/Opera') と入力してエンターキーを押します。
すると、ハンドシェイク取得アプリのコンソール画面に
Operaのハンドシェイクリクエスト GET /Opera HTTP/1.1 Origin: http://yahoo.co.jp Host: localhost:8181 Connection: Upgrade Sec-WebSocket-Key2: 2 6 85)9C0e 4-0O UJ8P6 Sec-WebSocket-Key1: 17^a73.73ND &_2111 Upgrade: WebSocket @09\=p{~といった感じで出力されると思います。これがハンドシェイクリクエストのデータです。
見ていただくと分かる通り、ハンドシェイクリクエストはHTTPのGETリクエスト形式で送られてきます。 ハンドシェイクリクエストを見てみると、Sec-WebSocket-Key1/Sec-WebSocket-Key2というフィールドが見られます。これはプロトコルバージョンhybi-00(hixi-76)〜hybi-03で採用されているハンドシェイク方法で使用されるフィールドですので、Operaはhybi-00(hixi-76)〜hybi-03のいずれかに対応したWebSocketクライアントが実装されているということがわかります。(以降、ハンドシェイク取得アプリは実行したまにしてください。)
Safari (ver5.1.7)
同様にSafariの開発ツールのコンソールにて new WebSocket('ws://localhost:8181/Safari') と入力してエンターキーを押します。Safariのハンドシェイクリクエスト GET /Safari HTTP/1.1 Upgrade: WebSocket Connection: Upgrade Host: localhost:8181 Origin: http://www.yahoo.co.jp Sec-WebSocket-Key1: 41Sb 1vT 7 A166 3 l70 Sec-WebSocket-Key2: _ 5i[5 ]5g4 k3\ tt037>6s ?,m?YZ??こちらもOperaと同様、Sec-WebSocket-Key1,Sec-WebSocket-Key2が見られますので、Safariもプロトコルバージョンhybi-00(hixi-76)〜hybi-03のいずれかに対応したWebSocketクライアントが実装されているということがわかります。
Chrome (ver20.0.1132.57 m)
同様に開発ツールのコンソール画面にて new WebSocket('ws://localhost:8181/Chrome') と入力しエンターキーを押します。Chromeのハンドシェイクリクエスト GET /Chrome HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: localhost:8181 Origin: http://www.yahoo.co.jp Sec-WebSocket-Key: 8cRxNay03B6I/Iu/roDBfQ== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: x-webkit-deflate-frameすると、今度はSec-WebSocket-Key1やSec-WebSocket-Key2というものがなく、Sec-WebSoket-Keyというのが1つだけとなりました。また、Sec-WebSocket-Versionというが追加されています。
プロトコルバージョンhybi-04からハンドシェイク方法の大幅な仕様変更が行われました。この仕様変更により以前のプロトコルバージョンとは互換性がありません。Sec-WebSocket-Key1/Sec-WebSocket-Key2は廃止され、代わりにSec-WebSocket-Keyというフィールドが1つになりました。同様にhybi-04からSec-WebSocket-Versionというフィールドが追加されました。これでWebSocketクライアントがどのプロトコルバージョンに対応している("要求している"といったほうが正しいかな?)かがわかるようになりました。だた、このSec-WebSocket-Versionの値が実際のプロトコルバージョンと1:1に対応しているわけではありません。
Sec-WebSocket-Versionの値 | 4 | 5 | 6 | 7 | 8 | 13 |
プロトコルバージョン(hybi-) | 04 | 05 | 06 | 07 | 08〜12 | 13〜RFC |
Firefox (ver14.0.1)
Firefoxの場合は、Chromeなどの(JavaScriptがデバッグできる)開発ツールが実装されていないため、アドオンをインストールする必要があります。Firefoxの開発ツールアドオンではFirebugが有名でしょう。このFirebugを使用することとして話を進めます。Firebugをインストールしていない場合は、アドオンマネージャで検索してインストールしてください。インストールするとブラウザーの右上にFirebugのアイコン が追加されます。そのアイコンをクリックするとFirebugが起動します。 起動するとFirebugのメニューに「コンソール」という項目がありますのでそこをクリックします。クリックすると一番下に(>>>)が表示されますので、そこに new MozWebSocket('ws://localhost:8181/Firefox') と入力してエンターキーを押します。
Firefoxのハンドシェイクリクエスト GET /Firefox HTTP/1.1 Host: localhost:8181 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ja,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive, Upgrade Sec-WebSocket-Version: 13 Origin: http://www.yahoo.co.jp Sec-WebSocket-Key: uqj8RVY6epCMoZIAjtNzkQ== Pragma: no-cache Cache-Control: no-cache Upgrade: websocketFirefoxのハンドシェイクリクエストはにぎやかです。User-Agentなども送られてきます。見ていただくとわかるようにChromeと同様Sec-WebSocket-KeyやSec-WebSocket-Versionというフィールドが見られます。FirefoxのSec-WebSocket-Versionの値も13となっているので、こちらも同様にプロトコルバージョンhybi-13〜RFC(hybi-17)のいずれかに対応したWebSocketクライアントが実装されているということがわかります。
おまけにMacのMountain Lionに搭載されたSafari6のハンドシェイクリクエストも確認してみる
2012/07/25にMax OS Xの最新メジャーバージョンMountain Lionがリリースされました。Mountain LionではSafariの最新バージョンSafari6が搭載されています。このSafari6のハンドシェイクリクエストも確認してみます。
Safari6のハンドシェイクリクエスト GET /Safari HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: localhost:8181 Origin: http://www.yahoo.co.jp Sec-WebSocket-Key: 931/dHGCRqK2asjfEOUMsA== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: x-webkit-deflate-frameChromeやFirefoxと同様にSec-WebSocket-Versionが13となっており、プロトコルバージョンhybi-13〜RFC(hybi-17)のいずれかに対応したWebSocketクライアントが実装されていることがわかります。
2012/08/04追記 さらにおまけにOpera 12.50のハンドシェイクリクエストも確認してみる
ちなみにOperaの12.50にようやくRFC版が実装されるとのことです(Opera Developer News)。いままでデフォルトでは無効となっていたのですが12.50になるとデフォルトで有効となるようです。12.50のスナップショットのインストーラーでインストールしてハンドシェイクリクエストを確認してみます。GET /Opera HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: 127.0.0.1:8181 Origin: http://www.yahoo.co.jp Sec-WebSocket-Key: Bv+wYgQ6D0M7fiSQu9q7bA== Sec-WebSocket-Version: 13こちらもChromeやFirefoxと同様にSec-WebSocket-Versionが13となっており、プロトコルバージョンhybi-13〜RFC(hybi-17)のいずれかに対応したWebSocketクライアントが実装されていることがわかります。
ついでにモバイル版のハンドシェイクリクエストも見てみましょう。
Android版 Chrome(ver18.0.1025123)
GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: 115.125.114.151:8181 Origin: http://yahoo.co.jp Sec-WebSocket-Key: 28w71cHJ2aJcHyCZwar6+g== Sec-WebSocket-Version: 13
Android版 Firefox Beta(ver15.0)
GET / HTTP/1.1 Host: localhost:8181 User-Agent: Mozilla/5.0 (Android; Tablet; rv:15.0) Gecko/15.0 Firefox/15.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ja,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive, Upgrade Sec-WebSocket-Version: 13 Origin: http://yahoo.co.jp Sec-WebSocket-Key: 54z+S4sLcELj7LjU/D83fA== Pragma: no-cache Cache-Control: no-cache Upgrade: websocket
Android版 Opera Mobile(ver12.0.4)
GET / HTTP/1.1 Host: localhost:8181 Origin: http://yahoo.co.jp Sec-WebSocket-Key1: '4 0531 `U18 248 Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Key2: V c26146e9!885O6 O&A8`:S1I
iPhone iOS Safari (ver iOS5.1.1)
GET /Safari HTTP/1.1 Upgrade: WebSocket Connection: Upgrade Host: localhost:8181 Origin: http://yahoo.co.jp Sec-WebSocket-Key1: 2 49 9}T R8 / 82 6F,9,6 Sec-WebSocket-Key2: Z pA : / 1|\07059M K5 153|S @'Ab9u1d
2012/09/28追記 iPhone iOS Safari (ver iOS6.0)
GET / HTTP/1.1 Upgrade: websocket Connection: Upgrade Host: localhost:8181 Origin: http://www.gtk2k.net Sec-WebSocket-Key: pzqLZiggVY8oz8RFzq9Nbw== Sec-WebSocket-Version: 13 Sec-WebSocket-Extensions: x-webkit-deflate-frameiOS6.0になるとSafari6と同等のSafariが実装されました。そのためWebSocketもプロトコルバージョンhybi-13〜RFC(hybi-17)に対応したWebSocketが実装されました。
iOSにもChromeなどの他のブラウザーアプリがありますが、アプリのガイドラインにおいてWebkit Javascriptを使用しなければならいとなっているため、
他のブラウザーアプリでもSafariと同じと思われます。(ちなみにiPhoneのChromeのハンドシェイクリクエストを確認するとSafariと同じでした。(iOS6においても同様でした))
しかし、今回のiOS6に搭載されたApple独自のMapアプリがすこぶる不人気で、iOS6にアップデートするのを控える人たちが少なからずいるようです。
そのため、現在はiOS5とiOS6の2つが存在する状態となり、あまりよろしくない状況となっています。
まとめ
このように、ハンドシェイクで送られてきたデータから、ある程度どのプロトコルのバージョンに対応しているのかがわかりました。ブラウザー | プロトコルバージョン |
---|---|
Opera(Windows版及びAndroid版) / Safari(Windows版およびiOS版) | hybi-00(hixi-76)〜hybi-03 |
Chrome / Firefox / Safari6 | hybi-13〜RFC(hybi-17) |
更に、@koizukaさんが、プロトコルバージョンごとのデータのやり取りにおける差異を表にまとめてくれています。プロトコルの仕様を理解していないとこの表の見方がわかりづらいと思われますが、パッと見てどういった違いがあるのかはわかるかと思います。hybi-15までで止まっていますがhybi-16以降もハンドシェイクやデータのやり取り(データフレームの仕様)はhybi-13と同じです。
2012/08/04追記 OperaにもRFC版が実装されたことにより(Stable版はまだ)、これでようやく実装されているプロトコルのバージョンの違いがなくなる日がさらにいっそう近くなりました。