Perl IO :: Socket :: INET + IO :: Async :: Stream在断开连接时重新连接到TCP服务器

时间:2017-09-07 00:00:49

标签: perl sockets events tcp

对于我的生活,我似乎无法弄清楚如何在断开连接后获得标准TCP套接字连接以重新连接,特别是在IO :: Async :: Loop

的上下文中

一些基础知识:

#!/usr/bin/perl
use strict;
use warnings;
use Socket;
use IO::Async::Loop;
use IO::Async::Stream;
use IO::Socket;
use Time::HiRes qw(usleep);

# standard event loop
my $loop = IO::Async::Loop->new;

# notification service socket connection; we only write outgoing
my $NOTIFY = IO::Socket::INET->new(
    PeerHost => $a_local_network_host,
    PeerPort => $comm_port,
    Proto => 'tcp',
    Type => SOCK_STREAM,
    ReuseAddr => 1,
    Blocking => 0
) or warn("Can't connect to NOTIFY: $!\n");
setsockopt($NOTIFY, SOL_SOCKET, SO_KEEPALIVE, 1);

# main interface element via IO::Async
my $notifystream = IO::Async::Stream->new(
    handle => $NOTIFY,
    on_read => sub {
        my ( $self, $buffref, $eof ) = @_;
        # here's where we need to handle $eof if the remote goes away
        if($eof) {
            # i have tried several things here
            usleep(200000); # give the remote service some milliseconds to start back up
            # process fails if remote is not back up, so i know the timeout is 'good enough' for this test
            # attempt to reconnect.  have also tried recreating from scratch
            $NOTIFY->connect("$a_local_network_host:$comm_port");
            # this doesn't seem to have any effect
            $self->configure(handle=>$NOTIFY);
        }
    }
);
$loop->add( $notifystream );

# kickstart the event loop
$loop->run;

### -- Meanwhile, elsewhere in the code -- ###

$notifystream->write("data for notification service\n");

实际上,循环中还有很多事情要发生。我还有更复杂的方法来测试套接字关闭或损坏,$ notifystream上的更多错误处理程序,以及重新连接到远程服务的更好的超时/退避,但是这应该显示我正在做的事情的主要症结

当远程服务器因任何原因离开时,我想尝试重新连接它而不会中断系统的其余部分。在大多数情况下,遥控器干净利落地发送eof,因为它故意重启(不是我的选择,只是我必须处理的事情),但我也想处理其他通信错误。

实际上,上面的代码就像它有效一样,但是远程服务不再接收对$ notifystream的进一步写入调用。没有生成错误,$ notifystream很乐意进行进一步的写入,但它们不会被传送到远程。

我有一种感觉,我做错了。我不想重写应用程序的其余事件循环,所以请不要只使用AnyEvent'类型的响应 - 真的希望更好地理解如何重新连接/ reuse /重新创建此处使用的变量(IO :: Socket :: INET和IO :: Async :: Stream)以补偿远程服务器暂时不可用的时间。

欢迎任何有关此目标的建议或参考。谢谢!

- = - = - = - = -

总结我收到(并且没有)收到的错误: 如果我没有留下任何睡眠,由于远程服务不可用,基本套接字的重新连接(或重新创建)将失败。 如果我尝试从头开始重新创建套接字,然后配置'流,我得到的是不能在未定义的'上调用方法sysread。这让我相信套接字没有正确重新创建。 在任何时候都没有内置的流媒体' on_read_error'或者' on_write_error'处理程序触发,无论我用当前代码写入套接字多少,尽管如果我完全破坏套接字,这些将产生错误。 套接字在我知道它关闭后似乎仍然处于活动状态,并且重新连接似乎没有改变任何东西。没有生成错误,但没有写入套接字。

是否有不同的语法重新连接到IO :: Socket :: INET套接字?到目前为止,调用 - > connect()或从头开始重建似乎是封闭连接的唯一选项,似乎都不起作用。

1 个答案:

答案 0 :(得分:1)

您无法多次连接现有套接字。您只能关闭套接字并创建一个新套接字。这与IO :: Socket :: INET,IO :: Async :: Stream甚至Perl无关,但这就是套接字API的工作方式。

详细信息:本地套接字实际上从未断开连接,即它仍配置为从特定本地IP地址和端口以及特定地址和端口发送数据。只是,由于底层TCP连接断开或关闭(即FIN交换),发送将不再起作用。由于没有用于取消绑定和取消连接套接字的API,唯一的方法是关闭它并创建一个未绑定且未连接的新连接,直到调用connect。然后,这个新套接字可能会或可能不会获得与前一个相同的文件描述符。