为什么通过 Perl Socket->send() 的 sendto() 会忽略对等地址?
Why does sendto() via Perl Socket->send() ignore the peer address?
我有一个程序(太复杂了,无法在此处展示)使用由 IO::Socket::INET->new()
创建的 UDP 套接字,同时具有 Local...
和 Peer...
地址和端口。所以期望是一个普通的 $sock->send($data, $flags)
将发送 $data
到创建套接字时指定的对等地址。这似乎有效。
然而,当我尝试使用 $sock->send($data, $flags, $dest)
将单个数据包发送到不同的目的地时,$dest
似乎被忽略了(并且数据包正在发送到套接字的对等地址)。我添加了很多调试消息,并且参数 $dest
被正确传递给 send
,但是 strace
显示 sendto()
是用 NULL
调用的 [=23] =] 和 0
对于 socklen
.
perldoc -f send
对我没有帮助。那么为什么目标地址被忽略了呢?
根据要求,这是(有点冗长)send_packet代码:
$RE_saddr = qr/^(.+):(\d+)$/;
sub send_packet($$;$)
{
my ($sock, $packet, $dest) = @_;
my @params = ($packet, 0);
if (defined($dest)) {
if (my ($addr, $port) = $dest =~ $RE_saddr) {
if (my $addr_bin = inet_aton($addr)) {
if (defined($dest = sockaddr_in($port, $addr_bin))) {
push(@params, $dest);
} else {
warn "bad address or socket for $addr:$port";
}
} else {
warn "bad address $addr";
}
} else {
warn "bad destination address $dest";
}
}
return 1
if ($sock->send(@params));
return undef;
}
所以很明显目的地被传递为“host : port”(一个字符串没有空格(这里的空格是由于加价不足造成的)。
如果在创建 IO::Socket::INET
时使用 Peer*
,则将调用 connect()
,这会设置套接字的对等名称。在这样的套接字上,您永远不必指定远程套接字地址,因为它有一个默认地址。
IO::Socket::send() 有一个 "feature":当套接字具有有效的对等名称时,它会忽略 TO
参数:
my $r = defined(getpeername($sock))
? send($sock, $_[1], $flags)
: send($sock, $_[1], $flags, $peer);
IO 1.12, which unfortunately predates the history of the current git repository 中引入了该更改:
Modified IO::Socket::send so not to pass 4 arguments to send
if the socket is connected
如果您不喜欢这种行为,则必须改用原始 CORE::send()
,即:
#!/usr/bin/perl
use warnings;
use strict;
use IO::Socket::INET;
use Socket qw(pack_sockaddr_in);
my $client = IO::Socket::INET->new(
PeerAddr => '127.0.0.1',
PeerPort => 2000,
Proto => 'udp',
) or die "client socket: $!\n";
my $addr = pack_sockaddr_in(2000, inet_aton('127.0.0.1'));
$client->send('ABCD', 0)
or die "IO::Socket::send() no addr: $!\n";
$client->send('ABCD', 0, $addr)
or die "IO::Socket::send() with addr: $!\n";
send($client, 'ABCD', 0, $addr)
or die "send() with addr: $!\n";
exit 0;
测试运行:
$ strace -e sendto,connect,socket perl dummy.pl
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 4
来自我的 Linux 机器的 strace 表明 send()
被映射到 sendto()
,因为 Perl 代码确实调用了 send() and sendto().
更新: 我创建了 upstream ticket #133936.
我有一个程序(太复杂了,无法在此处展示)使用由 IO::Socket::INET->new()
创建的 UDP 套接字,同时具有 Local...
和 Peer...
地址和端口。所以期望是一个普通的 $sock->send($data, $flags)
将发送 $data
到创建套接字时指定的对等地址。这似乎有效。
然而,当我尝试使用 $sock->send($data, $flags, $dest)
将单个数据包发送到不同的目的地时,$dest
似乎被忽略了(并且数据包正在发送到套接字的对等地址)。我添加了很多调试消息,并且参数 $dest
被正确传递给 send
,但是 strace
显示 sendto()
是用 NULL
调用的 [=23] =] 和 0
对于 socklen
.
perldoc -f send
对我没有帮助。那么为什么目标地址被忽略了呢?
根据要求,这是(有点冗长)send_packet代码:
$RE_saddr = qr/^(.+):(\d+)$/;
sub send_packet($$;$)
{
my ($sock, $packet, $dest) = @_;
my @params = ($packet, 0);
if (defined($dest)) {
if (my ($addr, $port) = $dest =~ $RE_saddr) {
if (my $addr_bin = inet_aton($addr)) {
if (defined($dest = sockaddr_in($port, $addr_bin))) {
push(@params, $dest);
} else {
warn "bad address or socket for $addr:$port";
}
} else {
warn "bad address $addr";
}
} else {
warn "bad destination address $dest";
}
}
return 1
if ($sock->send(@params));
return undef;
}
所以很明显目的地被传递为“host : port”(一个字符串没有空格(这里的空格是由于加价不足造成的)。
如果在创建 IO::Socket::INET
时使用 Peer*
,则将调用 connect()
,这会设置套接字的对等名称。在这样的套接字上,您永远不必指定远程套接字地址,因为它有一个默认地址。
IO::Socket::send() 有一个 "feature":当套接字具有有效的对等名称时,它会忽略 TO
参数:
my $r = defined(getpeername($sock))
? send($sock, $_[1], $flags)
: send($sock, $_[1], $flags, $peer);
IO 1.12, which unfortunately predates the history of the current git repository 中引入了该更改:
Modified IO::Socket::send so not to pass 4 arguments to send if the socket is connected
如果您不喜欢这种行为,则必须改用原始 CORE::send()
,即:
#!/usr/bin/perl
use warnings;
use strict;
use IO::Socket::INET;
use Socket qw(pack_sockaddr_in);
my $client = IO::Socket::INET->new(
PeerAddr => '127.0.0.1',
PeerPort => 2000,
Proto => 'udp',
) or die "client socket: $!\n";
my $addr = pack_sockaddr_in(2000, inet_aton('127.0.0.1'));
$client->send('ABCD', 0)
or die "IO::Socket::send() no addr: $!\n";
$client->send('ABCD', 0, $addr)
or die "IO::Socket::send() with addr: $!\n";
send($client, 'ABCD', 0, $addr)
or die "send() with addr: $!\n";
exit 0;
测试运行:
$ strace -e sendto,connect,socket perl dummy.pl
socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP) = 4
connect(4, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 0
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, NULL, 0) = 4
sendto(4, "ABCD", 4, 0, {sa_family=AF_INET, sin_port=htons(2000), sin_addr=inet_addr("127.0.0.1")}, 16) = 4
来自我的 Linux 机器的 strace 表明 send()
被映射到 sendto()
,因为 Perl 代码确实调用了 send() and sendto().
更新: 我创建了 upstream ticket #133936.