perl - 为什么通过 Perl Socket->send() 的 sendto() 会忽略对等地址?
问题描述
IO::Socket::INET->new()
我有一个程序(太复杂,无法在这里展示),它使用由两者Local...
以及Peer...
地址和端口创建的 UDP 套接字。所以期望一个普通的$sock->send($data, $flags)
将发送$data
到创建套接字时指定的对等地址。这似乎行得通。
但是,当我尝试使用 将单个数据包发送到不同的目的地时$sock->send($data, $flags, $dest)
,$dest
似乎被忽略了(并且数据包正在发送到套接字的对等地址)。我添加了许多调试消息,并且参数$dest
正确传递给send
,但strace
显示使用for和forsendto()
调用。NULL
sockaddr
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;
}
所以很明显,目的地是作为“主机 : 端口”(一个没有空格的字符串(空格是由于此处的标记不足))传递的。
解决方案
如果Peer*
在创建时使用IO::Socket::INET
thenconnect()
将被调用,它设置套接字的 peername。在这样的套接字上,您永远不必指定远程套接字地址,因为它有一个默认地址。
IO::Socket::send()TO
有一个“特性”:当套接字具有有效的 peername 时,它会忽略该参数:
my $r = defined(getpeername($sock))
? send($sock, $_[1], $flags)
: send($sock, $_[1], $flags, $peer);
该更改是在IO 1.12中引入的,不幸的是,它早于当前git 存储库的历史:
修改了 IO::Socket::send 以便在套接字连接时不传递 4 个参数来发送
如果您不喜欢这种行为,则必须改用 raw 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() 和 sendto()。
更新:我已经创建了上游票 #133936。
推荐阅读
- reactjs - 如果登录后我有来自后端的令牌,如何在导航栏上显示用户名和注销选项?
- ios - Swift 包管理器使包依赖于远程 XCFramework
- android - 小米米 A1(和其他)上的 USB 接口
- javascript - extraReducers 执行的顺序是什么,在 reducer 之前还是之后?
- upgrade - 我无法在未登录的情况下查看 sitefinity 前端网页?
- powershell - 压缩归档任务在 SSIS 中失败,文件为 1.25GB
- javascript - 是导出顶级变量引用 javascript 中的副作用
- typescript - 如何以编程方式获取变量的类型并将其用作函数参数的类型?
- c - 使用 vcpkg 和 VisualStudio 时 SDL2 的链接器错误
- sql - 错误 1054 (42S22):尝试通过 cmd 在 sql 中创建序列时,“字段列表”中的未知列“sq.nextval”