Sockaddr_inはIPv4用で、sockaddr_in6はIPv6用です。私にとって混乱は、sockaddrとsockaddr_in [6]の違いです。
一部の関数はsockaddr
を受け入れ、一部の関数は_sockaddr_in
_または_sockaddr_in6
_を受け入れます。したがって、
sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in)
であるため。
たとえば、ソケットがあり、そのIPアドレスの文字列IPアドレス(ipv4またはipv6の場合もあります)を取得します。
最初にgetsockname
を呼び出してaddr
を取得し、次に_inet_ntop
_に基づいて_addr.sa_family
_を呼び出します。
このコードスニペットに問題はありますか?
_char ipStr[256];
sockaddr_in6 addr_inv6;
sockaddr* addr = (sockaddr*)&addr_inv6;
sockaddr_in* addr_in = (sockaddr_in*)&addr_inv6;
socklen_t len = sizeof(addr_inv6);
getsockname(_socket, addr, &len);
if (addr->sa_family == AF_INET6) {
inet_ntop(addr_inv6.sin6_family, &addr_inv6.sin6_addr, ipStr, sizeof(ipStr));
// <<<<<<<<IS THIS LINE VALID, getsockname expected a sockaddr, but we use
// it output parameter as sockaddr_in6.
} else {
inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipStr, sizeof(ipStr));
}
_
質問に答えたくありません。しかし、ここで他の人に役立つかもしれないより多くの情報を提供するために、私の質問に答えることにしました。
linux
のソースコードを調べた後。以下は私の発見です。すべてがgetsockname
を実装する可能性のある複数のプロトコルがあります。そして、それぞれにアドレスデータ構造があります。たとえば、IPv4では_sockaddr_in
_、IPV6では_sockaddr_in6
_、_sockaddr_un
_ソケットでは_AF_UNIX
_です。 sockaddr
は、これらのAPIの署名の共通データストラットとして使用されます。
これらのAPIは、memcpyによって別のパラメーターlength
に基づいて、socketaddr_inまたはsockaddr_in6またはsockaddr_unをsockaddrにコピーします。
そして、すべてのデータ構造は、同じタイプのフィールドsa_familyで始まります。
これらの理由に基づいて、コードスニペットは有効です。なぜなら、_sockaddr_in
_と_sockaddr_in6
_の両方に_sa_family
_があり、_sa_family
_を確認した後、使用するために正しいデータ構造にキャストできるからです。 。
BTY、sockaddrのサイズに基づいてメモリを割り当てるsizeof(sockaddr_in6) > sizeof(sockaddr)
がipv6(エラーが発生しやすい)に十分でない理由はわかりませんが、これは履歴上の理由によると思います。
_sockaddr_in
_と_sockaddr_in6
_は両方とも構造で、最初のメンバーはsockaddr
構造です。
C標準によれば、構造体とその最初のメンバーのアドレスは同じであるため、sockaddr
へのポインターでsockaddr_in(6)
へのポインターをキャストできます。
パラメーターとしてsockaddr_in(6)
をとる関数はsockaddr
部分を変更する場合があり、パラメーターとしてsockaddr
をとる関数はその部分のみを考慮します。
それは少し継承に似ています。