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

I posted earlier here about problems I was having with socket programming.

Well I'm having a few more :)

I can't get my server to accept multiple (namely 2) clients. It accepts the first client fine and prints out the appropriate ready message, but doesn't even try to allow for the second one to connect.

Do I need to use IO::Select somehow? I've contacted several sources (including perl.com and google) trying to figure out how to allow a server to deal with multiple clients and most of um seem to point to Select.

My code is posted below. Any help would be much appreciated. I've primarily trying to get the server to accept 2 clients as handles, then communicate bi-directionally between each client and the server.

Here's the client code:

#!/usr/bin/perl -w use IO::Socket; # create a tcp connection to the specified host and port $client = IO::Socket::INET->new( Proto => "tcp", PeerAddr => 'localhost', PeerPort => '6969' ), or die "can't connect to server: $!"; print $client "READY\n"; while (<$client>) { if (/GO/i) { srand; # seed the random number function $num = int (rand 3); # get a random integer, 0 through 2 if ($num == 0) { print $client "Rock\n"; } elsif ($num == 1) { print $client "Paper\n"; } elsif ($num == 2) { print $client "Scissors\n"; } } if (/STOP/i) { last; } } close $client;
Here's the server code:
#!/usr/bin/perl use IO::Socket; $server = IO::Socket::INET->new( Proto => 'tcp', LocalHost => 'localhost', LocalPort => '6969', Listen => SOMAXCONN, Reuse => 1); die "can't setup server" unless $server; print "[Server $0 accepting clients]\n"; print "Rock, Paper, Scissors: $ARGV[0] iterations\n\n"; while ($client = $server->accept()) { print "[Server $0 receiving client $client]\n"; while (<$client>) { if ($play_game) { #play game in here } elsif (/READY/i) { $players{$client} = ++$players_ready; print "Player $players{$client}: Ready\n"; } if ($players_ready == 2) { print $client "GO\n"; $play_game = +1; } } close $client; }

Replies are listed 'Best First'.
Re: Bi-directional communication between 2 clients and 1 server over a single socket
by AgentM (Curate) on Oct 10, 2000 at 08:53 UTC
    Personally , I would use pipes, but since that's not an option on this extra credit homework assignment, I'll give you something more tangible. As I understood it, you need to handle BOTH clients at the same time, so the above will not work. You will need to save the returned accepted connections to separate variables. Here's the code I promised in the CBox. You really should be using IO::Socket::Unix but its not really a big deal for this homework. A better solution would be perform a UNIX select(2) but since the operations performed are so simple and fast the processes are all in the same running group, I have excluded this. Also, I do not see a proper bind, but since I am unfamiliar with IO::Socket, perhaps this is my mistake.
    --initialization as you have it-- $cli1=$server->accept(); $cli1->recv($data,10,0); die 'Aycaramba!' if $data ne 'READY' ; $cli2=$server->accept(); $cli2->recv($data,10,0) die 'Aycaramba again, mon!' if $data ne 'READY'; #$maxgames is predefined (passed from the exec!) #$cli1score #$cli2score for(1..$maxgames) { $cli1->print('GO'); $cli2->print('GO'); #ready to rock 'n' roll! #just wait for the input from the first player $cli1->recv($cli1var,10,0); $cli2->recv($cli2var,10,0); #now play the game my %win=('Rock',1,'Paper',2,'Scissors',3); my $avar1,$avar2; #warning: very little error-handling, but do you need it? $avar1=$win{$cli1var}; $avar2=$win{$cli2var}; if($avar1==$avar2){} elsif($avar1-1 == $avar2){$cli1score++;} elsif($avar1==1 &amp;amp;&amp;amp; $avar2==3){$cli1score++;} else{cli2score++;} } #end long game loop $cli1->print('STOP'); $cli2->print('STOP'); print "total scores: player 1: $cli1score, player 2: $cli2score, playe +r user: EXTRA CREDIT POINTS!\nHave a nice day\n"; $cli1->close(); $cli2->close();
    This is untested, but logically functional. This is the way you want to head in your program. The client is even simpler. All it does is connect, block on a read in an infinite loop for 'GO' and pick a random string of the three to send. Thus, the little doc's comment about the string passing challenge is crushed by the superiority of Perl!!!! Hahaha! Anyway, try to fork() a client like this:
    --initialization stuff as you have it-- #$serv is the opened handle to the server my %value=(1,'Rock',2,'Scissors',3,'Paper'); while(1) { $serv->recv($data,10,0); if($data eq 'STOP') die "server asked for stop\n"; $serv->print($value{int(rand(4)}); }

    I left you kind of a mess with undeclared vars and stuff but the core functionality is there.

    dude, that's it! go for the gold and good luck. if you can slip it in, name one of the variables after me! 8-)

    AgentM Systems or Nasca Enterprises is not responsible for the comments made by AgentM- anywhere.

      I don't know if the individual asking the question is doing this for a homework assignment, but if he is, at least he made an attempt to start the program and only asked for help when he ran into problems. This is better than some of the people that have just been posting their homework questions without even making an effort to solve the problem.

        i was discussing his homework problem in the chatterbox with him before (as i mentioned in the post). i also mentioned that i woudl post the code, and i did. he actuall ysent me the link to the assignment and i did it for him. how is this for "effort"? perhaps you posted to the wrong guy...
        AgentM Systems or Nasca Enterprises is not responsible for the comments made by AgentM- anywhere.
Re: Bi-directional communication between 2 clients and 1 server over a single socket
by merlyn (Sage) on Oct 10, 2000 at 07:08 UTC
    Do I need to use IO::Select somehow? I've contacted several sources (including perl.com and google) trying to figure out how to allow a server to deal with multiple clients and most of um seem to point to Select.
    Yes, you need to either fork to handle each conversation, or use IO::Select (or the lower-level interfaces) to "thread" your interactions. There are examples in perlman:perlipc.

    -- Randal L. Schwartz, Perl hacker

      k, thanx...I ended up rewriting the code entirely... since I know I only have 2 connections, I didnt need the accept while loop...I just hard coded each socket read for each player...made the problem ten times easier.

      However, playing around with sockets and select, etc was quite an educational (and frustrating :> ) experience...

      Can't wait til my next socket project.

      Thanx to all of u that helped me w/ this project.

      Magius_AR