2

I have a question about how I should be using IO::Socket; I have a script that should run constantly, monitoring an Asterisk server for certain events. When these events happen, the script sends data from the event off to another server via a TCP socket. I've found that occasionally, the socket will close. My question is whether I should be using a single socket, and keep it open forever (and figure out why + prevent it from closing), or should I open and close a new socket for each bit of data sent out?
My experience with this sort of thing is very minimal, and I've read all the documentation without finding the answer I'm looking for. Below is a sample of what I've got so far:

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

There's a bit more to it than that, but this shows where I feel I should be starting/stopping a new socket (within the ringcheck sub).

Let me know if you need me to clarify or add anything.

Thanks!

tsz
  • 307
  • 4
  • 18

1 Answers1

2

Whether it is better establish a new connection for each message or to keep the connection open depends on a few factors:

  • Is the overhead associated with establishing connections significant? This depends on factors such as the frequency with which messages need to be sent, and the quality of the network connection.

    If the remote end is 'localhost', as in your sample script above, then this is not likely to be an issue, and in fact in that case I would recommend using a Unix domain socket instead anyway.

  • Is the remote end sending anything back? Much harder to manage sporadic connections if either side may have asynchronous messages to send. Does not sound like it is the case for you though.

  • Are there any significant resources which you would be holding up by keeping the connection open?

Note that I don't consider random connection dropouts are a good reason to argue for making a new connection each time. If possible, better to diagnose that problem in any case. Otherwise, you might get unreliable performance no matter what approach you take.

In my experience a very common reason for seemingly random dropouts in long held TCP connections is intermediate tracking firewalls. Such firewalls will drop a connection if they don't see any activity on it for a period of time, to conserve their own resources. One way to combat this, which I use in some of my tools, is to set the socket option SO_KEEPALIVE on the socket, like this:

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

This has a couple of benefits - it results in the Kernel sending keepalive messages on your connection at regular intervals, even if all is quiet, which by itself is enough to keep some firewalls happy. Also, if your connection does drop, your program can find out straight away instead of next time you want to write to it (although you may not notice it unless you are regularly checking for errors on your sockets).

Perhaps your best approach might be to set the SO_KEEPALIVE, and keep your socket open, but also check for errors whenever you try to write to it, and if you have an error, close and re-open the connection.

This question may also be of use to you.

Community
  • 1
  • 1
harmic
  • 28,606
  • 5
  • 67
  • 91
  • Overhead doesn't matter. It's all LAN, and probably less than a message every 10 minutes. The remote end is not sending anything back. Nothing significant is being used to do any of this; I don't see a reason not to leave the connection open. Part of my problem is that I don't have access to the other side of this connection, so troubleshooting drops has to be done with what I've got. Your answer is incredibly helpful, I'll try the keepalive option and check back later today. Thanks again. – tsz Jul 25 '14 at 13:46
  • Seems to have worked, haven't had it drop since. Thanks for the great answer. – tsz Jul 31 '14 at 14:59