我有这个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
发送任何内容。为什么呢?
答案 0 :(得分:2)
简答
您的问题是您没有处理$udpSocket->recv(...)
错误,这会让您的send
更加混乱。
recv
很可能因ECONNREFUSED而失败。您希望忽略此错误并重试,并对send
调用执行相同的操作。
更长的答案
为了澄清,您对send
的调用不会返回错误“无法确定对等地址” - 它是croak
的消息。
IO :: Socket在成功调用$io_socket->recv()
后记住远程对等地址。此对等方是下一次调用$io_socket->send()
的隐含目标。但是,不成功 recv
不会留下可用的对等地址,并且以下send
只需要croak
来获取目的地。
(由于recv
缓冲区$clientQuery
在recv
失败后未被修改,因此您的代码不会阻塞。您正在再次处理先前的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});
}