IO :: Socket :: INET"发送:无法确定对等地址"

时间:2014-06-07 16:48:27

标签: perl sockets

我有这个Perl代码片段:

my $serverSocket = new IO::Socket::INET(
    LocalPort  => 53,
    Proto      => 'udp',
    ReuseAddr  => 1,
) or Error("Could't open server socket.");
print "Server started.\n";
my $clientQuery;
while (1) {
    $serverSocket->recv($clientQuery, 1024, 0);
    my $clientQueryParsed = IncomingDNSPacket::parseDNSPacket($clientQuery);
    my @questions = @{ $clientQueryParsed->{questions} };
    my @answers = map {getAnswer($_, $forwarder)} @questions;
    my $errorCode = @answers == 0 ? 3 : '';
    my $clientResponseParsed = {
        errorCode     => $errorCode,
        id            => $clientQueryParsed->{id},
        questions     => \@questions,
        answerRRs     => \@answers,
        additionalRRs => [],
        authorityRRs  => [],
    };
    my $clientResponse = OutgoingDNSPacket::makeAnswer(%$clientResponseParsed);
    $serverSocket->send($clientResponse, 0);
}

有时$serverSocket->send($clientResponse, 0);会返回此错误:send: Cannot determine peer address。我试图谷歌它,发现这种情况可能是因为远程主机关闭了套接字。但这不是TCP,这是UDP。为什么我会收到此错误?

编辑:此处的getAnswer代码:

sub getAnswer {
    my $question = shift;
    my $forwarder = shift;
    my @answer;
    @answer = $cache -> get($question);
    if (@answer == 0) {
        my $forwarderPacket = getAnswerFromForwarder($question);
        $cache -> add ($forwarderPacket);
        @answer = $cache -> get($question);
    }
    return @answer;
}

sub getAnswerFromForwarder {
    my $question = shift;
    my $forwQuery = OutgoingDNSPacket::makeQuestion(id => 0, questions => [$question]);
    my $forwSock = new IO::Socket::INET (
            PeerAddr => $forwarder,
            PeerPort => 53,
            Proto => 'udp'
        ) or Error("Couldn't create forwarder socket");
    $forwSock -> send($forwQuery, 0);
    my $select = new IO::Select;
    $select -> add ($forwSock);
    my @socks = $select -> can_read(2);
    if (@socks > 0) {
        my $forwAnswer;
        $forwSock -> recv($forwAnswer, 1024, 0);
        my $forwUnpacked = IncomingDNSPacket::parseDNSPacket($forwAnswer);
        return $forwUnpacked;
    } else {
        return {
            errorCode => 3,
            questions => [],
            answerRRs => [],
            authorityRRs => [],
            additionalRRs => []
            };
    }
}

$forwSock' send可能不成功。如果不成功,我无法从$serverSocket发送任何内容。为什么呢?

1 个答案:

答案 0 :(得分:2)

简答

您的问题是您没有处理$udpSocket->recv(...)错误,这会让您的send更加混乱。

recv很可能因ECONNREFUSED而失败。您希望忽略此错误并重试,并对send调用执行相同的操作。

更长的答案

为了澄清,您对send的调用不会返回错误“无法确定对等地址” - 它是croak的消息。

IO :: Socket在成功调用$io_socket->recv()后记住远程对等地址。此对等方是下一次调用$io_socket->send()的隐含目标。但是,不成功 recv不会留下可用的对等地址,并且以下send只需要croak来获取目的地。

(由于recv缓冲区$clientQueryrecv失败后未被修改,因此您的代码不会阻塞。您正在再次处理先前的recv d数据包。 )

现在,因为这是UDP ,潜在的错误可能与先前失败的send有关。很可能你发送了一个数据报,ICMP“dest unreach”在以后的某个时间回来了,并且套接字上的后续send / recv调用报告为ECONNREFUSED。

伪编码答案

use Errno;    # for %!
#use caution; this is untested and not thoroughly thought through

while (1) {
  my $ok = $udpSocket->recv($buf, $len, 0);
  if (! $ok) {
    redo if $!{ECONNREFUSED};  # Error from a previous UDP send
    die "recv: $!\n";
  }

  ...

  do {
    $ok = $udpSocket->send($buf, 0);
  } while (! $ok and $!{ECONNREFUSED});
}