2017-09-07 47 views
1

对于我来说,我似乎无法弄清楚如何获得一个标准的TCP套接字连接后重新连接断开,尤其是在IO ::异步::环Perl IO :: Socket :: INET + IO :: Async :: Stream在断开连接时重新连接到TCP服务器

一些基本的方面:

#!/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"); 

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

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

在实践中,上述代码的工作方式就像它的工作原理一样,但远程服务不再接收对$ notifystream的进一步写入调用。没有错误产生,$ notifystream愉快地进一步写入,但他们不被传送到远程。

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

欢迎对此目标提出任何建议或参考。谢谢!

- = - = - = - = -

总结错误我有(并且没有)接收: 如果我不留usleep,基座插座由于将失败的重新连接(或娱乐)远程服务不可用。 如果我试图从头开始重新创建套接字,然后'配置'流,我得到'不能调用未定义的方法sysread这导致我相信套接字没有正确重新创建。 在任何时候,无论我使用当前代码写入套接字的时间多少,流的内置'on_read_error'或'on_write_error'处理程序都会触发,但如果我完全销毁套接字,则会生成一个错误。 套接字似乎仍然是活跃的,我知道它已经关闭,重新连接似乎并没有改变任何东西。没有错误产生,但套接字没有被写入。

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

+0

我没有看到任何错误检查?你提到你有他们,他们说什么?你肯定需要“重新启用”重新启动,这些错误将有助于弄清楚什么。 – zdim

+0

我在IO :: Async :: Stream中有'on_read_error','on_write_error'和'on_writeable_stop' - 它们都没有触发。在所有。我也试过/ catch'd所有 - 问题是没有引发错误。它只是不起作用。 – derelict

+0

你尝试重新连接你的'$ NOTIFY'吗?这个问题还不清楚。你可能也可能不需要再向环路添加'(也许重新启动它)。你能澄清你是否尝试过,你能否用这些错误检查方法所说的信息来完成这个问题? – zdim

回答

1

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

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

相关问题