0

我在 perl 中有以下程序,它应该监听 IPv6 地址,理论上应该同时服务于双堆栈盒上的 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

看来,它只监听 IPv6 通配符地址 (::) 而不是 IPv4 通配符地址 (0.0.0.0)。我无法从 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(对于这个平台),我必须手动覆盖它,但是唉!我收到以下错误:

您的供应商尚未定义套接字宏 IPV6_V6ONLY,用于 c:\socket6\Socket6Server.pl 第 66 行

现在我想知道“供应商”是什么意思,是 Socket6 模块/perl 供应商还是操作系统供应商?

更新2

我认为答案在http://metacpan.org/pod/IO::Socket::IP中给出(对于 V6Only 参数)

使用以下几行:如果您的平台不支持禁用此选项,但您仍想同时侦听 AF_INET 和 AF_INET6 连接,则必须创建两个侦听套接字,一个绑定到每个协议。

这对我有用!但是我需要检查平台是否支持 V6Only 禁用(我的程序中的协议感知代码:(),与 Java 相比,Java 会自动为我执行此操作(检查并创建 2 个套接字)。

4

1 回答 1

1

这需要BIND_V6ONLY关闭套接字选项。有关方法的详细信息,请参阅IO::Socket::IP源代码。


另外,为了回应您的评论

我现在使用的是 Socket6,因为我无法在 Windows 上以某种方式使用 perl 对 IPv6 的内置支持,我正在与作者沟通以使其在我的设置中工作)

如果没有记错的话,这并不完全正确。您遇到了麻烦,IO::Socket::IP但简单的Socket东西应该都可以正常工作。您不需要使用Socket6,因为Socket2.006 已经具备所有功能。您可以将代码替换为:

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 回答