💬

gRPC Server streaming 通信の内容を Wireshark で確認する

2022/08/05に公開

gRPC Server streaming 通信のパケットを解析してみます。

Wireshark の設定方法などは前回の記事に簡単に書いているので参考にしてください。

解析対象のサーバ・クライアントプログラムも前回のものを使います。

パケット解析

Server streaming RPC

処理の流れ

  1. クライアントが ListTransform に自分の位置情報と向きを Transform として投げる
  2. サーバが他プレイヤー想定の Transform を複数件返す

  • No.823 - 825 c <=> s
    • TCP 3 way handshake
  • No.826 - 827 c => s
    • HTTP2 Magic
  • No.828 - 835 c <=> s
    • HTTP2 Settings
  • No.836 - 837 c => s
    • HTTP2 Header POST /grpc.GameService/ListTransform
  • No.838 - 839 c => s
    • HTTP2 DATA grpc.Transform
  • No.840 - 843 s => c
    • HTTP2 WINDOW_UPDATE
  • No.844 - 845 s => c
    • HTTP2 Headers 200 OK
    • HTTP2 DATA /grpc.GameService/ListTransform, Response
    • HTTP2 DATA /grpc.GameService/ListTransform, Response
    • (snip 100件)
  • No.846, 849 s => c
    • HTTP2 Headers End Headers, End Stream
  • No.847 - 848, 850 - 851 c => s
    • HTTP2 WINDOW_UPDATE
  • No.852
    • TCP rst

TCP 3 way handshake, HTTP2 Magic, HTTP2 Settings までは Unary RPC と変わらず。

No.836 - 839 がクライアントがリクエストしているところ。

No.836 - 837 でヘッダ(167 Bytes)を送った後、No.838 - 839 でデータ(82 Bytes)を渡しています。

データ部を確認

Transform としてなぜか 4 Bytes しか送信されていません・。・・。

  • Transform = Vector3 * 2
  • Vector3 = float32 * 3

なので、少なくとも 4 Bytes * 3 * 2 = 24 Bytes と結構大きな構造体になるはず・・・。

何故かと思って調べてみると、クライアント側コードで Transform の float32 を 0 で送信していたので、Protocol Buffers の仕様でデフォルト値として扱われて省略されたみたいです。

https://developers.google.com/protocol-buffers/docs/proto3#default

クライアントコードを修正して 0 ではなく乱数で埋めるようにしてみます。

修正後

main.go
	p := pb.Vector3{X: rand.Float32(), Y: rand.Float32(), Z: rand.Float32()}
	r := pb.Vector3{X: rand.Float32(), Y: rand.Float32(), Z: rand.Float32()}
	t := pb.Transform{Position: &p, Rotation: &r}

再度解析してみます。

データ(112 Bytes)に変わった

内部構造は以下のようになっています。

  • HTTP2 Stream DATA (48 Bytes)
    • GRPC Message (39 Bytes)
      • Protocol Buffers (34 Bytes)
        • Message: grpc.Transform (34 Bytes)
          • Field(1): position (17 Btyes)
            • Message: grpc.Vector3 (15 Bytes)
              • Field(1) float32 5 Bytes
              • Field(2) float32 5 Bytes
              • Field(3) float32 5 Bytes
          • Field(2): rotation (17 Btyes)
            • Message: grpc.Vector3 (15 Bytes)
              • Field(1) float32 5 Bytes
              • Field(2) float32 5 Bytes
              • Field(3) float32 5 Bytes

float32(4 Bytes) に制御データが追加されて 5 Bytes になっていたり、データとしては少し膨らんでいます。

元の解析データに戻り、No.844 - 845 がサーバのレスポンス

全体で 4887 Bytes
HEADERS(23 Bytes) の後、DATA(48 Bytes) が 100 件続いています。

DATA の内部構造自体はリクエストと全く同じでした。

というわけで server streaming RPC の解析は以上です。

制御データが付与されてデータが膨れているものの、最小限に抑えられているように思いました。
もちろん完全に型を固定して通信すれば、限界まで削れるとは思いますが、型を変えたくなった時に対応が大変そうな気もします。

streaming 通信が一度始まってしまえば無駄なパケットも少ないですし、オンラインゲームの様な極度に効率的な通信を求められるユースケースでも、利用シーンによっては許容範囲に入ってくるのでは・・・?と感じました。

Discussion