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

In a nonforking server I need to send STDOUT to any number of attached clients. I'm currently trying to bind STDOUT to the socket via $fd = filno $client; open STDOUT, ">&=$fd"; and this doesn't seem to be working when I start cylcing through the attached clients.

I've included by code so you can see exactly what I am trying to do. Perhaps (in fact even likely) I am making some basic mistake. Any input would be appreciated.

The log data indicates that I am, in fact, cycling through the clients as expected. I'm just lost at this point as to how to proceed.

#!/usr/bin/perl -w # nonforker - server who multiplexes without forking use POSIX; use IO::Socket; use IO::Select; use Socket; use Fcntl; use Tie::RefHash; use Carp; BEGIN { $coral_dir = "/usr/local/Coral"; } # This will be edited durin +g build. use lib "$coral_dir/lib"; #coral global libdir# use lib "../../lib"; #coral local libdir# use CRL; use IO::Scalar; $port = 9001; # change this at will # Listen to port. $server = IO::Socket::INET->new(LocalPort => $port, Listen => 10 ) or die "Can't make server socket: $@\n"; %ready = (); tie %ready, 'Tie::RefHash'; nonblock($server); $select = IO::Select->new($server); print "Server started\n"; open LOG, ">./errlog" or die "cannot open errlog $!\n"; print "Starting CoralReef\n"; #Coral::quick_start(0, 0, Coral::API_CELL); Coral::quick_start(); print "Quick start finished\n"; # Main loop: check reads/accepts, check writes, check ready to process while (1) { my $client; my $rv; my $data; # check for new information on the connections we have # anything to read or accept? foreach $client ($select->can_read(0)) { if ($client == $server) { # accept a new connection $client = $server->accept(); $select->add($client); nonblock($client); recv($client, $duration, 10, 0); $ready{$client} = time + $duration; $out_name = time; print LOG "MAIN: $client\n"; &swap_stdout($client); $writer = Coral::write_open("-", undef, "none"); &revert_stdout; if (!defined($writer)) { Coral::puts("Error, could not open output file!"); exit(1); } else { Coral::puts("Writing to file: $out_name"); } Coral::write_init_all($writer); $endtime = time + $duration; Coral::puts("$duration, $endtime, $out_name\n"); } } $iface = Coral::read_cell_all(\$binfo, \$cell); foreach $client (keys %ready) { handle($client); } } # handle($socket) deals with all pending requests for $client sub handle { # requests are in $ready{$client} # send output to $outbuffer{$client} my $client = shift; my $request; print LOG "HANDLE: $client\n"; &swap_stdout($client); if (Coral::write_cell($writer, $iface, $cell) < 0) { Coral::puts("Could not write cell! exiting!"); exit(2); } &revert_stdout; if ($ready{$client} <= time) { &swap_stdout($client); Coral::write_close($writers{$writer}[2]); &revert_stdout; delete $ready{$client}; $select->remove($client); $client->close; next; } } # nonblock($socket) puts socket into nonblocking mode sub nonblock { my $socket = shift; my $flags; $flags = fcntl($socket, F_GETFL, 0) or die "Can't get flags for socket: $!\n"; fcntl($socket, F_SETFL, $flags | O_NONBLOCK) or die "Can't make socket nonblocking: $!\n"; } #bind STDOUT to the client socket and unbuffer sub swap_stdout { my $client = shift; my $fd = fileno($client); print LOG "SWAP: $client == $fd\n"; open(STDSAVE, ">&STDOUT"); open(STDOUT, ">&=$fd"); select(STDOUT); $|=1; } # revert to the default STDOUT sub revert_stdout { close STDOUT; open(STDOUT, ">&STDSAVE"); }

Replies are listed 'Best First'.
Re: Sending STDOUT to Multiple Sockets
by abstracts (Hermit) on Aug 18, 2001 at 04:06 UTC
    Here is an example I cooked really quick that illustrates how you can redirect stderr to all non-blocking sockets: It looks like a broadcasting chat server.
    use Socket; use Fcntl; use IO::Socket; use IO::Select; my $serv = IO::Socket::INET->new(LocalPort => 9001, Reuse => 1, Listen + => 10) or die "Cannot open server: $!\n"; nonblock($serv); my $sel = IO::Select->new($serv); pipe $MYSTDERR, $MYWRITE; *STDERR = $MYWRITE; $sel->add($MYSTDERR); while(1){ for my $sock ($sel->can_read(0)) { if($sock == $MYSTDERR){ my $line = <$sock>; print $line; for my $s ($sel->handles()){ if($s != $MYSTDERR and $s != $serv){ print $s $line; } } } elsif($sock == $serv){ my $new = $serv->accept(); nonblock($new); $sel->add($new); bcast($new, "New Connection"); } else { # this is a client reading my $line = <$sock>; if($line){ chomp($line); bcast($sock, "Data: $line"); } else { bcast($sock, "Connection Dropped"); $sel->remove($sock); } } } } sub bcast{ my $sock = shift; warn sprintf "[%s:%s] @_\n", $sock->peerhost, $sock->peerport; } sub nonblock { my $socket = shift; my $flags; $flags = fcntl($socket, F_GETFL, 0) or die "Can't get flags for socket: $!\n"; fcntl($socket, F_SETFL, $flags | O_NONBLOCK) or die "Can't make socket nonblocking: $!\n"; }
    I hope it helps,,,

    Aziz,,,

Re: Sending STDOUT to Multiple Sockets
by suaveant (Parson) on Aug 17, 2001 at 23:23 UTC
    I would capture STDOUT and write it to each of the sockets... see this node capturing STDOUT for info how to capture.

                    - Ant
                    - Some of my best work - Fish Dinner

      I've actually tried that but it was failing for some reason. I was using the IO::Scalar routines. Often it woudl say that $a was undefined. :\ I'll try again and see what i can come up with.
        no good. I inserted
        tie *STDOUT, 'IO::Scalar', \$a; ...stuff... print $client $a; untie *STDOUT;
        in all of the appropriate locations and always got a warning about using an unitialized value. I think part of the problem is that the original C code (this is a wrapped C API I am using) actually uses a pointer to STDOUT instead of STDOUT proper. I don't know if this would be a significant issue but I'm starting to think it is.