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

I have cobbled together a primitive server, directly from the cookbook. It works, as long as only one client is connected. Being greedy, I would to have the program accept more than one connection.

"Ah-ha!", sez I,"This must be where non-blocking IO comes in". I read up on non-blocking IO, and this seems to be totally wrong.

But, while reading about non-blocking IO, I come across the idea of servers forking off processes to handle the details.
I've made a few attempts on this, but I can still maintain only one connection. Could someone elucidate me on this concept?
Below is the script that I'm using as a "base", and as a final note, I'm working on win32 (with fork capabilities, so that's not the issue, mmkay? :) )

#!/usr/bin/perl -w use IO::Socket; use Net::hostent; # for OO version of gethostbyaddr $PORT = 23; # pick something not in use $crlf = "\015\012"; $server = IO::Socket::INET->new(Proto => 'tcp', LocalPort => $PORT, Listen => 10, #SOMAXCONN, Reuse => 1); die "can't setup server" unless $server; print "[Server $0 accepting clients]\n"; while ($client = $server->accept()) { $client->autoflush(1); print $client "Welcome to $0; type help for command list.$crlf"; $hostinfo = gethostbyaddr($client->peeraddr); printf "[Connect from %s]\n", $hostinfo->name || $client->peerhost +; print $client "Command? $crlf"; while ( <$client>) { print "got $_";; next unless /\S/; # blank line if (/quit|exit/i) { last; + } elsif (/date|time/i) { printf $client "%s$crlf", scalar loc +altime; } elsif (/who/i ) { print $client $client->peerhost,$cr +lf; } elsif (/cookie/i ) { print $client "cookie$crlf"; } elsif (/motd/i ) {print "showing motd\n"; print $clien +t "motd$crlf"; } else { print $client "Commands: quit date who cookie motd$crlf"; } } continue { print $client "Command? $crlf"; } close $client; }

Replies are listed 'Best First'.
Re: Creating a server that accepts more than one client.
by VSarkiss (Monsignor) on Aug 16, 2001 at 23:33 UTC

    The usual way to do this is to have a parent process that is essentially always blocked on accept, with child processes that do the actual work. Fitting this into your framework would be something like this:

    while ($client = $server->accept()) { $pid = fork(); if ($pid == 0) { print $client "Welcome to $0"; # etc. # Probably a loop here reading from $client # ... Other stuff exit 0; # Remember to go away when done! } # parent process here close $client; } # and go back to accept.
    One thing missing from here is a throttle. You want to keep a count of active child processes so you don't keep creating new ones without any limit. Incrementing the counter is easy; the trick is to decrement the counter properly when each child process exits. In unix-land you can trap SIGCHLD for that, I don't know the equivalent on Win32. Whichever method you use, be careful to single-thread access to the counter. In other words, make sure when two children exit "at the same time", the counter really is decremented twice.

    I haven't looked at the modules for handling forks. They may well solve this problem for you.

    HTH

      Building on this, there are two kinds of servers for multiple clients: 1) multiple simultaneous clients and, 2) multpile sequential clients. Looking at your code, it appears that you have a server (at least trying to be) of the second type. The "while(accept)fork" model as explained by VSarkiss is for multiple simultaneous clients.

      The Net::Server module looks very interesting. I'd never seen or heard of it before, but may go that way in the future, if it is possible. Unfortunately such modules are not always compatible with other modules (e.g. Gtk becuase it does not use Gtk's event model), so you sometimes have to write your own.

      HTH, --traveler

Re: Creating a server that accepts more than one client.
by princepawn (Parson) on Aug 16, 2001 at 23:22 UTC
  • Iff this is an education exercise, then get Lincoln Stein's "Network Programming with Perl"
  • If you want something already written, try Net::Server

    @foreign = qw(HTTP HTML CGI SQL fixed-length-data xSV-data); @chromatic = @tilly = map { "$_ is plain text, easily manipulated in Perl" } @foreign; @princepawn = map { "find something on CPAN to manipulate $_" } @foreign;

Re: Creating a server that accepts more than one client.
by perrin (Chancellor) on Aug 16, 2001 at 23:27 UTC
    You mentioned that you have the Cookbook. There's plenty of forking server info in there. However, I learned about writing servers in Perl from the highly underrated O'Reilly book, Advanced Perl Programming. It has great examples and explanations of non-blocking I/O, forking, etc.
Re: Creating a server that accepts more than one client.
by busunsl (Vicar) on Aug 17, 2001 at 09:58 UTC
    Just to add another module to the list:

    If you decide not to fork, you can use IO::Multiplex to handle the non-blocking stuff.