1. ホーム
  2. linux

[解決済み】SO_REUSEADDRとSO_REUSEPORTはどう違うのですか?

2022-04-30 07:44:59

質問

man pages というソケットオプションのプログラマ向けドキュメント SO_REUSEADDRSO_REUSEPORT はOSによって異なるため、しばしば非常に混乱します。オペレーティングシステムによっては SO_REUSEPORT . WEB上では、このテーマに関して矛盾する情報が多く、特定のOSのあるソケット実装にのみ当てはまる情報が見つかることも多く、本文中で明示されていないこともあります。

では、具体的にどのように SO_REUSEADDR とは異なります。 SO_REUSEPORT ?

がないシステムは SO_REUSEPORT より制限されるのでしょうか?

また、異なるOSでどちらかを使用する場合、具体的にどのような動作が期待できるのでしょうか?

解決方法は?

移植性の素晴らしい世界へようこそ...というより、むしろその欠如です。この2つの選択肢を詳細に分析し、異なるオペレーティングシステムがそれらをどのように扱うかを深く見ていく前に、BSDソケット実装がすべてのソケット実装の母体であることに注目すべきです。基本的に他のすべてのシステムは、ある時点でBSDソケット実装(または少なくともそのインターフェイス)をコピーし、その後、独自に進化させ始めました。もちろん、BSDソケット実装も同時に進化しているので、後からコピーしたシステムは、先にコピーしたシステムにはない機能を手に入れることができます。BSD ソケット実装を理解することは、他のすべてのソケット実装を理解するための鍵であり、たとえ BSD システムのためにコードを書く気がなくても、それについて読んでおく必要があります。

この2つのオプションを見る前に知っておくべき基本的なことがいくつかあります。TCP/UDP接続は、5つの値のタプルによって識別されます。

{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}

これらの値のユニークな組み合わせで、接続を識別します。その結果、2つの接続が同じ5つの値を持つことはできず、そうでなければ、システムはこれらの接続をもはや区別することができなくなります。

ソケットのプロトコルは、ソケットの作成時に socket() 関数を使用します。送信元アドレスと送信先ポートは bind() 関数を使用します。宛先のアドレスとポートを設定するには connect() 関数を使用します。UDPはコネクションレス型のプロトコルなので,UDPソケットは接続せずに使用することができます.しかし,接続することは可能であり,場合によってはコードや一般的なアプリケーションの設計に非常に有利になります.コネクションレス型では、初めてデータを送信するときに明示的にバインドされなかったUDPソケットは、通常、システムによって自動的にバインドされます。バインドされていないUDPソケットは、いかなる(返信)データも受信できないためです。未束縛のTCPソケットも同様で、接続される前に自動的に束縛されます。

ソケットを明示的にバインドする場合、ポート 0 これは、任意のポートという意味です。ソケットを既存の全てのポートにバインドすることはできないので、その場合はシステム自身が特定のポートを選択する必要があります(通常、ソースポートの事前定義されたOS固有の範囲から選択されます)。同様のワイルドカードが送信元アドレスにも存在し、これは "任意のアドレス" とすることができます ( 0.0.0.0 はIPv4の場合、そして :: IPv6の場合)。ポートとは異なり、ソケットは任意のアドレスにバインドすることができます。ソケットが接続されると同時に、任意のローカルIPアドレスにバインドすることはできないため、後で接続する場合は、システムは特定のソースIPアドレスを選択しなければなりません。宛先アドレスとルーティングテーブルの内容に応じて、システムは適切なソースアドレスを選択し、"any"のバインドを、選択したソースIPアドレスへのバインドに置き換えます。

デフォルトでは、送信元アドレスと送信元ポートの同じ組み合わせに2つのソケットがバインドされることはありません。送信元ポートが異なる限り、送信元アドレスは実際には関係ありません。バインディング socketAipA:portAsocketB から ipB:portB があれば、常に可能です。 ipA != ipB が真である場合であっても portA == portB . 例 socketA は FTP サーバープログラムに属しており 192.168.0.1:21socketB は別のFTPサーバ・プログラムに属しており、そのサーバは 10.0.0.1:21 の場合、両方のバインディングが成功します。ただし、ソケットはローカルに任意のアドレスにバインドできることに注意してください。もし、ソケットが 0.0.0.0:21 この場合、既存のすべてのローカルアドレスに同時にバインドされ、他のソケットはポート 21 というように、どのIPアドレスにバインドしようとも 0.0.0.0 は、既存のすべてのローカルIPアドレスと競合します。

これまで述べてきたことは、すべての主要なオペレーティング・システムでほぼ同じです。アドレスの再利用が絡んでくると、物事はOSに依存するようになります。BSDから始めましょう。上で述べたように、BSDはすべてのソケット実装の母体だからです。

BSD

SO_REUSEADDR

もし SO_REUSEADDR にバインドされている他のソケットと競合しない限り、ソケットのバインドに成功することができます。 まさに は、送信元アドレスと送信先ポートの組み合わせが同じであることを意味します。さて、以前とどう違うのかと思われるかもしれません。キーワードは「quot;jourists"」です。 SO_REUSEADDR は、主にワイルドカードアドレス("任意のIPアドレス")が競合を検索する際にどのように扱われるかを変更するものです。

なし SO_REUSEADDR バインディング socketA から 0.0.0.0:21 をバインドし socketB192.168.0.1:21 は失敗します(エラー EADDRINUSE 0.0.0.0は「任意のローカルIPアドレス」を意味するので、このソケットではすべてのローカルIPアドレスが使用中とみなされ、これには 192.168.0.1 を使用します。とは SO_REUSEADDR は成功します。 0.0.0.0192.168.0.1 とは言えない は同じアドレスですが、1つはすべてのローカルアドレスを表すワイルドカードで、もう1つは非常に特殊なローカルアドレスです。上記の記述は、どの順番で書いても正しいことに注意してください。 socketAsocketB はバインドされています。 SO_REUSEADDR は必ず失敗します。 SO_REUSEADDR は必ず成功します。

概要を説明するために、ここで表を作って、考えられる組み合わせをすべて挙げてみましょう。

SO_REUSEADDR socketA socketB Result
---------------------------------------------------------------------
  ON/OFF 192.168.0.1:21 192.168.0.1:21 エラー(EADDRINUSE)
  オン/オフ 192.168.0.1:21 10.0.0.1:21 OK
  オン/オフ 10.0.0.1:21 192.168.0.1:21 OK
   OFF 0.0.0:21 192.168.1.0:21 エラー(EADDRINUSE)
   OFF 192.168.1.0:21 0.0.0:21 エラー(EADDRINUSE)
   オン 0.0.0.0:21 192.168.1.0:21 OK
   192.168.1.0:21の場合 0.0.0.0:21 OK
  ON/OFF 0.0.0.0:21 0.0.0.0:21 エラー(EADDRINUSE)

上の表は、以下を想定しています。 socketA に指定されたアドレスへのバインドが既に成功しています。 socketA であれば socketB が作成されると、どちらかが SO_REUSEADDR に与えられたアドレスにバインドされます。 socketB . Result に対するバインド操作の結果です。 socketB . もし最初のカラムが ON/OFF の値は SO_REUSEADDR は結果に関係ない。

なるほど。 SO_REUSEADDR はワイルドカードアドレスに効果があることがわかりました。しかし、効果はそれだけではありません。もう一つよく知られた効果があります。それは、ほとんどの人が SO_REUSEADDR をサーバ・プログラムで使用することができます。このオプションのもう一つの重要な使い方については、TCPプロトコルがどのように動作するかについて、より深く見ていく必要があります。

ソケットは送信バッファを持ち、もし send() 関数が成功しても、要求されたデータが本当に送信されたわけではなく、送信バッファにデータが追加されただけです。UDPソケットの場合、データは通常、すぐには送信されませんが、TCPソケットの場合、送信バッファにデータを追加してからTCP実装がそのデータを本当に送信するまで、比較的長い遅延が発生する可能性があります。その結果、TCPソケットを閉じたときに、送信バッファにまだ送信されていない保留中のデータが残っていることがありますが、これは send() の呼び出しは成功しました。もしTCPの実装があなたのリクエストに応じてすぐにソケットを閉じていたら、このデータはすべて失われてしまい、あなたのコードはそのことにすら気づかないでしょう。TCPは信頼性の高いプロトコルと言われていますが、このようにデータが失われるのはあまり信頼できることではありません。そのため、送信するデータが残っているソケットは TIME_WAIT 閉じるとき この状態では、保留中のデータがすべて正常に送信されるか、タイムアウトが発生するまで待機し、その場合はソケットが強制的にクローズされます。

カーネルがソケットを閉じるまでに、まだ飛行中のデータがあるかどうかに関係なく、最大でどれだけの時間待機するかを 余韻時間 . また リンガータイム は、ほとんどのシステムでグローバルに設定可能で、デフォルトではかなり長い値になっています (多くのシステムで 2 分が一般的な値です)。また、ソケットごとに設定することも可能で、ソケットオプションの SO_LINGER タイムアウトを短くしたり長くしたり、また完全に無効にすることも可能です。なぜなら、TCP ソケットを優雅に閉じるのは少し複雑な処理で、いくつかのパケットを送ったり戻したりする必要があり (さらに、パケットが失われた場合は再送する必要があります)、この閉じる処理全体もまた 余韻時間 . リンギングを無効にすると、ソケットが飛行中にデータを失う可能性があるだけでなく、常に強制的に閉じられるので、通常は推奨されません。TCP接続がどのように優雅に閉じられるかについての詳細は、この回答では説明できませんが、もっと詳しく知りたい場合は、以下を参照することをお勧めします。 このページ . そして、もしあなたが SO_LINGER もし、明示的にソケットを閉じることなくプロセスが終了した場合、 BSD (そしておそらく他のシステム) は、あなたが設定したことを無視して、それにもかかわらず、 待機します。これは、例えば、あなたのコードが単に exit() (小さな単純なサーバプログラムではよくあることです)、あるいはシグナルによってプロセスが強制終了される(不正なメモリアクセスのために単にクラッシュする可能性も含まれます)。そのため、どのような状況でもソケットが長引くことがないようにすることはできません。

問題は、システムが状態のソケットをどう扱うかです。 TIME_WAIT ? もし SO_REUSEADDR が設定されていない場合、ソケットの状態は TIME_WAIT は、送信元のアドレスとポートにまだバインドされているとみなされ、同じアドレスとポートに新しいソケットをバインドしようとすると、ソケットが本当にクローズされるまで失敗します(この時間は、設定された 余韻時間 . そのため、ソケットを閉じた後、すぐに送信元アドレスを再バインドできるとは思わないでください。ほとんどの場合、これは失敗します。しかし、もし SO_REUSEADDR が設定されている場合、同じアドレスとポートにバインドされた別のソケットが、そのソケットの状態 TIME_WAIT なぜなら、そのソケットはすでに半分死んでおり、あなたのソケットは全く同じアドレスに問題なくバインドできるからです。この場合、他のソケットが全く同じアドレスとポートを持っていても、何の意味もありません。瀕死のソケットと全く同じアドレスとポートを持つソケットを TIME_WAIT しかし、それはこの回答の範囲外であり、幸いなことに、そのような副作用は実際にはほとんどありません。

について最後に知っておくべきことが1つあります。 SO_REUSEADDR . 上に書いたことは、バインドしたいソケットがアドレスの再利用を有効にしている限り、すべて動作します。もう一方のソケットが、すでにバインドされている、あるいは TIME_WAIT の状態で、バインドされたときにもこのフラグが設定されていました。バインドが成功するか失敗するかを決めるコードは SO_REUSEADDR フラグに供給されるソケットの bind() の呼び出しで、他のすべてのソケットが検査された場合、このフラグは見向きもされない。

SO_REUSEPORT

SO_REUSEPORT は、ほとんどの人が予想するものです SO_REUSEADDR になります。基本的には SO_REUSEPORT を使用すると、任意の数のソケットを まさに である限り、同じ送信元アドレスとポートで すべて 以前のバインドソケットも SO_REUSEPORT はバインドされる前に設定されています。あるアドレスとポートにバインドされた最初のソケットが、バインド前に設定されていた SO_REUSEPORT が設定されている場合、他のソケットが全く同じアドレスとポートにバインドされることはない。 SO_REUSEPORT は、最初のソケットが再びバインドを解除するまで、設定されているか否かを問わない。の場合とは異なり SO_REUESADDR を処理するコードは SO_REUSEPORT は、現在バインドされているソケットが SO_REUSEPORT が設定されていることを確認し、さらに、アドレスとポートが競合するソケットに SO_REUSEPORT バインドされたときに設定されます。

SO_REUSEPORT を意味するものではありません。 SO_REUSEADDR . つまり、ソケットに SO_REUSEPORT がバインドされ、他のソケットが SO_REUSEPORT を設定した場合、全く同じアドレスとポートにバインドされると、バインドに失敗します。これは予想通りですが、他のソケットがすでに死んでいて TIME_WAIT の状態になります。のソケットと同じアドレスとポートにバインドできるようにするためです。 TIME_WAIT のどちらかが必要です。 SO_REUSEADDR がそのソケットに設定されているか SO_REUSEPORT が設定されている必要があります。 の両方で ソケットをバインドする前に もちろん、両方を設定することは可能です。 SO_REUSEPORTSO_REUSEADDR を、ソケット上で実行します。

については、これ以上語ることはないでしょう。 SO_REUSEPORT よりも後に追加されたこと以外は SO_REUSEADDR そのため、このオプションが追加される前に BSD のコードをフォークした他のシステムの多くのソケット実装では、このオプションを見つけることができず、このオプション以前には、BSD で 2 つのソケットをまったく同じソケットアドレスにバインドする方法はありませんでした。

Connect() Returning EADDRINUSE?

ほとんどの人が知っているのは bind() というエラーで失敗することがあります。 EADDRINUSE しかし、アドレスの再利用で遊んでいるうちに connect() もそのエラーで失敗します。これはどうしてでしょうか?リモートアドレスは、結局のところコネクトがソケットに追加するものですが、どうしてすでに使われているのでしょうか?全く同じリモートアドレスに複数のソケットを接続することは、これまで一度も問題になっていません。

私の回答の一番上に書いたように、接続は5つの値のタプルで定義されますよね?そして、これらの5つの値はユニークでなければならず、そうでなければシステムはもはや2つのコネクションを区別することができないと言いましたね?アドレスの再利用では、同じプロトコルの2つのソケットを同じ送信元アドレスとポートにバインドすることができるんだ。つまり、5つの値のうち3つは、この2つのソケットですでに同じ値になっているのです。もし、この2つのソケットを同じ宛先アドレスとポートに接続しようとすると、全く同じタプルを持つ2つのソケットが接続されることになります。これは、少なくともTCP接続ではうまくいかない(UDP接続はいずれにせよ、本当の接続ではない)。もしデータが2つのコネクションのどちらかに到着した場合、システムはそのデータがどちらのコネクションのものかを知ることができません。少なくとも、宛先アドレスや宛先ポートは、どちらの接続でも異なるものでなければならず、そうすれば、受信データがどちらの接続に属するかをシステムが識別するのに問題はない。

そこで、同じプロトコルの2つのソケットを同じ送信元アドレスとポートにバインドし、両方を同じ送信先アドレスとポートに接続しようとすると、次のようになります。 connect() は実際にはエラーで失敗します。 EADDRINUSE これは、同じ5つの値のタプルを持つソケットがすでに接続されていることを意味します。

マルチキャストアドレス

ほとんどの人はマルチキャストアドレスの存在を無視していますが、実際に存在します。ユニキャストアドレスが1対1の通信に使われるのに対して、マルチキャストアドレスは1対多の通信に使われます。IPv6について学んだときにマルチキャストアドレスを意識するようになりましたが、マルチキャストアドレスはIPv4にも存在していました。ただし、この機能は公共のインターネットではあまり使用されていませんでした。

の意味は SO_REUSEADDR は、マルチキャストアドレスの場合、複数のソケットを送信元マルチキャストアドレスとポートの全く同じ組み合わせにバインドすることができるようになるため、変更されます。言い換えると、マルチキャストアドレスでは SO_REUSEADDR と全く同じ動作をします。 SO_REUSEPORT はユニキャストアドレスの場合です。実は、このコードでは SO_REUSEADDRSO_REUSEPORT はマルチキャストアドレスと同じであることを意味します。 SO_REUSEADDR を意味します。 SO_REUSEPORT は、すべてのマルチキャストアドレスに適用され、その逆もまた然りです。



FreeBSD/OpenBSD/NetBSD

これらはすべて、オリジナルの BSD コードからかなり遅れてフォークされたもので、それゆえ、これら 3 つはすべて BSD と同じオプションを提供し、BSD と同じように動作します。



macOS (MacOS X)

macOSの中核は、BSDスタイルのUNIXである「"」です。 ダーウィン Mac OS 10.3 では、Apple が POSIX に完全に準拠できるように、(macOS は POSIX 認定を受けています) FreeBSD 5 のコードベースと再同期させられたのです。コアにマイクロカーネルを搭載しているにもかかわらず(" マッハ ")、それ以外のカーネル(" XNU ")は基本的にBSDカーネルと同じで、そのためmacOSはBSDと同じオプションを提供し、それらもBSDと同じように動作するのだそうです。

iOS / watchOS / tvOS

iOSはmacOSのフォークで、カーネルが若干変更され、ユーザースペースのツールセットが若干削られ、デフォルトのフレームワークセットが若干異なっています。私の知る限り、これらはすべてmacOSとまったく同じように動作します。



リナックス

Linux < 3.9

Linux 3.9以前は、オプションの SO_REUSEADDR が存在します。このオプションは、2つの重要な例外を除いて、一般にBSDと同じように動作します。

  1. リスニングしている(サーバー)TCPソケットが特定のポートにバインドされている限りは SO_REUSEADDR オプションは、そのポートをターゲットとするすべてのソケットに対して完全に無視されます。同じポートに 2 つ目のソケットをバインドすることは、BSD でも可能だった場合に限り SO_REUSEADDR を設定します。例えば、ワイルドカードのアドレスにバインドして、 さらに特定のアドレスにバインドすることはできませんし、 その逆もできませんが、BSD では SO_REUSEADDR . 同じポートにバインドして、ワイルドカード以外の2つのアドレスにバインドすることは、常に許可されているのです。この点では、Linux は BSD よりも制限的です。

  2. 2つ目の例外は、クライアントソケットに対して、このオプションは全く同じように動作することです。 SO_REUSEPORT は、バインドされる前に両方がこのフラグをセットしている限り、BSDで使用されます。これを許可した理由は、様々なプロトコルで複数のソケットを同じ UDP ソケットアドレスに正確にバインドできることが重要であり、かつてのように SO_REUSEPORT 3.9 より前のバージョンでは SO_REUSEADDR は、そのギャップを埋めるために、適宜変更されました。その点では、LinuxはBSDより制約が少ないですね。

Linux >= 3.9

Linux 3.9では、オプション SO_REUSEPORT を Linux にも導入しました。このオプションはBSDのオプションと全く同じ動作をし、すべてのソケットがバインドする前にこのオプションが設定されている限り、全く同じアドレスとポート番号にバインドすることができます。

とはいえ、まだ2つの違いがあります。 SO_REUSEPORT を他のシステムで使用することができます。

  1. ポートハイジャックを防止するために、特別な制限があります。 同じアドレスとポートの組み合わせを共有したいすべてのソケットは、同じ有効ユーザーIDを共有するプロセスに属していなければなりません! つまり、あるユーザーが他のユーザーのポートを盗むことはできないのです。これは、このような特別なマジックによって、欠落している SO_EXCLBIND / SO_EXCLUSIVEADDRUSE のフラグを設定します。

  2. さらにカーネルは、以下のような特別なマジックを行います。 SO_REUSEPORT ソケットは、他のオペレーティングシステムにはないものです。UDPソケットでは、データグラムを均等に分配しようとし、TCPリスニングソケットでは、入ってくる接続要求(を呼び出すことによって受け入れられるもの)を分配しようとします。 accept() ) を、同じアドレスとポートの組み合わせを共有するすべてのソケットに均等に配置します。したがって、アプリケーションは複数の子プロセスで同じポートを簡単に開くことができ、その際に SO_REUSEPORT を使えば、非常に安価にロードバランシングを行うことができます。



アンドロイド

Androidのシステム全体は、ほとんどのLinuxディストリビューションとは多少異なりますが、その中核はわずかに修正されたLinuxカーネルで動作します。したがって、Linuxに適用されるものはすべてAndroidにも適用されるはずです。



ウィンドウズ

Windowsが知っているのは SO_REUSEADDR オプションはありません。 SO_REUSEPORT . 設定方法 SO_REUSEADDR をソケットに設定するのと同じように動作します。 SO_REUSEPORTSO_REUSEADDR をBSDのソケットで使用することができますが、1つの例外があります。

Windows 2003 より前のバージョンでは、ソケットに SO_REUSEADDR は常に、すでにバインドされているソケットとまったく同じソースアドレスとポートにバインドすることができました。 もう一方のソケットがバインドされたときにこのオプションが設定されていなかったとしても . この動作により、あるアプリケーションは、他のアプリケーションの接続ポートを盗むことができます。もちろん、これがセキュリティ上大きな意味を持つことは言うまでもありません。

マイクロソフトはそのことに気づき、もうひとつ重要なソケットのオプションを追加しました。 SO_EXCLUSIVEADDRUSE . 設定方法 SO_EXCLUSIVEADDRUSE は、バインドに成功した場合、送信元アドレスとポートの組み合わせをこのソケットだけが所有し、他のソケットはバインドできないことを確認します。 もない がある場合 SO_REUSEADDR を設定します。

このデフォルトの動作はWindows 2003で初めて変更され、Microsoftはこれを「Enhanced Socket Security」と呼んでいます(他のすべての主要なオペレーティングシステムでデフォルトになっている動作にしてはおかしな名前ですね)。詳しくは このページをご覧ください . 3つの表があります。最初のものは古典的な動作(互換性モードを使用する場合はまだ使用可能です!)、2番目は Windows 2003 以降で bind() の呼び出しが同じユーザーによって行われた場合、そして3つ目の呼び出しは bind() の呼び出しは、異なるユーザーによって行われます。



ソラリス

Solarisは、SunOSの後継機種です。SunOS はもともと BSD のフォークをベースにしており、SunOS 5 以降は SVR4 のフォークをベースにしていますが、SVR4 は BSD, System V, Xenix のマージなので、Solaris もある程度までは BSD のフォークであり、かなり初期のものであると言えます。その結果、Solarisが知っているのは SO_REUSEADDR はありません。 SO_REUSEPORT . その SO_REUSEADDR の動作はBSDとほとんど同じです。私の知る限りでは、以下のような同じ動作をさせる方法はありません。 SO_REUSEPORT Solarisでは、2つのソケットを全く同じアドレスとポートにバインドすることはできない、ということです。

Windowsと同様に、Solarisにもソケットに排他的バインディングを与えるオプションがあります。このオプションの名前は SO_EXCLBIND . このオプションがソケットをバインドする前に設定されている場合、ソケットをバインドする前に SO_REUSEADDR が別のソケットでアドレスの競合をテストしても、何の効果もありません。例:もし socketA はワイルドカードのアドレスにバインドされており socketB があります。 SO_REUSEADDR と同じポートで、ワイルドカードでないアドレスにバインドされています。 socketA の場合、このバインドは通常成功します。 socketA がありました。 SO_EXCLBIND を有効にしている場合、その場合は SO_REUSEADDR .



その他のシステム

あなたのシステムが上記のリストにない場合、私は小さなテストプログラムを書きましたので、あなたのシステムがこれらの2つのオプションをどのように処理するかを調べることができます。 また、私の結果が間違っていると思われる場合は コメントを投稿したり、誤った主張をしたりする前に、まずそのプログラムを実行してください。

このコードをビルドするのに必要なのは、ちょっとしたPOSIX API(ネットワーク部分用)とC99コンパイラ(実際には、C99以外のほとんどのコンパイラでも、そのコンパイラが socketBinttypes.h ;例えば stdbool.h は、C99の完全サポートを提供するずっと前から、両方をサポートしていました)。

このプログラムを実行するのに必要なのは、システム内の少なくとも1つのインターフェイス(ローカルインターフェイス以外)にIPアドレスが割り当てられていて、そのインターフェイスを使用するデフォルトルートが設定されていることです。このプログラムは、そのIPアドレスを収集し、2つ目の"specific address"として使用することになります。

思いつく限りの組み合わせをテストします。

  • TCPとUDPのプロトコル
  • 通常ソケット、リッスン(サーバー)ソケット、マルチキャストソケット
  • gcc ソケット1、ソケット2、または両ソケットに設定する
  • SO_REUSEADDR ソケット1、ソケット2、または両ソケットに設定する
  • で作ることができるすべてのアドレスの組み合わせ SO_REUSEPORT (ワイルドカード)を使用します。 0.0.0.0 (特定アドレス)、そしてプライマリインターフェースで見つかった2番目の特定アドレス(マルチキャストの場合は単なる 127.0.0.1 すべてのテストにおいて)

で、その結果をきれいな表で表示します。を知らないシステムでも動作します。 224.1.2.3 その場合、このオプションは単にテストされていません。

このプログラムが簡単にテストできないのは、どのように SO_REUSEPORT のソケットに作用する。 SO_REUSEADDR というのは、ソケットを強制的にその状態にしておくのは非常に厄介だからです。幸いなことに、ほとんどのオペレーティングシステムは、ここでは単にBSDのように動作するようで、ほとんどの場合、プログラマは単にその状態の存在を無視することができます。

以下はそのコードです。 (私はそれをここに含めることができない、答えはサイズ制限を持っており、コードはこの応答を押して制限を超えてしまう).