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

All,

I'm writing a server script that should stay running, for each client connection I fork a new child, but when I add "exit 0" at the end of the child code, the server script exist (even though the script doing the right thing). So, what I've read in that in Solaris 7 (and I'm assuming 8, since that is what I'm using) is that accept() does not automatically restart (after a child exits). If I take the "exit 0" everything looks fine, but it's leaving behind stray processes (not a good thing). Any help would be greatly apprieciated. Here is the code:
use strict; use warnings; use IO::Socket; use POSIX qw(:sys_wait_h); # use diagnostics; my $local = "172.23.167.114"; my $port = "7070"; my $maxconn = SOMAXCONN; my ($new_sock, $c_addr, $buf, $pid, $status); $|++; # NOTE: use netstat to see network properties. # ignore child processes to prevent zombies # $SIG{CHLD} = 'IGNORE'; # Using this actually makes all return codes + = -1 !! # $SIG{PIPE} = 'IGNORE'; sub REAPER{ my $pid = wait; # while ( waitpid(-1,WNOHANG) > 0 ) { } $status = $? >> 8; $SIG{CHLD} = \&REAPER; # wait for next child process } $SIG{CHLD} = \&REAPER; # wait for 1st child process my $sock = new IO::Socket::INET ( LocalHost => $local, LocalPort => $port, Proto => 'tcp', Listen => $maxconn, Reuse => '1', ); die "Could not create socket: $@ \n" unless $sock; print "RECEIVER::IP = $local PORT = $port MaxConn = $maxconn\n"; while (($new_sock, $c_addr) = $sock->accept() ) { die "Can't fork: $!" unless defined ($pid = fork()); if ( $pid == 0 ) { # Child code my ($client_port, $c_ip) = sockaddr_in($c_addr); my $client_ipnum = inet_ntoa($c_ip); my $client_host = gethostbyaddr($c_ip, AF_INET); print "CHILD CODE [cpid::$$]: Connection from: $client_host [$cl +ient_ipnum ", $new_sock->peerport, "] \n\n"; while (defined($buf = <$new_sock>) ) { if ($buf =~ /^EXECUTE:(.*)/ ) { print $1, "\n"; system("/msg/spgear/tools/bin/perl $1") || die "Could not +run program: $1 -- $!\n"; if ( defined $status ) { print $new_sock "RETURN CODE: $status \n"; undef $status; } else { print $new_sock "RETURN CODE: undef \n"; } } else { print "INVALID SYNTAX\n"; print $new_sock "INVALID SYNTAX\n"; } } # exit(0); } else { # Parent code $new_sock->close; print "\nPARENT CODE [ppid::$$ cpid:$pid]\n"; } }

Replies are listed 'Best First'.
Re: In Solaris accept() is not restarted [on Solaris] ...
by pg (Canon) on Oct 24, 2003 at 17:11 UTC

    No, I don’t think that’s the problem, I worked on telecom systems on Solaris for years, and this never happened to me (accept does not restart???, it is too basic to allow a bug).

    By reading your code, I can see that your program will quit the outmost while loop, when accept() call fails (In array context, it returns empty array, which is evaluated to false). I don’t think that’s what you want, and it most likely confused you as what is the reason that it is not accepting connection any more.

    Also it is not a good idea, for quite a few OS systems, to create socket with no timeout. This makes the wake up very slow, and probably even hang.

      Actually, I read that in a different node (it's not something I investigated) accept()

      I just started network programming a few days ago (how's that for being a newbie ?). So, still have a lot to learn.
      Do you have any better suggestions on how the while loop should be constructed ? What's a good recommended timeout ?
      Thx.
Re: In Solaris accept() is not restarted [on Solaris] ...
by iburrell (Chaplain) on Oct 24, 2003 at 20:55 UTC
    If the child doesn't exit, not only does it hang around, it goes into the accept loop. And it might accept some connections and fork off its own children.

    It usually a good idea to close the server socket at the beginning of the child code.

    Finally, you should use waitpid in a while loop in the REAPER subroutine. Multiple child processes can exit by the time the signal handler gets run and a single wait will only get one of them. This will leave zombies.

Re: In Solaris accept() is not restarted [on Solaris] ...
by Anonymous Monk on Oct 24, 2003 at 17:10 UTC
    use Errno qw(:POSIX); while (1) { ($new_sock, $c_addr) = $sock->accept(); unless ($new_sock) { next if $!{EINTR}; die "Can't accept: $!"; } die "Can't fork: $!" unless defined ($pid = fork()); #(etc)