UDP/IP通信を選択するときのチェックリスト

とある案件で、アプリケーション間の通信に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よりも上位のレイヤー）で、データが抜けてもスキップする仕掛けがある[^message_lost_2]  
　・ダメだった場合はユーザーがリトライすればよい[^message_lost_3]  
→許されないならば、TCPを使ったほうが楽だと思います。  
[^message_lost_2]: よくUDPの例に出てくるVoIPの場合、UDPの上位にRTP（Real-time Transport Protocol）があり、その時刻情報からデータ順序を把握する。
[^message_lost_3]: Wake on LANなんかがこれに該当する。

- **UDPの特性がアプリケーションにとって好ましいか？**  
（例）
　・マルチキャスト送信で、データ転送量を削減したい  
　・TCPの信頼性機構が逆に邪魔  
　　（タイムアウト・リトライなどを悠長にしている場合じゃない／挙動に影響されたくない、など。）  
　・TCPのプロトコルオーバーヘッドが気になるほど能にシビア  
　　（DNSなどがそうらしい[^DNS_UDP]。）
→こういった前向きな理由があるならば、ぜひUDPを検討しましょう。  
　ただ、性能にシビアなアプリケーションを作るならば、有識者と入念に設計レビューしましょう。  
[^dns_udp]: https://atmarkit.itmedia.co.jp/fnetwork/dnstips/028.html  

- **通信路は安定しているか？**
IPパケットの紛失が頻繁に発生するような通信路で、データサイズがMTUに収まらないようなメッセージを送るのには不向きといえるでしょう[^probability]。  
（後述する「データがMTUに収まらない」レベルのデータを送る場合、すべてのIPパケットが揃わないとメッセージは不正になり、破棄せざるを得ないため。）  
→「閉域のイーサネットでイマドキのL2/L3スイッチを使っていれば大丈夫」と聞くが、実際どうなのかは不明（詳しく調べたい）  
[^probability]: 数学は何十年前に投げ捨てたのであっているかわからないですが、それっぽく書くと  
通信路でIPパケットが紛失する確率をP（Pは0以上1以下）、分割数をnとすると  
　メッセージが届かない確率＝（1-P）^n
となる、はず。  

- **送信するデータ量はどれくらいか？**
  - データが（UDPヘッダ、IPヘッダを含めても）MTUに収まる（1472bytes以下[^udp_pdu_size]）
    1つのIPパケットに収まるため、データの部分的な消失、データ内の順序の入れ替わりは気にしなくてよい。  
    ただし、複数の同じデータが到達する可能性はあるため、**複数のUDPパケットが到達しても問題ない、または後着を破棄するアプリケーションの設計が必要**。  
    (2022/02/28追記) もちろん、データが到達しない可能性の考慮は必要。

  - 上記よりも大きいデータサイズ  
    (2022/02/28訂正ここから) 
    ~~メッセージは複数のIPパケットに分割される。そのため、IPパケットの紛失、複製、順序の入れ替わりを考慮した設計が必要。~~  
    ~~具体的には、**受信側で「メッセージのサイズ、ハッシュなど」を確認する**など。~~
    ~~※上記設計であれば、もしUDP受信バッファを超えるUDPパケットが来た場合も検知できる。その他、Linuxのsocketであれば`MSG_TRUNC`フラグ[^udp_buffer]を見るなど。~~

    **IPフラグメントの再構築時は、IPプロトコルにて再構築が行われる。すべてのIPパケットが揃うまで上位層には渡されない（そのままタイムアウトする）。**[^ip_fragment]  
    そのため、UDPパケットとして受信できているならば、構成するIPパケットの紛失や複製、順序の入れ替わりは考慮不要。  
    （一応UDPでも、UDPヘッダにチェックサムを入れることはできる。ただし、IPv4はオプション扱いとのこと。[^udp_checksum]）  
    「データがMTUに収まる」のケースと同様に、複数のデータが到達した場合、データが到達しなかった場合を考慮したアプリケーションの設計は必要。
    (2022/02/28訂正ここまで) 

  - Wikipediaでも言及しているとおり[^udp_wiki]、送信データが大容量の場合は転送レートを下げるなどの設計が必要（輻輳制御にあたるもの）。  

  - 連続してUDPパケットを送信する場合、受信側でデータ受信～処理を行える十分な時間間隔をあけるなどの設計が必要（フロー制御にあたるもの）。  

[^ip_fragment]: https://atmarkit.itmedia.co.jp/ait/articles/0304/04/news001_3.html
[^udp_checksum]: http://www.ieice-hbkb.org/files/03/03gun_04hen_04.pdf
[^udp_pdu_size]: https://blogs.itmedia.co.jp/komata/2011/04/udp-8fa9.html
[^udp_buffer]: https://linuxjm.osdn.jp/html/LDP_man-pages/man7/udp.7.html
[^udp_wiki]: 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

# 全体的に読んでおきたい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