0

IPv6アドレスでリッスンすることになっているperlの次のプログラムがあり、理論的には、IPv4(IPv4マップIPv6アドレスを介して)とデュアルスタックボックスのIPv6クライアントの両方にサービスを提供する必要があります。

use Socket;
use Socket6;

@res = getaddrinfo('', 8086, AF_UNSPEC, SOCK_STREAM,0, AI_PASSIVE);
my @ipv6Result;

while(scalar(@res)>=5){
    my @currentResult = @res;
    ($family, $socktype, $proto, $saddr, $canonname, @res) = @res;
    if($family == AF_INET6){
        @ipv6Result = @currentResult;
    }
} 

if(@ipv6Result){
    ($family, $socktype, $proto, $saddr, $canonname) = @ipv6Result;
}

socket(Socket_Handle, $family, $socktype,$proto) || next;
bind(Socket_Handle,$saddr  )    || die "bind: $!";
listen(Socket_Handle, 1)                       || die "listen: $!";
$paddr = accept(Client,Socket_Handle) || die "accept: $!";

これを実行した後、netstat は次の観察結果を示しました。

c:\Perl\bin>netstat -nao | findstr 8086
TCP    [::]:8086              [::]:0                 LISTENING       2892

IPv4 ワイルドカード アドレス (0.0.0.0) ではなく、IPv6 ワイルドカード アドレス (::) のみをリッスンしているようです。IPv4 クライアントからこのサーバー プロセスに接続することはできませんでしたが、IPv6 クライアントを介して接続することはできました。

次のようにJavaで同様のサーバープログラムを試しました(同じセットアップで):

import java.net.ServerSocket;
public class CodeTCPServer {
    public static void main(String[] args) throws Exception{
        new ServerSocket(8086).accept();
    }
}

これに対する netstat の出力は次のとおりです。

C:\Users\Administrator>netstat -nao | findstr 8086
TCP    0.0.0.0:8086           0.0.0.0:0              LISTENING       3820
TCP    [::]:8086              [::]:0                 LISTENING       3820

IPv6 と IPv4 の両方でリッスンしているようで、IPv4 および IPv6 クライアントから接続することもできます。

Linux ボックスで同じ perl プログラムを実行すると、正常に動作し、IPv4 および IPv6 クライアントを介して接続できます。

Windows の何かが perl プログラムが IPv4 と IPv6 の両方でリッスンするのを止めているのではないかと思います (ただし、同じ理由で Java プログラムも停止しているはずです)。プログラムロジックに問題がある場合、Linuxでも機能しないはずです。

(私は今のところ Socket6 を使用しています。Windows 上でどういうわけか perl の組み込みの IPv6 サポートを使用できなかったため、作成者と連絡を取り、自分のセットアップで動作させるようにしています)

アップデート:

私はちょうど次のことを試しました:

setsockopt (Socket_Handle, IPPROTO_IPV6, IPV6_V6ONLY, 0 ) or print("\nFailed to set IPV6_V6ONLY $! ");

ソケット オプションのデフォルト値は 1 (このプラットフォームの場合) であり、手動でオーバーライドする必要があると予想されますが、残念ながら! 次のエラーが発生しました:

ベンダーは、c:\socket6\Socket6Server.pl 行 66 で使用されるソケット マクロ IPV6_V6ONLY を定義していません

ここで、「ベンダー」とは何を意味するのだろうか、それは Socket6 モジュール / perl ベンダーまたは OS ベンダーですか?

更新2

答えはhttp://metacpan.org/pod/IO::Socket::IP (V6Only 引数の場合) にあると思います。

プラットフォームがこのオプションの無効化をサポートしていないが、それでも AF_INET 接続と AF_INET6 接続の両方をリッスンしたい場合は、各プロトコルに 1 つずつバインドされた 2 つのリッスン ソケットを作成する必要があります。

そして、これは私のために働いた!しかし、プラットフォームがV6Onlyの無効化をサポートしているかどうかを確認する必要があります(私のプログラムのプロトコル認識コード:()、Javaと比較すると、Javaは自動的にそれを行います(2つのソケットを確認して作成します)。

4

1 に答える 1

1

これには、BIND_V6ONLYソケット オプションをオフにする必要があります。詳しいやり方はIO::Socket::IPソースを見てください。


また、コメントへのお返事として

私は今のところ Socket6 を使用しています。Windows では perl の組み込みの IPv6 サポートを使用できなかったため、作成者と連絡を取り合って自分のセットアップで動作させるようにしています)

メモリが機能する場合、それは厳密には真実ではありません。問題がありましIO::Socket::IPたが、プレーンなSocketものはすべて正常に機能するはずです。2.006 には既にすべての機能があるSocket6ため、使用する必要はありません。Socketコードを次のものに置き換えることができます。

use Socket qw( :addrinfo SOCK_STREAM AF_INET6 );

my ($err, @res) = getaddrinfo('', 8086,
   { socktype => SOCK_STREAM, flags => AI_PASSIVE });
my $ipv6Result;

my $current;
while(@res){
    $current = shift @res;
    if($current->{family} == AF_INET6) {
        $ipv6Result = $current;
    }
}

if($ipv6Result) {
    $current = $ipv6Result;
}

socket(my $sock, $current->{family}, $current->{socktype}, $current->{proto}) or next;
bind(my $sock ,$current->{addr}) or die "bind: $!";
listen(my $sock, 1) or die "listen: $!";

my $paddr = accept(my $client, $sock) or die "accept: $!";
于 2012-08-23T12:38:52.107 に答える