6

このgetaddrinfo()関数は、クライアント プログラムが特定のホストへのソケットを作成するための正しいデータを効率的に見つけることを可能にするだけでなく、サーバーが正しいソケットにバインドすることも可能にします (理論上)。

私はそれについて学び、 Pythonを介してそれをいじり始めました:

from socket import *
for i in getaddrinfo(None, 22, AF_UNSPEC, SOCK_STREAM, IPPROTO_IP, AI_PASSIVE): i

収量

(2, 1, 6, '', ('0.0.0.0', 22))
(10, 1, 6, '', ('::', 22, 0, 0))

何か問題があるのではないかと思います。

これらの回答に対して正確に何をすればよいのでしょうか? するべきか

  • これらすべての回答の ing ソケットを作成するlisten()必要がありますか?
  • 本当に機能する最初のものを選ぶだけですか?

マンページの例では、最初のものだけを取得し、エラーがなければ満足することをお勧めしますが、私の例では IPv4 経由でしか接続できません。

しかし、それらすべてを試してみると、特定の条件 (OS、ソケットフラグなど) が満たされた場合に IPv6 サーバーソケットも IPv4 をリッスンするため、2 つのサーバーソケットを心配する必要があります。

どこが間違っていると思いますか?


編集:明らかに、私は間違っているとは考えていませんが、私のPCは間違ったことをしています. /etc/gai.conf私はOpenSUSE に同梱されているデフォルトを使用しています。誰かが私を正しい方向に向けることができれば幸いです。

編集 2: 与えられたケースでstraceは、読み取り後に内部的に行われた次の呼び出しを与えます/etc/gai.conf(現在、ポート 22 を使用すると何らかの悪影響がある可能性があると考えたため、ポート 54321 を使用しますが、そうではありませんでした):

socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP) = 3
connect(3, {sa_family=AF_INET6, sin6_port=htons(54321), inet_pton(AF_INET6, "::", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, 28) = 0
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(38289), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
connect(3, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(54321), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
getsockname(3, {sa_family=AF_INET6, sin6_port=htons(60866), inet_pton(AF_INET6, "::ffff:127.0.0.1", &sin6_addr), sin6_flowinfo=0, sin6_scope_id=0}, [28]) = 0
close(3)                                = 0

明らかに、決定は呼び出しの結果に従って行われることを意図していgetsockname()ます...

ところで: https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/673708およびそこに記載されている他のバグレポートは、私の観察を裏付けています。そこにいる何人かの人々は、新しい動作が正しいと主張しているので、私は明らかにAF_INET6... の使用に固執しています:-(

4

2 に答える 2

3

何らかのgetaddrinfo理由で間違った結果を返しています。最初に IPv6 ソケットを返すことになっています。私が考えることができる唯一のことは、OSがシステムの優先順位が低いIPv6(6to4またはTeredo)を検出し、それらを回避する場合です。その場合、IMOは間違っています。編集:自分のコンピューターが同じことをしていることに気付きました.6to4を使用しています.

ただし、両方を聞くことも、AF_INET6代わりに を使用することもできますAF_UNSPEC。次に、setsockopt を実行して無効にすることができますIPV6_V6ONLY

getaddrinfo はここで妥当な処理を行い、該当するすべての結果を返します (ただし、前述のように順序が間違っています)。アプリケーションによっては、1 つまたは 2 つのリッスン ソケットが有効な方法です。

于 2011-11-13T19:47:29.550 に答える
0

JFTR:マンページに記載されているプログラムが間違っているようです。

両方のIPタイプをリッスンするための2つの可能なアプローチがあります。

  1. IPv6ソケットのみを作成し、v6のみのフラグをオフにします。

    from socket import *
    s = socket(AF_INET6, SOCK_STREAM)
    s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0)
    s.bind(...)
    

    それぞれ

    from socket import *
    ai = getaddrinfo(None, ..., AF_INET6, SOCK_STREAM, 0, AI_PASSIVE)[0]
    s = socket(ai[0], ai[1], ai[2])
    s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 0)
    s.bind(ai[4])
    

    長所:

    • 取り扱いが簡単

    短所:

    • XP(AFAIK)では機能しません-2つの異なるプロトコルスタックがあります
  2. 2つのソケットで動作し、v6onlyフラグをオンにします。

    from socket import *
    aii = getaddrinfo(None, ..., AF_UNSPEC, SOCK_STREAM, 0, AI_PASSIVE)
    sl = []
    for ai in aii:
        s = socket(ai[0], ai[1], ai[2])
        if ai[0] == AF_INET6: s.setsockopt(IPPROTO_IPV6, IPV6_V6ONLY, 1)
        s.bind(ai[4])
        sl.append(s)
    

    sl受け入れループですべてのソケットを処理します(select()これを行うには、IOを使用または非ブロッキングにします)

    長所:

    • (ほぼ)プロトコルに依存しない処理を使用しますgetaddrinfo()
    • XPでも動作します

    短所:

    • 扱いが複雑
于 2011-11-13T23:05:59.360 に答える