【问题标题】:Proper use of IO::Socket::INET for TCP client in perl在 perl 中为 TCP 客户端正确使用 IO::Socket::INET
【发布时间】:2014-07-25 15:17:15
【问题描述】:

我有一个关于应该如何使用 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 子中)。

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

谢谢!

【问题讨论】:

    标签: perl sockets tcp


    【解决方案1】:

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

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

      如果远程端是“localhost”,如上面的示例脚本中所示,那么这不太可能是一个问题,实际上在这种情况下,我还是建议使用 Unix 域套接字。

    • 远程端是否发回任何东西?如果任何一方可能有异步消息要发送,则管理零星连接会更加困难。听起来你的情况并非如此。

    • 通过保持连接打开,您是否会占用大量资源?

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

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

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

    这有几个好处 - 它会导致内核定期在您的连接上发送 keepalive 消息,即使一切都很安静,这本身就足以让一些防火墙保持正常运行。此外,如果您的连接确实断开,您的程序可以立即发现,而不是下次您要写入它时(尽管您可能不会注意到它,除非您定期检查套接字上的错误)。

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

    This question 也可能对您有用。

    【讨论】:

    • 开销无关紧要。都是局域网,每10分钟可能不到一条消息。远端没有发回任何东西。没有什么重要的东西被用来做这件事;我看不出有什么理由不让连接保持打开状态。我的部分问题是我无法访问此连接的另一端,因此必须使用我所拥有的来解决掉线问题。您的回答非常有帮助,我将尝试使用 keepalive 选项,并在今天晚些时候回来查看。再次感谢。
    • 似乎奏效了,从那以后就再也没有掉过。感谢您的出色回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2012-03-08
    • 1970-01-01
    • 2015-01-27
    • 1970-01-01
    相关资源
    最近更新 更多