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

I'm needing to maintain a small server that I telnet into with multiple conenctions, complete tasks and logoff.

everything is fine except I need to talk to each client through the server form a client. It would be like a simple chat server.

I tried setting up a hash like this:
$connections{$user_name}{client} = $client; and then runnig it through a loop foreach $key (keys %connections) { print $connections{$key}{client} "info to be displayed"; }

but that only sends the message to the current client (the one that generated the message). and %connections is defined as a global variable.

heres the stripped down code ... the server was stolen from the perl cookbook ... any help would be GREATLY appriated. thanks
#!/usr/bin/perl # preforker - server who forks first use IO::Socket; use Symbol; use POSIX; # establish SERVER socket, bind and listen. $server = IO::Socket::INET->new(LocalPort => 6969, Type => SOCK_STREAM, Proto => 'tcp', Reuse => 1, Listen => 10 ) or die "making socket: $@\n"; # global variables $PREFORK = 5; # number of children to maintain $MAX_CLIENTS_PER_CHILD = 5; # number of clients each child sho +uld process %children = (); # keys are current child process I +Ds $children = 0; # current number of children ############# %connections = (); #current connections ############# sub REAPER { # takes care of dead children $SIG{CHLD} = \&REAPER; my $pid = wait; $children --; delete $children{$pid}; } sub HUNTSMAN { # signal handler for SIGINT local($SIG{CHLD}) = 'IGNORE'; # we're going to kill our children kill 'INT' => keys %children; exit; # clean up with dignity } # Fork off our children. for (1 .. $PREFORK) { make_new_child(); } # Install signal handlers. $SIG{CHLD} = \&REAPER; $SIG{INT} = \&HUNTSMAN; # And maintain the population. while (1) { sleep; # wait for a signal (i.e., child's + death) for ($i = $children; $i < $PREFORK; $i++) { make_new_child(); # top up the child pool } } sub make_new_child { my $pid; my $sigset; # block signal for fork $sigset = POSIX::SigSet->new(SIGINT); sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: $!\n"; die "fork: $!" unless defined ($pid = fork); if ($pid) { # Parent records the child's birth and returns. sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; $children{$pid} = 1; $children++; return; } else { # Child can *not* return from this subroutine. $SIG{INT} = 'DEFAULT'; # make SIGINT kill us as it did be +fore # unblock signals sigprocmask(SIG_UNBLOCK, $sigset) or die "Can't unblock SIGINT for fork: $!\n"; # handle connections until we've reached $MAX_CLIENTS_PER_CHIL +D for ($i=0; $i < $MAX_CLIENTS_PER_CHILD; $i++) { $client = $server->accept() or last; # do something with the connection ################## My Code #################### print $client "What is your name: "; $user_name = <$client>; $connections{$user_name}{client} = $client; foreach $key (keys %connections) { Send($connections{$key}{client}, "test from $key"); } } ################## End My Code #################### # tidy up gracefully and finish # this exit is VERY important, otherwise the child will become # a producer of more and more children, forking yourself into # process death. exit; } }

Replies are listed 'Best First'.
Re: forking server
by chromatic (Archbishop) on Oct 12, 2000 at 23:05 UTC
    bliz's answer fits with my understanding. (On systems that support it, there's a copy-on-write operation that means %children will be the same in a parent and the child only until one of them changes it. Then they'll both get their own unique copies.)

    I think part of the difficulty is that you have the parent communication bit in the child bits of make_new_child(). I'd expect to see that rather in the parent part.

    I'm not a socket guru, so I don't know if storing the results of an accept() call that way will work. Since you're using the same socket on the parent side, I'd expect you can only talk to one kid at a time, and that only if the kid has made a connection to the parent.

    merlyn has an example of a preforking chat server on his web page. It's WebTechniques column 22 or 23, I believe. Examining that code and the explanation may be more helpful in this case.

Re: forking server
by bliz (Acolyte) on Oct 12, 2000 at 22:56 UTC
    I might be mistaken when I say this.. so don't be too harsh on me. :) Anyhow, when you fork each of your child "servers", that accept connections, they each get their own copy of the current %children. When they add the new child's socket to the children hash, it only goes to THEIR copy of the children hash. All other server children have their own copy, and the main server has it's own copy. In order to do what you are wanting to do, you will need to use some shared memory for the %children hash.
    --
    bliz
    
      how would I do this? I just tried accessing each fo the keys in %childern and it doesn't come up with anything ... Is there a better approach to this problem .. would a non-forkign server be better? I tried that first, but it didn't handle multiple connections very well.
        man perlfunc, and search for the following functions, which will allow you to allocate and access shared memory. I would also suggest reading up on some shared memory concepts and such. Good luck, hope it works out for you, and most of all -- that you learn a lot from it.

        shmctl, shmget, shmread, shmwrite
        --
        bliz
        
RE: forking server
by AgentM (Curate) on Oct 13, 2000 at 00:23 UTC
    Let me look into my magic ball... Don't want to bother with shared memory? Pipes are yet another solution (since file descriptors are inherited by children.) I see Pipe Dreams in your future...What do you think WonderCat?
    pipe(fd1,fd2); #make kiddies (not that! is that all you think about? {slap}) close(fd1); #in kiddie #back to sexless parent close(fd2); write(fd1,$data,x); #the kid just got some info on the pipe! x bytes!

    AgentM Systems or Nasca Enterprises is not responsible for the comments made by AgentM- anywhere.
      pipe is a good solution for unidirectional communication.

      socketpair works for bi-directional communication. i'm using it in a forking server right now, and it works like a charm.

        agreed, but all socketpair does is create two pipes and link them to the file descriptors so it's the same. Note that this is solution in all legacy UNIX software.
        AgentM Systems or Nasca Enterprises is not responsible for the comments made by AgentM- anywhere.
Re: forking server
by Anonymous Monk on Oct 12, 2000 at 22:34 UTC
    The Above Send Function is defined as
    sub Send { my($client,$message) = @_; print $client "$message"; }
    nothing big, just forgot to include this sub-procedure in the above code. thnx again guys.
Re: forking server
by Anonymous Monk on Aug 03, 2006 at 17:07 UTC
    From one Anon Monk to the original poster Anon Monk: Thank You! I'm a begginer to Perl and had to jump into socket programming due to a project I'm working on and that code snippet from Perl CookBook helped me out a great deal for multithreading a server. There's a wealth of info everywhere but it's a little cumbersome to get to the specifics of what you're looking for (and on top to know the "keywords" to use for your searches). It's only been a few weeks since I started using Perl, and there's no way I would have been able to put together that type of code for handling multiple clients in such a short time from following the tutorials. I'll have to invest in that Perl Cookbook. Keep up the great help here! My salutations to all the monks! ;) ~AM~