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

Hi,

I don't know how to solve this problem, even Perldoc provides no solution.
I want that the a server reads a file and sends each line to as many clients as connected, then reads the next line and so on.

Normally the server-program spawns a child as soon as a client knocks at the door. Then each kid is reading from the file and sends each $line to the client independedly.
But I want - let me say - synchronize the clients so that they receive the same line at (allmost) the same time.
The example of synchronized clients by sending the time (e.g. Perldoc, SavePipes..) doesn't work for me, because each spawned kid of the server graps with 'localtime' a common and therefore synchronized resource. But I have to create the common resource myself, s.th. like
open (READ, "< $file"); # common source (Server-Mom)
while (!condition) { sleep 1 }; # wait until all have connected (Server-Mom)
while (<READ>) { print; sleep 1;} # to each client so part of the Server-Kid

Can s.o. help me with the code before,in between and after my lines?

Thanks a lot in advanve,
Carl
  • Comment on Socket: Server shall send same $line to several clients

Replies are listed 'Best First'.
Re: Socket: Server shall send same $line to several clients
by castaway (Parson) on Feb 22, 2003 at 16:13 UTC
    Hmm.. You seem to assume that forking is the only way to connect to more that one client at a time, actually you can do it without fork at all, which means the main part keeps a reference to each socket connected, and can easily send data to each of the sockets in the list.
    Eg:
    my $sockserv = new IO::Socket::INET( LocalAddr => '192.168.1.1', LocalPort => $port, Proto => 'tcp', Listen => 1, Reuse => 1, Timeout => 60); die "Can't create Server ($!)" unless $sockserv; $x = new IO::Select(); $x->add($sockserv); $clients = 0; $sockclient=(); while (1) { my @handles = $x->can_read(); # wait for data foreach $handle (@handles) { if ($handle == $sockserv) { $sockclient = $sockserv->accept() || warn "accept: $!" +; $clients++; if ($clients > 5) { print $sockclient "Sorry, we're full.\n"; $sockclient->close(); } else { $sockclients[$clients] = $sockclient; } $x->add($sockclients[$clients]); } $cl = is_member($handle, @sockclients); if ($cl > -1) { $n_read = sysread($sockclients[$cl], $buf, 1024); return 0 if (!n_read || !length $buf); parse_external_command($buf, $sockclients[$cl]); } } }
    Thus you have all clients in @sockclients, and can send data to each one using:
    foreach my $sock (@sockclients) { print $sock $data; }
    If you need forks tho, you'll need to find some other way to get an array of the sockets.
    So maybe you should show us your code.

    C.

    Disclaimer: Copied partly from working code, not tested as is!

      Thanks castaway,

      it seems pretty much that this is what I was looking for.
      I was mentally bound to the idea of froking a new kid for each client and therefore I was thinking of forking another kid that reads the file(s) and sends them to all other client-kids. But this seems to me too complicated for Perl, Perl is smarter (than me). But thank Good, that there are guys like you :-)

      I'll soon will try your solutions.
      For now thanks a lot,
      Carl

      castaway,

      from your code I was able to go on, thanks for this nice push. I packed the Server & Handle stuff in one package having in mind that I might be able to listen for new clients within a spawned kid and pass the updated valid client-handles to its parent.

      But this failed and want to ask now:

      is there a way to pass those handels or the updated class var.: @CL (which contains all client-handles) to another process?

      package SoServ; #: one server for all clients use IO::Socket::INET; use IO::Select; my @CL = (); #: array of sock.Handles to Clients my $S; #: Socket-Server my $H; #: for IO::Select sub new { #: new Socket-Server my $me = {}; $me->{'a'} = shift; #: loc.Adddress address $me->{'p'} = shift; #: port to connect $S = IO::Socket::INET->new( LocalAddr => $me->{'a'}, LocalPort => $me->{'p'}, Proto => 'tcp', Listen => 1, Reuse => 1, Timeout => 60 ); die "Can't create SoServ ($!)\n\n" unless $S; $H = IO::Select->new(); $H->add($S); return bless $me; } #: wait for new connections sub waitNewConn { my $me = shift; #: either endless (1) or just 2 clients (0): my $withKids = shift; while (1) { # endless Loop if with Kids! # wait timeout-sec and then try again: my $newCL = $S->accept() || next; print $newCL "Hi, guy, nice meeting you\n"; $H->add($newCL); #update @CL instead of:push @CL,$newCL; @CL = $H->can_write(1); # no kid: return after 2 conn. return @CL if (@CL == 2 && !$withKids); } # while (1) } # sub sub clients { return @CL } ####### end packages ############# package main; my $addr = 'localhost'; # hard coded for now my $port = 2345; # dito my $Serv = SoServ::new( $addr,$port ); # start Server # Array of Handles to Clients derived from SoServ->@CL my @Clients = (); my $withKids = 1; # want kids or everyth. in 1 process? if ($withKids) { spawn($Serv); # kid is waiting for new Clients } else { # no kid, waiting here for new (2) Clients @Clients = $Serv->waitNewConn(0); } my $line = ''; my $f = '/home/cas/Data/sp1Min/spAdj.dta'; my $n = 9; # just 9 lines for now open(SP, "< $f") or die "can't open SP: $f: $!"; # while (defined($line = <SP>)) { print $line; # control @Clients = $Serv->clients() if ($withKids); for (@Clients) { print $_ $line; } last if (!$n--); } close(SP); exit; sub spawn { my $S = shift; my $pid; if (!defined($pid = fork)) { print "cannot fork: $!"; return; } elsif ($pid) { print " I'm the Server-Parent: pid:$pid \$\$:$$\n"; return; # I'm the parent } $S->waitNewConn(1); # yes with Kids, of course => (1) exit; } # end sub spawn