11

具体的には、sin_addr は IPv4 と IPv6 ソケット アドレスの異なるメモリ ロケーションに配置されているようです。これは奇妙な結果になります:

#include <stdio.h>                                                                               
#include <netinet/in.h>                                                                          

int main(int argc, char ** argv) {                                                               
  struct sockaddr_in sa;                                                                         
  printf("sin_addr in sockaddr_in  = %p\n", &sa.sin_addr);                                       
  printf("sin_addr in sockaddr_in6 = %p\n", &((struct sockaddr_in6*)&sa)->sin6_addr);            
};

出力:

sin_addr in sockaddr_in  = 0x7fffa26102b4
sin_addr in sockaddr_in6 = 0x7fffa26102b8

これら 2 つの値が同じではないのはなぜですか?

これは同じデータ (接続するアドレス) を指しているため、同じアドレスに配置する必要があります。それ以外の場合、IPv4 または IPv6 がわからない sockaddr_in で inet_ntop を呼び出すにはどうすればよいでしょうか?

4

2 に答える 2

28

これら 2 つの値が同じではないのはなぜですか?

sockaddr_insockaddr_in6は、異なるアドレス ファミリ (それぞれ IPv4 と IPv6) に使用される異なる構造体です。これらは、1 つを除いて、相互に互換性がある必要はありません。最初のフィールドは、アドレス ファミリを保持するために 16 ビット整数でなければなりません。 sockaddr_inはそのフィールドを常に に設定しAF_INETsockaddr_in6常にそのフィールドを に設定しAF_INET6ます。このようにファミリ フィールドを標準化することで、 にsockaddr基づく API はそのフィールドにアクセスでき、必要に応じて残りの構造体データを解釈する方法を認識できます。とはバイト サイズが異なるため、sockaddrベースの API は通常int、入力/出力としてサイズ値も持っている理由でもあります。そのため、API は、渡すバッファーのサイズを検証できる必要があります。sockaddr_insockaddr_in6

これは同じデータ (接続するアドレス) を指しているため、同じアドレスに配置する必要があります。

いいえ、そうすべきではありません。構造体内のアドレス フィールドの位置は、構造体が属するアドレス ファミリのタイプに固有です。およびが正確に同じオフセットにアドレスを格納する必要はsockaddr_inありません。sockaddr_in6

それ以外の場合、IPv4 または IPv6 がわからない sockaddr_in で inet_ntop を呼び出すにはどうすればよいでしょうか?

sockaddr_inIPv4 のみで使用sockaddr_in6でき、IPv6 のみで使用できます。を持っているsockaddr_in場合は、IPv4 アドレスを持っていることsockaddr_in6を暗黙のうちに認識しており、 を持っている場合は、IPv6 アドレスを持っていることを暗黙のうちに認識しています。inet_ntop()渡されたデータを解釈する方法を認識できるように、その情報を指定する必要があります。

struct sockaddr_in sa;
inet_ntop(AF_INET, &(sa.sin_addr), ...);

.

struct sockaddr_in6 sa;
inet_ntop(AF_INET6, &(sa.sin6_addr), ...);

ファミリにとらわれないコードを記述できるようにするには、可能な場合sockaddr_storageは代わりにsockaddr_inまたはsockaddr_in6直接使用する必要があります。 と構造体の両方をsockaddr_storage保持するのに十分なサイズです。どちらの構造体も同じオフセットとサイズでファミリ フィールドを定義するため、ポインタ ( 、、、など)を操作する任意の API で使用できます。sockaddr_insockaddr_in6sockaddr_storagesockaddr*connect()accept()bind()getsockname()getpeername()

ただし、はそのカテゴリに分類されないため、 を使用する場合は手動でinet_ntop()引き離す必要があります。たとえば、次のようになります。sockaddr_storageinet_ntop()

struct sockaddr_storage sa;

switch (sa.ss_family)
{
case AF_INET:
    inet_ntop(AF_INET, &(((sockaddr_in*)&sa)->sin_addr), ...);
    break;
case AF_INET6:
    inet_ntop(AF_INET6, &(((sockaddr_in6*)&sa)->sin6_addr), ...);
    break;
}
于 2012-10-31T21:46:13.507 に答える
3

いいえ、ipv6 の場合は使用する必要があります

in6_addr // is used to store the 128-bit network address

sockaddr_in6

詳細はこちらから参照できます

デュアル スタック、つまり ipv4 と 6 をサポートするコードを記述するには、これを使用します。

于 2012-10-31T11:50:28.710 に答える