要約
- TCPが守るのは順序付きバイト列であり、CALLやREPLYの境界ではない。ONC RPCは1メッセージを、1個以上の断片からなる1レコードに収めた。
- 各断片の先頭は4バイトのビッグエンディアン値で、下位31ビットがその断片長、最上位ビットが「この断片の後でレコード終了」を表した。
- レコード完了はフレーミングの証拠にすぎない。XDRの妥当性、XID、認証、手続きの受理、永続的な実行結果には別の証拠が要る。
「最後」のビットは成功を意味しなかった
パケット解析画面で最上位ビットが立つと、仕事の完了印に見えやすい。しかし、そのビットが閉じるのは手続きではない。CALLまたはREPLYを運ぶrecord-markingレコードだけである。本文の資格情報が拒否されても、プログラム番号が存在しなくても、レコードは正しく終わり得る。
逆も成り立つ。接続がレコード途中で失われても、サーバー側の処理が実行されなかったとは限らない。フレーミングは受信側に届いた境界を記述する。遠隔の副作用の履歴を運んでくるわけではない。
この狭さを理解するには、土台のTCPを見る必要がある。RFC 793は順序付きオクテットを提供し、PUSHがレコード境界を供給しないことを明記した。現行のRFC 9293も、TCPを信頼できる順序付きバイトストリームとしている。セグメント端はRPCの句読点ではない。
4バイトに長さと終端を同居させた
1988年4月のRFC 1050は、この橋渡しをrecord markingと呼んだ。1件のRPCメッセージが1件のRMレコードに入り、レコードは1個以上の断片でできる。6月のRFC 1057も同じ形式を引き継いだ。
断片ヘッダーは4バイトの符号なし値で、上位バイトから並ぶ。下位31ビットは直後に続く断片データのオクテット数で、ゼロから2^31 - 1までを表せる。最上位ビットは真偽値であり、1なら現在の断片がレコードの最後、0なら同じレコードに次の断片が続く。
重要なのは判断の時点である。最上位ビットを読んだ瞬間にレコードが終わるのではない。宣言された本文をすべて受信した後で終わる。長さ分を受信してもビットが0なら、次の4バイトは次のレコードではなく同じレコードの次断片を示す。
TCPの読み出し方から独立した状態機械
受信側はまずマーカー4バイトをそろえる。1回のreadで1バイトしか来なくても異常ではない。4バイトがそろえば、終端ビットと長さを保存し、その長さだけストリームを消費する。本文が複数回の読み出しに分かれても意味は変わらない。
非終端なら再び4バイトを集める。終端なら、それまでの断片を1件のRPCメッセージとして上位層へ渡し、余った次の4バイトから別レコードを始める。1回の読み出しに複数レコードが入っても、1枚のTCPセグメントがマーカー途中で切れても、同じ規則で処理できる。
長さゼロの断片も記述範囲に含まれる。データがなくても、終端か継続かという情報は残る。一方、宣言長の途中でEOFになれば、短いレコードが完成したのではなく未完の断片である。そこで分かるのは受信の状態であり、遠隔処理の結果ではない。
三種類の「断片」を混ぜない
RM fragmentはIPフラグメントでもTCPセグメントでもない。XDRの1項目でも、アプリケーションの1回の書き込みでもない。1つのRM断片が多数のTCPセグメントにまたがり、1つの受信バッファに複数のRM断片や複数レコードが並ぶことがある。
TCP再送やオフロードが可視パケットを組み替えても、再構成されたバイト列が同じならRM境界は同じである。したがって運用記録でpacket、segment、fragment、record、messageを同じ列に入れると、損失場所も責任主体も分からなくなる。
PSHとの違いもここにある。PSHは送信済みバイトを先へ動かす時機に影響できる。RMは、現在の断片を何バイト読めばよいか、いつ1メッセージがそろうかを決める。速く届ける希望と、文法上の終点は別の契約である。
XDRの外側に置く必要があった
RPCメッセージの値はXDRで表現される。RFC 4506は整数、可変長データ、パディング、構造体などの外部表現を共通化する。それによって異なる計算機がCALLやREPLYの内容を同じ順で解釈できる。
それでもRPC仕様は、record markerがXDR標準形式ではないと明記する。バイト順は似ていても、仕事が違うからである。マーカーはRPC値全体を解釈する前に、その値を含む範囲を見つける。内容を読まなければ終点が分からない設計なら、破損時に境界を探す層が内容の意味に依存してしまう。
正しい境界と正しい内容は別々に失敗できる。過大または未完の断片を内容解釈前に拒否できる一方、完全に区切られたレコードをXDRやRPCのエラーとして拒否することもできる。
31ビットを予約済みメモリーと読まない
31ビットは1断片の長さ欄であって、受信側への割当命令ではない。さらに1レコードは複数断片を持てるため、基礎形式はレコード全体の単一上限を示さない。宣言値をそのまま一括確保する実装は、送信者に受信メモリーの支配権を渡してしまう。
累積レコード長、断片数、同時バッファ量、継続時間にローカル予算を設ける必要がある。これはRFCの数値を読み替える主張ではなく、形式を安全に実装するための運用判断である。拒否時には、宣言値と実受信量と予算のどこで止めたかを残す。
また、仕様は境界がプロトコルエラーの検出と回復に役立つと述べるが、任意のペイロード内を走査して「それらしい4バイト」から再開する方法は定めない。偶然の値を新レコードとみなすより、接続を閉じる方が正確な場合がある。
形は保たれ、別の転送では置き換えられた
RFC 1831は1995年に同じ形式を標準化し、RFC 5531は2009年にワイヤ変更なしで引き継いだ。簡単な形式が残った理由は、TCPに欠ける境界だけを補ったことにある。
一方、RFC 8166のRPC-over-RDMAは、transport streamとpayload streamからなる別のフレーミングを持ち、TCP record markingを含む他のRPC framingを置き換える。下層でTCPを使っていても重ねない。動的な切り替えはRPCメッセージとメッセージの間で、転送方式と協調して行う。
つまりrecord markingはRPC手続きそのものの永久属性ではない。TCPというバイトストリームにRPCメッセージを載せるための、転送別アダプターだった。
資料が証明しないもの
閉じた資料集合はRFC 793、RFC 1050、RFC 1057、RFC 1831、RFC 4506、RFC 5531、RFC 8166、RFC 9293である。これらは形式と改訂と層境界を証明するが、現在の採用率、特定ライブラリの動作、実接続の内容、相手の身元、権限、手続き結果を証明しない。
最上位ビットが長く生き残ったのは、控えめだったからだ。異なるパケット化の上でもメッセージ終点を共有できたが、その中の仕事まで終わったとは一度も宣言しなかった。
会員向け解説
プロフィールの詳細
適切な会員レベルでログインすると、解説全文と出典メモをご覧いただけます。
Strategic Circle 限定
Strategic Circle
すべての読者に公開されています。参加してログインすると プロフィール解説 を閲覧できます。
Strategic Circle に参加Leadership Alliance 会員限定
Leadership Alliance
対象となる IP 資産の所有者・管理者向けです。ログインすると Leadership Alliance の解説を閲覧できます。
Leadership Alliance に参加
