ealeon ealeon - 4 months ago 21
Perl Question

perl socket: increment port if in use

I have the following code:

use IO::Socket::INET;
use Sys::Hostname;
use Socket;


my($addr)=inet_ntoa((gethostbyname(hostname))[4]);

my $port_to_use = 7777;

my $socket = new IO::Socket::INET (
LocalHost => $addr,
LocalPort => $port_to_use,
Proto => 'tcp',
Listen => 5,
Reuse => 1
);
die "cannot create socket $!\n" unless $socket;
my $client_socket = $socket->accept();


if i start this in one screen and start another one in the other screen, i get an error:

cannot create socket Address already in use


instead of dying, i would like to try using different port (increment by 1) until it can find the one to use.

I've try to convert the
die
line with
eval
but im not able to catch it

any suggestions?

Answer

Here is a tidier alternative which actually checks to make sure the failure to bind to a given port was due to the port being in use. It also limits the port range to check. If you use the code in the other answer, and, if for some reason, the machine is not allowing your application to bind to any ports, you are going to get stuck in an infinite loop. It may also cause your application to bind to ports that should otherwise have been left alone etc.

#!/usr/bin/env perl

use strict;
use warnings;

use Carp qw( croak );
use Errno qw( EADDRINUSE );
use IO::Socket::INET;
use Sys::Hostname qw( hostname );
use Socket;

# These can come from a config file or command line
# See also https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Dynamic.2C_private_or_ephemeral_ports
# https://unix.stackexchange.com/a/39784/2371

my @port_range = (0xC000, 0xFFFF);

my $addr = inet_ntoa( (gethostbyname(hostname) )[4]);
my $socket;

TRY_PORT:
for my $port ($port_range[0] .. $port_range[1]) {
    warn "Trying port $port\n";

    $socket = IO::Socket::INET->new(
        LocalHost => $addr,
        LocalPort => $port,
        Proto => 'tcp',
        Listen => 7,
        Reuse => 0,
    );

    if ($socket) {
        warn "Bound to port $port\n";
        last TRY_PORT;
    }

    if ( EADDRINUSE != $! ) {
        croak "Cannot bind to port '$port': $!";
    }
    warn "Port in use, trying the next one\n";
}

$socket->accept
     or croak "...";
# ...