Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Can a socket listen for multiple hosts?

by sierpinski (Chaplain)
on Jan 22, 2010 at 15:22 UTC ( [id://818975]=perlquestion: print w/replies, xml ) Need Help??

sierpinski has asked for the wisdom of the Perl Monks concerning the following question:

I'm working on a script using IO::Socket, and the goal is to have multiple hosts use a common port to report some statistics to a central server. I've noticed through testing already that once a socket is connected from sender to receiver, no other senders can send to that port.

Once the sender is finished, the receiver exits, but that's not a problem since I can have it spawn another listen session, but the sent information from other hosts is lost. Is there way to do either of the following:

1. Make the other senders wait, or queue the information so nothing is lost even if it is transmitted at the same time.
2. Listen to multiple hosts on the same socket connection at the same time.
3. Notify the sender that the socket is in use so they need to try again later.

Any of these options would allow me to do what I need to do. Does IO::Socket permit any of these?

Thanks monks!
/\ Sierpinski
  • Comment on Can a socket listen for multiple hosts?

Replies are listed 'Best First'.
Re: Can a socket listen for multiple hosts?
by JavaFan (Canon) on Jan 22, 2010 at 15:32 UTC
    This is possible. If it wasn't, one couldn't make a webserver with any decent throughput.

    The "trick" is to either use threads, forks, or a select loop. The main loop waits for a connection, does an accept, and then have a child or thread deal with the connection. Or, in the case of a select loop, you've another handle to dispatch on.

    See also man perlipc, section "Internet TCP Clients and Servers", although that sections predates IO::Socket.

Re: Can a socket listen for multiple hosts?
by zentara (Archbishop) on Jan 22, 2010 at 15:34 UTC
    Look at IO::Select if your connect times are short and fast. I would guess that you can add filehandles from different ports to the same select object.

    If select don't work for you, like for big file transfers, you will need a forking or threaded server. See Glib based forking server with root messaging for example, or google for "perl forking server" or "perl threaded server".

    Otherwise, I havn't a clue what you want. :-)


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
Re: Can a socket listen for multiple hosts?
by keszler (Priest) on Jan 22, 2010 at 15:42 UTC
Re: Can a socket listen for multiple hosts?
by Your Mother (Archbishop) on Jan 22, 2010 at 17:50 UTC

    Sort of following on what trwww said. You could, if desired, go with nginx (or lighttpd) and fastcgi instead of Apache and modperl. Pretty lightweight, high performance, and a wealth of functionality and extensibility based on web standards. It's much easier and less bug-prone to follow guides and docs than to code this sort of thing up from scratch.

Re: Can a socket listen for multiple hosts?
by trwww (Priest) on Jan 22, 2010 at 16:51 UTC

    Hello,

    It sounds to me like you are trying to reinvent the Apache web server. I would take a look at it with the mod_perl module enabled. Of course its not the best advice if you don't have a lot of experience with it, but you're asking questions about stuff that you really don't want to have to think about while trying to implement a customer's app.

    Apache and a good framework like CGI::Application or Catalyst make it look like the issues you are considering now do not even exist. They are completely abstracted from you.

    Regards,

    EDIT: of course, the overhead of a full blown webserver might not fit your use case, so my advice may be irrelevant.

Re: Can a socket listen for multiple hosts?
by steve (Deacon) on Jan 22, 2010 at 20:11 UTC
    There are a lot of modules that can help in this process, and it might help to take a look at the documentation for a few on CPAN to see how you can use them for your particular application. For example, Net::Server and possibly Net::Server::Simple are easy to use and may work for what you are trying to do.
      Thanks for the responses. I ended up using IO::Select, and got it working. Posting the code here for posterity/review. Thanks!
      #!/usr/bin/perl use IO::Socket; use IO::Select; my $socket =new IO::Socket::INET->new ( LocalHost => "<ipaddress>", LocalPort => '<port>', Proto => 'tcp', Listen => 1, Reuse => 1, ); die("Couldn't create socket! $!\n") unless $socket; my $select = new IO::Select($socket); while(@ready = $select->can_read) { foreach $fh (@ready) { if($fh == $socket) { $new = $socket->accept; $select->add($new); my $host= $new->peerhost; print "[Accepting connection from $host]\n"; } else { my $line = <$fh>; $line =~ s/\s+$//; if($line =~/^quit$/i) { my $host = $fh->peerhost; $select->remove($fh); $fh->close; print "[Connection from $host terminat +ed\n"; } else { print $fh->peerhost, " said '$line'\n" +; print $fh "You said: '$line'\n"; } } } }
      Thanks for all the help! With multiple servers sending at the same time, it keeps track (using peerhost) which host sends the message, and of course the message contents.
      /\ Sierpinski

        Don't use buffered IO (e.g. read, readline aka <>) with select. You could get into a situation where data is waiting in Perl's buffer, and select wouldn't know anything about it.

        Don't use IO that blocks after it reads all that's available to be read (e.g. read, readline aka <>) with select. It defies the purpose of using select.

        You don't handle EOF, so you could end up having a handle that's permanently ready to read.

        Fixed:

        #!/usr/bin/perl use strict; use warnings; use IO::Socket::INET qw( ); use IO::Select qw( ); sub process_msg { my ($client, $msg) = @_; chomp $msg; my $host = $client->peerhost; print "$host said '$msg'\n"; return lc($msg) eq 'quit; } my $server = IO::Socket::INET->new( ... ) or die("Couldn't create server socket: $!\n"); my $select = IO::Select->new($server); my %bufs; while (my @ready = $select->can_read) { for my $fh (@ready) { if ($fh == $server) { my $client = $server->accept; $select->add($client); $bufs{fileno($client)} = ''; my $host = $client->peerhost; print "[Accepted connection from $host]\n"; } else { our $buf; local *buf = \$bufs{fileno($fh)}; my $rv = sysread($fh, $buf, 64*1024, length($buf)); if (!$rv) { my $host = $fh->peerhost; if (defined($rv)) { print "[Connection from $host terminated]\n"; } else { print "[Error reading from host $host: $!]\n"; } process_msg($fh, $buf) if length($buf); delete $bufs{fileno($fh)}; $sel->remove($fh); next; } while ($buf =~ s/\G(.*\n)//g) { if (!process_msg($fh, "$1")) { my $host = $fh->peerhost; print "[Connection from $host terminated]\n"; delete $bufs{fileno($fh)}; $sel->remove($fh); last; } } } } }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://818975]
Approved by marto
Front-paged by Arunbear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (6)
As of 2024-04-25 10:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found