在perl中正确使用IO :: Socket :: INET for TCP客户端

时间:2014-07-25 04:37:59

标签: perl sockets tcp

我有一个关于如何使用IO :: Socket的问题;我有一个应该不断运行的脚本,监视Asterisk服务器的某些事件。当这些事件发生时,脚本通过TCP套接字将事件中的数据发送到另一个服务器。我偶尔发现插座会关闭。我的问题是我是否应该使用单个套接字,并保持它永远打开(并弄清楚为什么+阻止它关闭),或者我应该为发出的每一位数据打开和关闭一个新的套接字?
我对这类事情的体验非常小,而且我没有找到我正在寻找的答案就阅读了所有文档。以下是我到目前为止的一个示例:

#!/usr/bin/perl
use Asterisk::AMI;
use IO::Socket;
use strict;
use warnings;

my $sock = new IO::Socket::INET (
  PeerAddr => '127.0.0.1',
  PeerPort => '1234',
  Proto => 'tcp',
);

sub newchannel {
  my ($ami, $event) = @_;

  if ($event->{'Context'} eq "from-trunk") {

    my $unique_id = $event->{'Uniqueid'};
    my $this_call = $call{$unique_id};

    $this_call->{caller_name} = $event->{'CallerIDName'};
    $this_call->{caller_number} = $event->{'CallerIDNum'};
    $this_call->{dnis} = $event->{'Exten'};

    $call{$unique_id} = $this_call;
  };

}

sub ringcheck {
  my ($ami, $event) = @_;

  if ($event->{SubEvent} eq 'Begin') {
    my $unique_id = $event->{UniqueID};
    if (exists $call{$unique_id}) {

      my $this_call = $call{$unique_id};

      $this_call->{system_extension} = $event->{Dialstring};
      $this_call->{dest_uniqueid}  = $event->{DestUniqueID};


      printf $sock "R|%s|%s|%s||%s\n",
        $this_call->{caller_name},
        $this_call->{caller_number},
        $this_call->{system_extension},
        $this_call->{dnis};

      $this_call->{status}  = "ringing";
  }
}

还有更多的东西,但这表明我觉得我应该开始/停止一个新的套接字(在ringcheck sub中)。

如果您需要我澄清或添加任何内容,请告诉我。

谢谢!

1 个答案:

答案 0 :(得分:1)

是否更好地为每条消息建立新连接或保持连接打开取决于以下几个因素:

  • 与建立连接相关的开销是否显着?这取决于诸如需要发送消息的频率以及网络连接的质量等因素。

    如果远程端是'localhost',就像上面的示例脚本一样,那么这不太可能是一个问题,事实上在这种情况下我建议使用Unix域套接字。无论如何。

  • 远程端是否发回了什么?如果任何一方可能有异步消息要发送,那么管理偶发连接要困难得多。听起来不像是你的情况。

  • 通过保持连接打开,您是否有任何重要资源?

请注意,我不认为随机连接丢失是每次争论建立新连接的好理由。如果可能,最好在任何情况下诊断该问题。否则,无论采取何种方法,您都可能获得不可靠的表现。

根据我的经验,在持久的TCP连接中看似随机丢失的一个非常常见的原因是中间跟踪防火墙。如果这些防火墙在一段时间内没有看到任何活动,就会丢弃连接,以节省自己的资源。我在一些工具中使用的一种解决方法是在套接字上设置套接字选项SO_KEEPALIVE,如下所示:

use Socket;
...
setsockopt($sock, SOL_SOCKET, SO_KEEPALIVE, 1);

这有几个好处 - 它会导致内核定期在您的连接上发送keepalive消息,即使一切都很安静,这本身就足以让一些防火墙感到满意。此外,如果您的连接断开,您的程序可以立即找到而不是下次要写入它(虽然您可能不会注意到它,除非您经常检查插槽上的错误)。

也许您最好的方法可能是设置SO_KEEPALIVE,并保持套接字打开,但每次尝试写入时都要检查错误,如果有错误,请关闭并重新打开连接。

This question也可能对您有用。