我在 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 个套接字)。