Почему sendto () через Perl Socket-> send () игнорирует адрес партнера? - PullRequest
1 голос
/ 14 марта 2019

У меня есть программа (слишком сложная, чтобы представить ее здесь), которая использует сокет UDP, созданный IO::Socket::INET->new() с адресами и портами Local... и Peer.... Таким образом, ожидается, что обычный $sock->send($data, $flags) отправит $data на адрес равноправного узла, указанный при создании сокета. Это похоже на работу.

Однако, когда я пытаюсь отправить отдельный пакет в другое место назначения с помощью $sock->send($data, $flags, $dest), $dest, кажется, игнорируется (и пакет отправляется на адрес равноправного сокета). Я добавил множество отладочных сообщений, и параметр $dest правильно передается send, но strace показывает, что sendto() вызывается с 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;
}

Очевидно, что назначение передается как " host : port " (одна строка без пробелов (пробелы из-за недостатков разметки здесь)).

1 Ответ

2 голосов
/ 14 марта 2019

Если вы используете Peer* при создании IO::Socket::INET, будет вызван connect(), который задает имя узла сокета.В таком сокете вам никогда не придется указывать адрес удаленного сокета, потому что он имеет адрес по умолчанию.

IO :: Socket :: send () имеет «функцию»:он игнорирует параметр TO, когда сокет имеет допустимое имя узла:

 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

Strace с моего компьютера Linux указывает, что send()сопоставлен с sendto(), потому что код Perl вызывает send () и sendto () .


UPDATE: Я создал восходящий билет # 133936 .

...