7

私は IPv6 ソケット、特に Windows Vista 以降で提供される「デュアル スタック」機能、および明らかにデフォルトで Unix で提供される機能を試しています。サーバーを特定の IP アドレスまたはローカル マシンのホスト名解決にバインドすると、IPv4 クライアントからの接続を受け入れることができないことがわかりました。ただし、INADDR_ANY にバインドすると、できます。

私のサーバーの次のコードを検討してください。IPv6 ソケットを作成し、IPV6_V6ONLY フラグをゼロに設定するという Microsoft のアドバイスに従っていることがわかります。

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;     // We intend to use the addrinfo in a call to connect().  (I know it is ignored if we specify a server to connect to...)

int nRet = getaddrinfo("powerhouse", "82", &hints, &result);

SOCKET sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);

int no = 0;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&no, sizeof(no)) != 0)
    return -1;

if (bind(sock, result->ai_addr, result->ai_addrlen) ==  SOCKET_ERROR)
    return -1;

if (listen(sock, SOMAXCONN) == SOCKET_ERROR)
    return -1;

SOCKET sockClient = accept(sock, NULL, NULL);

これが私のクライアントのコードです。IPv4 ソケットを作成し、サーバーに接続しようとしていることがわかります。

addrinfo* result, *pCurrent, hints;

memset(&hints, 0, sizeof hints);    // Must do this!
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;

if (getaddrinfo("powerhouse", "82", &hints, &result) != 0)
    return -1;

SOCKET sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
int nRet = connect(sock, result->ai_addr, result->ai_addrlen);

私の接続呼び出しの結果は常に 10061 です: 接続が拒否されました。

サーバー コードを :: にバインドするように変更し (または NULL ホストを getaddrinfo() に渡す (同じこと))、クライアント コードを変更して getaddrinfo() 呼び出しで NULL ホストを指定すると、V4 クライアントは接続できます。大丈夫。

誰でも理由を説明できますか?デュアルソケットの動作が必要な場合は、NULL ホストを指定する必要がある (したがって、INADDR_ANY を使用する) 必要があるということは何も読んでいません。マルチホーム ホストがあり、使用可能な IP の一部でのみ IPv4 を受け入れたいので、これは必須ではありません。

編集 15/05/2013:

これは、コードが失敗する理由について私を混乱させた関連ドキュメントです。

IPv6 Winsock アプリケーション用のデュアルスタック ソケットから

「Windows Vista 以降では、IPv6 と IPv4 の両方のトラフィックを処理できる単一の IPv6 ソケットを作成する機能が提供されています。たとえば、IPv6 用の TCP リッスン ソケットが作成され、デュアル スタック モードになり、ポート 5001 にバインドされます。このデュアルスタック ソケットは、ポート 5001 に接続する IPv6 TCP クライアントからの接続と、ポート 5001 に接続する IPv4 TCP クライアントからの接続を受け入れることができます。」

「デフォルトでは、Windows Vista 以降で作成された IPv6 ソケットは、IPv6 プロトコルでのみ動作します。IPv6 ソケットをデュアルスタック ソケットにするためには、setsockopt 関数を IPV6_V6ONLY ソケット オプションで呼び出して、この値を次のように設定する必要があります。 IPV6_V6ONLY ソケット オプションがゼロに設定されている場合、AF_INET6 アドレス ファミリ用に作成されたソケットを使用して、IPv6 アドレスまたは IPv4 マップ アドレスとの間でパケットを送受信できます(強調鉱山)」

4

2 に答える 2

8

IPv4 と IPv6 は、2 つの別個のプロトコルです。一方のプロトコルのパケットは、他方のプロトコルを使用して処理することはできません。そのため、デュアル スタックの概念が存在します。システムは IPv4 と IPv6 の両方のプロトコル スタックを実行し、IPv4 と IPv6 の両方のアドレスを持っています。

オペレーティング システムには、IPv4 と IPv6 のすべてのアドレスをリッスンする IPv6 ソケットを設定できるトリックがあります。ホストには両方のアドレス ファミリが必要であり、ワイルドカード アドレスにバインドする場合にのみ機能します。そのソケットをもう機能しない固定アドレスにバインドすると、バインドしたアドレスでのみ機能します。

したがって、使用可能なすべてのアドレスでリッスンする場合は、IPV6_V6ONLY を 0 に設定し、ワイルドカード アドレスでリッスンします。::ffff:IPv4 クライアントは、IPv4 アドレスを含む最後の 32 ビットから始まる IPv6 アドレスを使用するものとして表示されます。

特定のアドレスにバインドする場合は、リッスンする各アドレスにバインドされたソケットが必要になります。次に、ieselect(...)を使用してそれらのソケットを監視し、誰かが接続したためにアクティブになったソケットに応答する必要があります。

于 2013-05-10T12:14:13.647 に答える
3

このリンクhttp://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch12lev1sec2.htmlは、IPv4 および IPv6 接続に関する詳細情報を提供します。

ほとんどのデュアルスタック ホストは、リッスン ソケットを処理する際に次のルールを使用する必要があります。

  • リスニング IPv4 ソケットは、IPv4 クライアントからの着信接続のみを受け入れることができます。
  • サーバーに、ワイルドカード アドレスをバインドしたリスニング IPv6 ソケットがあり、IPV6_V6ONLY ソケット オプション (セクション 7.8) が設定されていない場合、そのソケットは、IPv4 クライアントまたは IPv6 クライアントからの着信接続を受け入れることができます。IPv4 クライアントからの接続の場合、接続用のサーバーのローカル アドレスは、対応する IPv4 マップ IPv6 アドレスになります。
  • サーバーに、IPv4 にマップされた IPv6 アドレス以外の IPv6 アドレスをバインドしたリッスン IPv6 ソケットがある場合、またはワイルドカード アドレスをバインドしたが、IPv6_V6ONLY ソケット オプション (セクション 7.8) を設定した場合、そのソケットは IPv6 クライアントからの着信接続を受け入れることができます。それだけ。
于 2015-03-08T10:05:32.433 に答える