UDP — 何も約束しないから得られるもの
一言でいうと
UDPは、ポート番号とチェックサムだけを載せた薄い殻であり、信頼性をアプリケーションが自分で設計したいときに、その自由を与えてくれます。
なぜ必要なのか
TCPの信頼性はタダではありません。接続の確立に往復が必要で、損失が生じると、再送が終わるまで、その後のデータをアプリケーションに渡してくれません。リアルタイムの音声通話で、0.5秒前の音を再送してもらっても役に立たないのに、TCPは待ちます。順序を保証するという約束のためです。
この現象をヘッドオブラインブロッキングといいます。あるアプリケーションには、信頼性より遅延が重要で、そのときは、何も約束しないトランスポート層のほうが、かえってよいのです。
どう動くのか
UDPヘッダーは8バイトです。送信元ポート、宛先ポート、長さ、チェックサムがすべてです。接続状態も、シーケンス番号も、再送もありません。送ったデータグラムは、届くこともあれば届かないこともあり、順序が入れ替わることもあります。
この単純さが、3つをもたらします。1つ目は、接続確立の往復がないことです。2つ目は、サーバーが接続状態を保持しないので、はるかに多くのクライアントをさばけることです。3つ目は、アプリケーションが再送のポリシーを自分で決められることです。
DNSがUDPを使う理由が、1つ目と2つ目です。問い合わせ1つに応答1つで終わるのに、接続を結んで切るために、往復を3回使う理由がありません。ただし、応答が512バイトを超えると、サーバーが切り詰め(TC)フラグを立て、クライアントは同じ問い合わせをTCPで送り直します。そのため、DNSサーバーへのTCP 53ポートをブロックすると、普段は問題ないのに、レコードが増えた特定のドメインだけが解決されなくなります。診断しにくい種類の事故です。
QUICは、3つ目を極限まで押し進めた例です。UDPの上に、再送、順序保証、暗号化、多重化をすべて新しく実装しました。なぜTCPを直さずに、UDPの上に作り直したのでしょうか。TCPは、OSのカーネルと、世界中の中間機器に組み込まれていて、変えるのに10年かかりますが、UDPの上の実装は、アプリケーションのアップデートだけで配布できるからです。HTTP/3が、ストリームごとの独立した配送を提供して、TCPのヘッドオブラインブロッキングをなくしたのも、この自由のおかげです。
現場での姿
UDPを使うときに、必ず自分で気をつけるべきものが、サイズです。MSSクランピングはTCPにだけ適用されるので、UDPベースのプロトコルは、パスMTUの問題を自分で処理する必要があります。QUICが、デフォルトのデータグラムサイズを、1200バイトという保守的な値に設定している理由が、これです。
もう1つ。UDPにはフロー制御がないので、送る側が受け取る側より速いと、受信バッファーがあふれて、黙って捨てられます。/proc/net/udpのdrops列や、ss -u -aでキューの状態を見ると、この損失を確認できます。メトリクスコレクターやログ転送ツールが、「ときどきデータが抜ける」と言うときに、よく見つかる原因です。
続くクイズで確認すること
UDPを選ぶのがいつ合理的なのか、TCPの順序保証が、なぜあるアプリケーションにはかえって有害なのかを説明できるかを確認します。