

UDPのソケット通信をC++で実装しています。

受信バッファサイズの設定方法がわかりません。どの程度のバッファを送信したかを確認するツールなどはありますでしょうか。

作る際に参考にしたページのコードでは2048bitに設定しています。

[UDP / IP でパケットの送受信を行う](https://hensa40.cutegirl.jp/archives/780)

環境  
Ubuntu 20.04 LTS


CC BY-SA 4.0
2022-10-14T01:05:46.427
91583
2022-10-18T09:36:12.187
2022-10-14T02:55:59.063
3060
54466
question
socket udp
ソケット通信のバッファサイズについて


短い答え  
バッファサイズは 2048 バイトでよいです

長い答え  
Ethernet 上での UDP パケットの最大サイズは MTU で決めてよいです。 MTU の最大値は 1500 byte ないし PPPoE
等が入ると小さくなって 1492 だったり 1454 だったりします。なので「１回で転送できる最大バイト数」は 1454 としておくのが無難です。

バッファサイズは 1454 より大きい値であればいいので、いくつでもいいです。極端な話、１ギガバイトのバッファを用意して 1454
バイトしか使わないというのもアリです。無意味ですが。あとは CPU の L1/L2/L3
キャッシュの境界にうまく載るように（コンパイラや実行環境が載せてくれることを期待して）２のべき乗サイズにしておくのが無難で、となると 1454
以上の２のべきは 2048 だからこれを採用しておけば十分です。

ローカルイントラネットで全機器がジャンボフレーム対応なら MTU
を大きくできますが、では具体的にいくつにするとよいかは機器によって違うので最適値は現場によって異なります。いちいち調査してられないので、ソフト作成側はそんなことは気にせず
MTU=1454 バイトを採用しておけば十分。

UDP を Ethernet 上で実装するなら UDP
は成功するか失敗するかしかありえないので「どの程度のバッファを送信したか」なんてものは意味がありません。受信側から見て、転送が成功するか失敗するかしかないです。成功時は
`sendto()` で送ったバイト数が正確に得られ、失敗時は何も得られません（成功か失敗かすら不明）


CC BY-SA 4.0
2022-10-14T02:34:31.580
91586
2022-10-14T02:34:31.580
8589
91583
answer


質問が曖昧なため, 解釈によって捉え方が違ってきそう  
可能なら, 状況を詳しく記したほうがよいでしょう

* * *

#### メッセージ長を調べる方法

最大メッセージサイズ(UDPのパケットサイズ) は設計段階で(本来は) 決めることです

送信側プログラムの構造(詳細)が不明などの場合, 送信内容の確認には

  * tcpdump や Wireshark, eBPF などのツールで確認することが可能
  * ユーザーアプリでもとりあえず可能 ( **Ubuntu** 20.04 LTS での例) 
    1. [recv, recvfrom, recvmsg](https://manpages.ubuntu.com/manpages/focal/ja/man2/recv.2.html) のフラグ: `MSG_PEEK`, `MSG_TRUNC` を指定することでパケットの長さがわかる
    2. バッファーが足りなければ拡張
    3. (`MSG_PEEK`無しで) 実際の読み込み

* * *

#### 受信バッファーサイズ設計

受信バッファーサイズの設計としては, 他の解にも出てる通り

  * ハードウェアおよびネットワークの現実に最大限マッチするようにサイズ値は比較的小さい 2の累乗
  * 送信側を考慮できるなら, MTU - 30バイト前後(各種ヘッダー長) までのサイズで送るとよい

また, 送信の際 Ubuntu含む最近の OSでは, MTU超えると (そのままでは)送信できない場合もあるので注意

参考: [udp - IPv4 の
ユーザーデータグラムプロトコル](https://manpages.ubuntu.com/manpages/focal/ja/man7/udp.7.html)

> デフォルトでは、Linux の UDP は Path MTU Discovery を行う。 つまり、カーネルは特定の宛先 IP アドレスの MTU
> (Maximum Transmission Unit; 最大転送単位) を記録し、UDP パケットの書き込みが MTU を超えた場合
> `EMSGSIZE` を返す。 `EMSGSIZE` を返された場合、アプリケーションはパケットサイズを小さくすべきである。 ソケットオプション
> `IP_MTU_DISCOVER` または`/proc/sys/net/ipv4/ip_no_pmtu_disc` ファイルを使って Path MTU
> Discovery を無効にすることもできる (詳細は
> [ip](https://manpages.ubuntu.com/manpages/jammy/ja/man7/ip.7.html)(7) を参照)。
> Path MTU Discovery を無効にした場合は、パケットサイズが インターフェースの MTU よりも大きいと UDP はそのパケットを
> フラグメント化して送出する。 しかしながら、性能と信頼性の理由から Path MTU Discovery を 無効にするのは推奨できない。

* * *

### 追記

質問にある「参考にしたページのコード」について

あまり参考にすべきプログラムに見えないです

  * `recvfrom()` の引数 `from_addr`, `sin_size` の初期化が行われていない (`sin_size` は入出力両用)
  * それなのに受信バッファーは `memset()` してる (必要ない)

APIドキュメント参考に, 正しい使い方してるサイトを参考にしたほうがよいでしょう  
(パッと検索してみると, NULL指定で無視してるところが多く 変数指定は見当たらなかったけど, どこかにあるはず)

* * *

### 追記2

Pythonでのバッファ確認スクリプト (たぶん Linuxのみ動作)  
(送信された・到着した バッファの確認ツール)

[code]

    import logging
    logging.basicConfig(
        level=logging.DEBUG,
        format='{asctime},{msecs:05.1f} {levelname}: {message}',
        datefmt='%X', style='{',
    )
    echolog = logging.getLogger('udpecho')
    
    import socket
    import select
    
    MSG_PEEK  = 0x02    # Linuxのヘッダーからもってきた値, 他では動かないかも
    MSG_TRUNC = 0x20    #
    
    def echo_server(sk, args={}):
        echolog.info(f'UDP echo {sk.getsockname()}')
        buffsize = args.get('buffsize', 4096)
        while True:
            # 必要なら select などで受信バッファー確認
    
            if args.get('peek', False):
                mesg, addr = sk.recvfrom(buffsize, MSG_PEEK|MSG_TRUNC)
                echolog.info(f'PEEK {addr}, len={len(mesg)}')
            mesg, addr = sk.recvfrom(buffsize)
            echolog.info(f'recvfrom {addr}, len={len(mesg)} {mesg=}')
    
            if args.get('echo', True):
                r = sk.sendto(mesg, addr)
                echolog.info(f'sendto {r=}')
    
    if __name__ == '__main__':
        sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sk.bind(('', 9999))
    
        try:
            echo_server(sk, {'select': True, 'peek': True, 'buffsize': 16})
        except KeyboardInterrupt:
            print('---')
        finally:
            sk.close()
        import sys
        sys.exit()
    
[/code]


CC BY-SA 4.0
2022-10-14T07:02:50.017
91592
2022-10-18T09:36:12.187
2022-10-18T09:36:12.187
43025
43025
91583
answer