http://www.perlmonks.org?node_id=293014

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

Hi Monks: I'm trying to send a small line from a fast-cgi script
acting like a client to a server using UNIX sockets.
This works fine the first time.
The problem is the server dies as soon
as the client closes the connection.

Here is the code:

### Client use FCGI; use IO::Socket; use strict; # some variables .... my ($cnt,$phrase,$rem_ip)=(0,'',''); my $client; my %env; my $req=FCGI::Request(\*STDIN,\*STDOUT,\*STDERR,\%env); while ( $req->Accept() >= 0 ) { $cnt++; my $phrase=substr $env{'QUERY_STRING'} , 3 , length $env{'QUERY_STRING'}; my $rem_ip=$env{'REMOTE_ADDR'}; if ( defined $phrase and defined $rem_ip ) { my $msg=$phrase.'|'.$rem_ip; my $sockname = "/tmp/dynipsockd.sock"; $client = IO::Socket::UNIX->new( Peer => $sockname, Type => SOCK_STREAM, Timeout => 5, ) or die "$0: error connecting to '$sockname': $@\n"; my $pid = fork(); die "Cannot fork\n" unless defined $pid; if ($pid) { write_sock($msg); waitpid($pid, 0); } else { read_sock(); } } else { &write_log; } printf "%s%s","content-type:text/html\n\n"," .... \n"; } sub write_sock { my $msg = shift; print $client $msg,"\n"; # print to socket print $client "\n"; # connection ends } sub read_sock { while (my $line = <$client>) { print $line; # print to stdout } } sub write_log { open (L,">>/tmp/dynipc.log"); print L localtime(time),": Iteration: ",$cnt,": Connection Attempt +ed with invalid string. Env : $env{'REMOTE_ADDR'} \n"; } ### Server use IO::Socket; use POSIX ":sys_wait_h"; use DBI; use strict; use Data::Dumper; my $sockname = "/tmp/dynipsockd.sock"; # Some initial operations. # Db connection, inititialization # of data structures, etc. start_daemon(); sub start_daemon { my $pid; if ($pid = fork()) { waitpid($pid, 0); } else { if ($pid = fork()) { exit; } $0 = "unixsockd: accepting connections on $sockname"; # for ī +psī &service_clients( get_sock() ); # incoming requests } } sub get_sock { unlink $sockname; my $sock = IO::Socket::UNIX->new( Local => $sockname, Type => SOCK_STREAM, Listen => SOMAXCONN, ) or die "$0: error starting daemon on '$sockname': $@\ +n"; chmod 0660, $sockname; chown scalar getpwnam('nobody'), 0, $sockname; return $sock; } sub service_clients { my $sock = shift; $SIG{CHLD} = \&reaper; my $client; while ( $client = $sock->accept ) { my $pid = fork(); die "Cannot fork\n" unless defined $pid; if ($pid) { # parent warn "Connection received!\n"; close $client; # parent won't use it next; # be ready for another client } # child close $sock; # no use to child process_requests($client); # do what it was conceived for print "Client served !\n"; exit 0; # terminate child } } sub process_requests { my $client = shift; $0 = "unixsockd: handling requests..."; # for īpsī while ( my $line = <$client> ) { # read from socket last if $line =~ /^\s$/; # exit on empty line chomp $line; eval { &some_function($line) }; print $client "\n"; } } sub reaper { #WNOHANG: return immediately if no child has exited. while (waitpid(-1,WNOHANG) > 0) {} #reset the sig for the next child to die; $SIG{CHLD} = \&reaper; } sub some_function { # .. opens db conn, fills up some hashes, closes # db conn, etc &some_other_func; } } sub some_other_func { #..... very basic operations with hashes }
Any help will be highly appreciated.

Replies are listed 'Best First'.
Re: Unix sockets
by MarkM (Curate) on Sep 22, 2003 at 04:47 UTC

    My first bet would be that you are not watching for EINTR as a result of $sock->accept. The first time through the loop, the connection is accepted and a new process is spawned to handle the connection. Once this process dies, the caller receives SIGCHLD, which causes accept() to be interrupted, which should result in $! == POSIX::EINTR(). If this is the case, the accept() should be restarted within your code.

    On an unrelated note, there are a few error cases that you don't check, such as the initial fork() in start_daemon() returning undef, etc.

    Cheers,
    mark

Re: Unix sockets
by zentara (Archbishop) on Sep 22, 2003 at 14:54 UTC
    Just a thought............... I set up some scripts to run with FastCGI and one of the things that needed to be set right was the configuration for mod_fcgi in httpd.conf. Depending on the way mod_fcgi is configured, the fcgi scripts are handled in different ways...static....dynamic...etc. You might need to read up on it. I needed a line "FastCGIConfig -restart" in my httpd.conf to get mine working. The difference between static and dynamic scripts is important. One way keeps the same script going, the other will kill and restart a server for each connection. I have no idea how it affects sockets.

      At first I jumped to the FastCGI conclusion as well, however, if you note, I believe it is the server process that is dying, not the client FastCGI process. I conclude, therefore, that the FastCGI component is irrelevant.

        That's right, It's the socket server who dies unexpectedly, the FCGI process is still there after all. I tried wrapping the "$sock->accept" loop section with another while() {} but I didn't like the results. Thank You for your help! Hugo