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. | [reply] [d/l] [select] |
Re: Can a socket listen for multiple hosts?
by zentara (Cardinal) 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. :-)
| [reply] |
Re: Can a socket listen for multiple hosts?
by keszler (Priest) on Jan 22, 2010 at 15:42 UTC
|
| [reply] |
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.
| [reply] |
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.
| [reply] |
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. | [reply] |
|
|
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.
| [reply] [d/l] |
|
|
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;
}
}
}
}
}
| [reply] [d/l] [select] |
|
|
|
|
|
|
|
|
|