WebSocket(RFC 6455)上で使用するプロトコル設計についての備忘録

一般論として、全二重の通信プロトコルを実装するにあたっては、いくつか注意すべき点があって、具体的には、到達確認と切断シーケンスについて定めておかないと、送達されたはずのメッセージがロストしていたり、切断タイミングによってエラーが発生*1したりする。

具体例をあげると、たとえばTCP/IPにおいてshutdown(2)を用いずに、いきなりclose(2)を呼んでいると、read(2)やwrite(2)がエラー(ECONNRESET)を返す場合がある。

翻って、WebSocket (RFC6455)の場合はどうなってるか? だいたい以下のような感じっぽい。

  • ws.close()が呼び出されるとWebSocketをCLOSING状態に変更し、Closeフレームを送信する
  • ws.onmessageはWebCosketがCLOSING状態にある間も呼ばれるかもしれない*2
  • 相手からCloseフレームを受け取った場合、自身がまだCloseフレームを送信していなければ可及的速やかにこれを送信し、自らの状態をCLOSEDに変更し、ws.oncloseを呼び出す
  • 相手からCloseフレームを受け取った場合、自身が既にCloseフレームを送信していれば、自らの状態をCLOSEDに変更し、ws.oncloseを呼び出す
  • TCP/IPソケットは両者がCloseフレームを送信した後に閉じられる
  • WebSocket実装は、Closeフレームの送信後にデータを送信してはならない

以上より、WebSocket上で全二重の通信を行う場合、

  • アプリケーションレイヤでの切断シーケンスの実装は不要*3
  • 通信相手にメッセージが届いたという保証はWebSocketでは提供されない*4

ということがわかる。

参考:

*1:通信エラーなのかか切断時のタイミング依存の問題化切り分け出来ないケースが発生する

*2:RFC6455 5.5.1によると、呼ばれないかもしれない

*3:サーバ側クライアント側いずれにおいて任意のタイミングでws.close()を呼び出してもエラー発生(ws.onerrorの呼出)の危惧なく切断処理が可能という意味

*4:そのような保証がいる場合はアプリケーションレイヤでACKを送受信する仕組みが必要