この記事について
この記事はいつしかやった会津大学RFC輪読会にて、僕が読んだものをまとめていて、公開し忘れていたものです。
読んだRFCはWebSocketについての RFC6455
https://tools.ietf.org/html/rfc6455
日本語役があります。
https://triple-underscore.github.io/RFC6455-ja.html
WebSocketは前々から色々と使っていて、ライブラリを使っている際にちょくちょく疑問点あったので、このタイミングで読めてよかったです。
ここから僕のメモ件、まとめたものです。
WebSocketとは
サーバーの更新を伝えるために、めっちゃポーリングされてていてそれはTCP接続が増える、上にリクエスト毎にHTTPヘッダがが含まれるためにオーバーヘッドが大きい
解決策として、クライアント、サーバー両方向用にに単独のTCP接続を利用する。
HTTPをトランスポート層に利用していて、HTTPプロキシやフィルタリングもサポートできるようになっている。
WebSocket通信
WebSocketの通信は、ハンドシェイクとデータ転送からなリマス。
WebSocket通信
WebSocketの通信は、ハンドシェイクとデータ転送からなリマス。
開始ハンドシェイク
Client
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
一行目はWebSocketのエンドポイントを識別する
Sec-WebSocket-Protocol
はクライアントが受けられるアプリの指示に使える。一つのHTTP Requestに複数のヘッダがあってもおk
Origin
はブラウザから送信する際につくもので、オリジンが違う接続を却下できる
Sec-WebSocket-Key
にクライアントが生成したランダムな長さが16バイトの列をbase64エンコードしたもののnonce。
Sec-WebSocket-Version
は13が固定で指定される
サーバー
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Accept
はクライアントがSec-WebSocket-Key
で送ったものをハッシュを構築したものを含めて返す。
これがない場合接続が確立されない。
Sec-WebSocket-Protocol
にはサーバーがクライアントのSec-WebSocket-Protocol
から自分が話せるプロトコルを一つ選択しつける。
データの送信
ハンドシェイクが成功したら、サーバー、クライアントが独立してデータを送信できる。
“messages”と呼ばれる概念的な単位で区切られているデータを相互に転送する。
一つのmessageは一つ以上のフレームからできている。
フレーム
データの種類
- テキスト
– バイナリ
– 制御用(closeなど)
– ping, pong
– 追加ように予約されているものがいくつかある。
これは例えばGoのWebSocketライブラリである /gorilla/websocket
の場合
if err := conn.WriteMessage(messageType, p); err != nil {
log.Println(err)
return
}
のmessageType
で指定するものです。
ここで渡しているmessageType
はint
なのですが、その値は以下のようになっています。
0x01 | テキスト |
0x02 | バイナリ |
0x03~0x07 | 追加のデータフレームに予約ずみ |
0x08 | close |
0x09 | ping |
0xA | pong |
0xB~0xF | 追加の制御フレームに予約済み |
終了ハンドシェイク
双方が終了の制御フレームを遅れるので、最初にCloseを送った方が、相手がCloseを送ったら接続を切る。
URIについて
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
例: wss://example.com/websocket?query="hogefuga"
セキュリティとか
- 接続の機密性、安全性はTLS通す必要があるので
wss
を使う - ブラウザ以外からも通信できるので、予期しない方法でアクセスされている可能性を考慮する必要がある。
以上です。
終わりに
簡単かと思いきや結構量があって難しかったです。まだ全部は読みきれませんでした。
双方向通信は最近だとHTTP2を用いたgRPCなんかが出ていますが、~gRPCはまだstreaming対応していないような~
https://github.com/grpc/grpc-web#3-write-your-js-client
Streaminに対応しているようです。
Webも対応してるみたいなので、WebSocketの代用にgRPC Streamも良さそうですね