IO::Socket::INET - Listen on exactly two IPs the same time?

K

Kai Bleek

Hi there,

I'm just writing some little perl-server using IO:Socket::INET.

Now, I would like to make the server listen to more than one IP
without listening to ALL IPs (0.0.0.0). Is it possible with
IO::Socket::INET at all to give the server a second, maybe third IP?

I'm not that experienced with this module/socket programming. I
googled around but couldn't find any reference to the solution of this
problem (neither did I find someone mention this problem - it seems
I'm the only one who isn't able to solve this - maybe I have
overlooked something really obvious?)

So, if someone has an idea how to get it working that way, please drop
me a hint.

Thanks in advance,
Kai
 
U

Uri Guttman

KB> Now, I would like to make the server listen to more than one IP
KB> without listening to ALL IPs (0.0.0.0). Is it possible with
KB> IO::Socket::INET at all to give the server a second, maybe third IP?

the socket system only allows binding to one IP/port pair or wildcarding
any IP with a single port.

KB> So, if someone has an idea how to get it working that way, please drop
KB> me a hint.

the easiest way would be to listen on each desired IP with the same port
and use an event loop or io::select to handle the multiple listen
sockets. once you get a connected socket you can treat them all the same
if needed or differently based on the listen IP. you can get the listen
IP from the socket with the getsockaddr call

uri
 
K

Kai Bleek

the socket system only allows binding to one IP/port pair or wildcarding
any IP with a single port.

Hm, well, that's a pity.
the easiest way would be to listen on each desired IP with the same port
and use an event loop or io::select to handle the multiple listen
sockets. once you get a connected socket you can treat them all the same
if needed or differently based on the listen IP. you can get the listen
IP from the socket with the getsockaddr call

I took a look at the IO::Select module and with a little
code-rewriting it does the job. Had some problems with forking first,
but that's solved now, too!

Many thanks for the help!
Best regards,
Kai

If it was of any interest, this is how I'm using the select:

# defining two objects
my $server_remote = IO::Socket::INET->new(LocalAddr => '192.168.0.60',
....);
my $server_local = IO::Socket::INET->new(LocalAddr => '127.0.0.1',
....);

# Adding to select:
my $read_set = new IO::Select();
$read_set->add($server_remote);
$read_set->add($server_local);

# entering loop
while (1) {
my @select_set = $read_set->can_read;
foreach my $server (@select_set) {
# distinguish between socket-IPs
if ($server == $server_remote) {
my $connect = $server->accept();
$read_set->add($connect);
# some Code...
$read_set->remove($connect);
$connect->close;
} elsif ($server == $server_local) {
my $connect = $server->accept();
$read_set->add($connect);
# some different code
$read_set->remove($connect);
$connect->close;
}
}
}
 
U

Uri Guttman

KB> Hm, well, that's a pity.

note that this is not a perl restriction but one that came with the
original BSD socket API. the whole socket/bind/listen api is geared
towards one address and would have been very ugly if it supported
multiple addresses. using select on those multiple sockets is not
difficult and works around the problem nicely. you would want select to
be used in any case for any proper server so this adds very little code
to listen to more than one address on the same port.



KB> # Adding to select:
KB> my $read_set = new IO::Select();
KB> $read_set->add($server_remote);
KB> $read_set->add($server_local);

KB> # entering loop
KB> while (1) {
KB> my @select_set = $read_set->can_read;
KB> foreach my $server (@select_set) {
KB> # distinguish between socket-IPs
KB> if ($server == $server_remote) {
KB> my $connect = $server->accept();
KB> $read_set->add($connect);
KB> # some Code...
KB> $read_set->remove($connect);
KB> $connect->close;
KB> } elsif ($server == $server_local) {
KB> my $connect = $server->accept();
KB> $read_set->add($connect);
KB> # some different code
KB> $read_set->remove($connect);

why do you remove the $connect when you don't add it to the io::select
set? unless you do that elsewhere. and then you would need to check for
a connected socket vs a listen socket.

KB> $connect->close;
KB> }
KB> }
KB> }

there are better ways to do that. i would use the $server_local and
$server_local objects as keys to a dispatch table hash and then dispatch
based on what socket was returned from io::select. then the body of your
loop is much cleaner and easier to extend just by adding entries into
the dispatch table.

<untested>

$read_set->add($server_remote);
$listen_handler{$server_remote} = \&remote_handler ;

$read_set->add($server_local);
$listen_handler{$server_local} = \&local_handler ;

# entering loop
while (1) {
my @select_set = $read_set->can_read;
foreach my $server (@select_set) {

my $connect = $server->accept();
my $handler = $listen_handler{$server} ;

# here you could check if it is a listen socket or a connected one by
#seeing if you got a handler for it.

$handler->( $connect ) ;
$connect->close;
}


uri
 
K

Kai Bleek

Hi again,
why do you remove the $connect when you don't add it to the io::select
set? unless you do that elsewhere. and then you would need to check for
a connected socket vs a listen socket.

well - guess you're right about that - some artefact from my figuring
out how to use IO::Select.
there are better ways to do that. i would use the $server_local and
$server_local objects as keys to a dispatch table hash and then dispatch
based on what socket was returned from io::select. then the body of your
loop is much cleaner and easier to extend just by adding entries into
the dispatch table.

I couldn't quite get along with the dipatch tables. Since I want to
fork any connect as soon as it hits the server, my loop now looks like
this:

while (1) {
my @select_set = $read_set->can_read;
foreach my $server (@select_set) {
fork_connect($server);
}
}

Quite clearly arranged, isn't it? In the forking code I built
something one would use a "case"-contruct for if it were not perl
we're using:

if ($childpid == 0) {
if ("$server_ip" eq "$ini_ref->{'section'}->{'LOCAL_ADDRESS'}") {
remote_connect($connect);
} elsif ("$server_ip" eq "127.0.0.1") {
local_connect($connect);
} else {
die "Connect from undef IP!\n";
}
}

Not the most elegant way I guess, but it works for now.
Thanks again for the help! I keep that dispatch table alternative in
mind for later improvements.

Kai
 
A

Andrew Rich \(VK4TEC\)

have you just invented a super new lan card that runs two ip addresses at
once ?
 
U

Uri Guttman

"AR\" == Andrew Rich \(VK4TEC\) <Andrew> writes:

AR\> have you just invented a super new lan card that runs two ip addresses at
AR\> once ?

no. he is probably using an IP alias technique supported on most
OSs. actually it seems he was separating internal (localhost) and
external connections which amounts the the same problem of listening on
more then one IP address. and for your information, lan cards listen to
mac (ethernet) addresses and not IP addresses.

uri
 
K

Kai Bleek

Uri Guttman said:
AR\> have you just invented a super new lan card that runs two ip addresses at
AR\> once ?

no. he is probably using an IP alias technique supported on most
OSs. actually it seems he was separating internal (localhost) and
external connections which amounts the the same problem of listening on
more then one IP address. and for your information, lan cards listen to
mac (ethernet) addresses and not IP addresses.

yes - you are right, in the end I will have several IPs added as
virtual interfaces upon the main interface (eth0:0, eth0:1). But this
has nothing to do with my select-prob, as you stated and what could be
read in my previous posts, I just wanted to distinguish between local
and remote connects. Which - btw - I have successfully realised
(including forks and shared memory variables (storable +
ipc::sharelite)) by now using a dispatch table, following your earlier
suggestion.

Regards,
Kai
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top