私はIPにとらわれないコーディングをしようとしていますが、さまざまなソースから示唆されているように、sockaddr_storageを使用しようとしました。ただし、すべてのAPI呼び出し(getaddrinfo、getnameinfo)は、引き続きstructsockaddrに依存しています。そして、それらの間でキャストすることは正確に良いオプションではありません、gvesは他の多くの問題を引き起こします。
そして、sockaddr_inとsockaddr_in6に別々にキャストすると、sockaddr_storageを使おうとする目的が損なわれます。
単純なクライアントサーバーソケットアプリケーションの開発にsockaddr_storageを効果的に使用したことのある人。
IPV6とIPV4のプログラミングを共同で行う場合の問題は、純粋なsockaddr構造体自体がsockaddr_in6を保持するのに十分な大きさではないことです。したがって、sockaddr_inまたはsockaddr_in6のいずれかである可能性のあるアドレスを盲目的に渡す必要がある場合は、sockaddr_storageの方が少し使いやすいです。
結局のところ、sockaddr_in、sockaddr_in6、またはsockaddr_storageのいずれを使用していても、sendto、recvfrom、connect、accept、およびその他の多くのソケット関数を呼び出すには、これらのポインターをキャストする必要があります。これは、ソケットプログラミングの既知のニュアンスにすぎません。安全でないことをしているという感覚を手放すだけです。あなたのコードは大丈夫です。
これで、IPV4とIPV6の両方で機能することを目的としたネットワークコードを作成するときに、さまざまなネットワークタイプを処理するための豊富なswitchステートメントがあるという罠に簡単に陥ることがあります。次に、コードは次のように乱雑になります。
if (addr.ss_family == AF_INET)
sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in))
else (addr.ss_family == AF_INET6)
sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in6));
そして、そのタイプの「if family == AF_INET」式は、簡単に何度も繰り返し始めることができます。それはあなたが避けたいものです。
C++を使用していると仮定すると、ソケットアドレスオブジェクトの抽象化クラスが非常に便利であることがわかります。 github here と here に例があります。 CSocketAddressクラスは、{sockaddr、sockaddr_in、sockaddr_in6}の和集合によってサポートされており、sockaddr_storageを使用して構築できます。このクラスを開始する前にsockaddr_storageについて知っていたとしたら、ユニオンの代わりにそれを使用していました。いずれにせよ、次のようにコードを書くことができます。
CSocketAddress addr;
...
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength());
同様に、「accept」ステートメントは次のようになります。
sockaddr_storage addrstorage = {};
int len = sizeof(sockaddr_storage);
accept(sock, (sockaddr*)&addrstorage, &len);
CSocketAdddress addr(addrstorage); // construct an address object to pass around everywhere else
これは、bind、send、およびrecvを呼び出すコードパスにとって非常に役立ちました。これで、STUNサーバーとクライアントのコードパスは、ソケットアドレスのファミリタイプについて何も知る必要がなくなりました。これらは「CSocketAddress」オブジェクトでのみ機能します。 IPV4およびIPV6固有のコードは、クライアントとサーバーの初期化中、つまりアドレスオブジェクトが実際に構築されるときだけです。幸いなことに、それも部分的に抽象化されました。
ヘルパー関数を熟読することもできます ここ 。ホスト名を解決したり、アダプタを列挙したりするために、IPに依存しない方法でさらに役立つものがいくつかあります。これはLinuxコードですが、一部はWindowsとwinsockに正常にマップされるはずです。
このコードベースへのTCPサポートの追加はほぼ完了しました。SOCK_STREAMのサポートを追加するプロセスでは、の違いに対処するために1つの変更を加えたり、新しいコードを追加したりする必要はありませんでした。 IPV4およびIPV6アドレス構造。
私は一般的に_struct sockaddr_storage
_の必要性を認識していません。特定のプロトコルのsockaddr構造体に十分なスペースを割り当てることが目的ですが、IPバージョンに依存しないコードでそれを行う必要がある頻度はどれくらいですか。通常、getaddrinfo()
を呼び出すと、一連の_struct sockaddr *
_ sが得られ、それらが_sockaddr_in
_であるか_sockaddr_in6
_であるかは関係ありません。単に、それらを渡すだけです。そのままbind()
およびconnect()
に(キャストは不要)。
典型的なクライアント/サーバーコードでは、_struct sockaddr_storage
_が役立つと思う主な場所は、accept()
の2番目のパラメーター用のスペースを予約することです。この場合、accept()
に対して1回、getnameinfo()
に対してもう一度_struct sockaddr *
_にキャストする必要があるのは醜いことに同意します。しかし、私はそれらのキャストを回避する方法を見ることができません。これはCです。構造体の継承には常に多くのキャストが含まれます。