とある案件で、アプリケーション間の通信にUDPを使う検討をした時のメモ。
一般的な「Ethernet」の上で動く前提で書きます。
用語の定義
この記事では、以下の定義で用語を使用します。
-
IPパケット
IP(Internet Protocol)層でやり取りされるパケットで、IPヘッダおよびデータ部分で構成されるもの。
この記事では、「MTU単位で分割されたデータ」という意味で使用する。 -
MTU(Maximum Transmission Unit)
一度に送信することができるデータのサイズ。
物理的なネットワーク媒体ごとに固有の値を持っている(イーサネットであれば最大1500bytes)。 -
UDPパケット
UDP(User Datagram Protocol)層でやり取りされるパケットで、UDPヘッダおよびデータ部分で構成されるもの。
前提知識:各レイヤーにおける「データの信頼性」
イーサネット
フレームチェックシーケンスにより、受信側でフレーム内の破損検出を行う。
フレームが破損していた場合、そのフレームは破棄される。
IP
IPプロトコル自身は、IPパケットの消失、複製、順序の入れ替わりが起こりうる。が、これを救う仕組みはない(上位プロトコルの実装次第)。
IPパケットのヘッダには「チェックサム」があり、チェックサムが合わないIPパケットはルーティング中に破棄される。
(つまり、受信したIPパケット自身は正しい)
(参考)TCP
ネットワーク層以下のパケット転送は信頼できないという考えのもと、信頼性を保証するための機構が準備されている。
-
確認応答
受信側は、パケットを受信したときに「受信したこと」を送信側に応答する。
この応答を送信側で受け取らなかったときは、パケット紛失と判断して再送する。 -
フロー制御
確認応答で、受信側バッファーの空きサイズを通知する。
送信側は、空きがあると分かったときにパケットを送信する。 -
順序制御
上位層から受け取ったデータが(厳密には、データにTCPヘッダ、IPヘッダを付与したものが)MTUより大きい場合、TCPで分割を行う。
分割時に「シーケンス番号」を書き込み、受信側でデータを組み立てるときにシーケンス番号を用いる。
IPプロトコルでも分割する仕掛けはある(IPフラグメンテーション)が、IPプロトコルでは組み立て時の順序性の保証はしないため、TCPが肩代わりする。 -
輻輳制御
分割したデータ(TCPセグメント)が紛失したとき、上記の仕組みで再送を行う。
再送が頻発する状態になったとき、送信するデータ数を制限して、輻輳が連続して発生しないようにする。
UDP
TCPのような信頼性機構は持たない。
UDP/IP通信を選択するときのチェックリスト
-
多少のメッセージ抜けは許されるか?
(例)
・xx秒おきにデータを送る仕様なので、1、2回の抜けであれば許容できる
・受信するアプリケーション側(もしくはUDPよりも上位のレイヤー)で、データが抜けてもスキップする仕掛けがある1
・ダメだった場合はユーザーがリトライすればよい2
→許されないならば、TCPを使ったほうが楽だと思います。
-
UDPの特性がアプリケーションにとって好ましいか?
(例)
・マルチキャスト送信で、データ転送量を削減したい
・TCPの信頼性機構が逆に邪魔
(タイムアウト・リトライなどを悠長にしている場合じゃない/挙動に影響されたくない、など。)
・TCPのプロトコルオーバーヘッドが気になるほど能にシビア
(DNSなどがそうらしい3。)
→こういった前向きな理由があるならば、ぜひUDPを検討しましょう。
ただ、性能にシビアなアプリケーションを作るならば、有識者と入念に設計レビューしましょう。
-
通信路は安定しているか?
IPパケットの紛失が頻繁に発生するような通信路で、データサイズがMTUに収まらないようなメッセージを送るのには不向きといえるでしょう4。
(後述する「データがMTUに収まらない」レベルのデータを送る場合、すべてのIPパケットが揃わないとメッセージは不正になり、破棄せざるを得ないため。)
→「閉域のイーサネットでイマドキのL2/L3スイッチを使っていれば大丈夫」と聞くが、実際どうなのかは不明(詳しく調べたい)
-
送信するデータ量はどれくらいか?
-
データが(UDPヘッダ、IPヘッダを含めても)MTUに収まる(1472bytes以下5)
1つのIPパケットに収まるため、データの部分的な消失、データ内の順序の入れ替わりは気にしなくてよい。
ただし、複数の同じデータが到達する可能性はあるため、複数のUDPパケットが到達しても問題ない、または後着を破棄するアプリケーションの設計が必要。
(2022/02/28追記) もちろん、データが到達しない可能性の考慮は必要。 -
上記よりも大きいデータサイズ
(2022/02/28訂正ここから)
メッセージは複数のIPパケットに分割される。そのため、IPパケットの紛失、複製、順序の入れ替わりを考慮した設計が必要。
具体的には、受信側で「メッセージのサイズ、ハッシュなど」を確認するなど。
※上記設計であれば、もしUDP受信バッファを超えるUDPパケットが来た場合も検知できる。その他、LinuxのsocketであればMSG_TRUNC
フラグ6を見るなど。IPフラグメントの再構築時は、IPプロトコルにて再構築が行われる。すべてのIPパケットが揃うまで上位層には渡されない(そのままタイムアウトする)。7
そのため、UDPパケットとして受信できているならば、構成するIPパケットの紛失や複製、順序の入れ替わりは考慮不要。
(一応UDPでも、UDPヘッダにチェックサムを入れることはできる。ただし、IPv4はオプション扱いとのこと。8)
「データがMTUに収まる」のケースと同様に、複数のデータが到達した場合、データが到達しなかった場合を考慮したアプリケーションの設計は必要。
(2022/02/28訂正ここまで) -
Wikipediaでも言及しているとおり9、送信データが大容量の場合は転送レートを下げるなどの設計が必要(輻輳制御にあたるもの)。
-
連続してUDPパケットを送信する場合、受信側でデータ受信~処理を行える十分な時間間隔をあけるなどの設計が必要(フロー制御にあたるもの)。
-
全体的に読んでおきたいWebサイト
こちらも参考にさせていただきました。
- 基礎から学ぶWindowsネットワーク 第13回 データグラム通信を実現するUDPプロトコル
https://atmarkit.itmedia.co.jp/ait/articles/0310/09/news001.html - 図を見て学ぶネットワーク基礎 仕組みが一目瞭然、インターネットを支えるTCPとUDPを完全図解
https://xtech.nikkei.com/atcl/nxt/column/18/00780/052700004/?P=2
-
よくUDPの例に出てくるVoIPの場合、UDPの上位にRTP(Real-time Transport Protocol)があり、その時刻情報からデータ順序を把握する。 ↩
-
Wake on LANなんかがこれに該当する。 ↩
-
数学は何十年前に投げ捨てたのであっているかわからないですが、それっぽく書くと
通信路でIPパケットが紛失する確率をP(Pは0以上1以下)、分割数をnとすると
メッセージが届かない確率=(1-P)^n
となる、はず。 ↩ -
https://linuxjm.osdn.jp/html/LDP_man-pages/man7/udp.7.html ↩
-
https://atmarkit.itmedia.co.jp/ait/articles/0304/04/news001_3.html ↩
-
https://ja.wikipedia.org/wiki/User_Datagram_Protocol#%E4%BF%A1%E9%A0%BC%E6%80%A7%E3%81%A8%E8%BC%BB%E8%BC%B3%E5%88%B6%E5%BE%A1%E3%81%AE%E5%AE%9F%E7%8F%BE ↩