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

Hi All,
Have a question about Sockets. Here is my problem; using the code below (perl black book) I am trying to setup communication between hosts. Both start and run fine. The problem is, if I start the 'Server' it will only allow me to talk back to one of the clients connected.
Example:
one server, two clients.
Both clients connect, but the server will only talk to the first client connected.
Anything sent from the second client is buffered till the first client is killed, and then is instantly printed on the server side.
am I forking too late in the code?
Please let me know.. thanks.

## CLIENT ##

#!/usr/local/bin/perl -w #use strict; use IO::Socket; #my ($socket, $child_pid, $line) = (); $socket = IO::Socket::INET->new ( PeerAddr => '123.123.123.123', # changed ip for post =) PeerPort => '5123', Proto => "tcp", Type => SOCK_STREAM ) or die "Could not create client: $!\n"; unless (defined($child_pid = fork())) {die "Can not fork: $!\n"}; if ($child_pid) { while ($line = <>) { print $socket $line; } } else { while ($line = <$socket>) { print "SERVER: $line"; } }

## SERVER ##
#!/usr/local/bin/perl -w #use strict; use IO::Socket; #my ($server, $client, $child_pid, $line) = (); $server = IO::Socket::INET->new ( LocalPort => '5123', Type => SOCK_STREAM, Reuse => 1, Listen => 5 ) or die "Could not create server: $!\n"; while ($client = $server->accept()) { unless (defined($child_pid = fork())) {die "Can not fork: $!\n +"}; if ($child_pid) { print "CLIENT: $client\n"; while ($line = <$client>) { print "CLIENT: $line"; } } else { while ($line = <>) { print $client $line; } } }

Edit: BazB, added code tags.

Replies are listed 'Best First'.
Re: To Fork for Not to Fork
by iburrell (Chaplain) on Nov 10, 2003 at 22:05 UTC
    The code as written is using forking to separate reading and writing so they don't block. It is not using forking to serve multiple clients at once. Also, the server is reading from stdin; it is doing the equivalent of netcat and piping through the network connection.

    In a forking server, the child process handles the request, and the parent process goes back to waiting for new clients. If you want to separate reading and writing, the child process can fork again, with the grandchild handling reading and the child writing.

Re: To Fork for Not to Fork
by pg (Canon) on Nov 10, 2003 at 21:12 UTC

    The problem with your server code is that, once it gets into that while loop, there is another inner while loop to communicate with the first client (Your code looks like it is reading from STDIN, but I think it is a simple typo on your side, as it makes less sense, so i take it as reading from client socket). It simply stuck there as long as the first client is connected. Once the first client is disconnected, the server starts to get empty strings (or undef, platform dependent), and quit the inner while loop. Only then, your execution path goes back to that accept statement.

    One way is to make your server multi-threaded. One thread keeps accept connections, when the other one reads from clients by using select to determine readable ones.

    Another choice is to spawn thread for each connection.

    Third solution. Actually it is even okay to not use multi-thread or fork, simply make sure your server will accept new connections once a while, instead of stuck with one client.

    Do a super search with my name as author, and search for key words: Socket and threads. You can find quite a few code samples for different purposes. They are helpful.

    Here is a sample to spawn a new thread for each connection:

    server: use strict; use IO::Socket; use threads; $|++; my $server = IO::Socket::INET->new(LocalPort => '5123',Reuse => 1,List +en => 5) or die "Could not create server: $!\n"; while (my $client = $server->accept()) { threads->create(\&talk_with_one_client, $client)->detach(); } sub talk_with_one_client { my $client = shift; while (my $line = <$client>) { print $line; chomp $line; print $client $line + 1, "\n"; } } client: use strict; use IO::Socket; $|++; my $socket = IO::Socket::INET->new(PeerAddr => "localhost", PeerPort = +> '5123', Proto => "tcp") or die "Could not create client: $!\n"; print $socket "1\n"; while (my $line = <$socket>) { print $line; chomp $line; sleep 1; print $socket $line + 1, "\n"; }

      I downvoted this reply because it doesn't answer the question. (It also demonstrates two useless uses of autoflush, but that's a different problem.)

      The original code has two main problems: child processes don't exit and parent processes block on STDIN. You can certainly possibly avoid these errors by throwing away the example code and rewriting it according to a different concurrency model, but that really doesn't help the original poster learn anything forking.

        The use of $|++ is not useless. I can agree there is no need for it to appear in my client code, but there is a good reason for it to appear in the server code.

        From time to time, I saw posts complaining their codes are not working simply because of the confusion caused by not-flushed debug infos, which can be easily fixed by setting $|. It is a better practice in multi-threading programming ,to turn on autoflush regardless, unless it is forbidden by the nature of your application. Instead of saying it is useless, I see it as a better style.

        I didn't notice the original post is reading from STDIN, instead of the client socket. That's my mistake, but it is understandable, as that doesn't make sense there. I think that's just a simple mistake, and the original author's purpose is to read from the client. However as I pointed out in my original post, his structure is wrong, even changing it to read from client, he will still stuck communicating with the first client, and not going back to accept() until the first client ended.

        BTW, next time there is no need to tell me about the down vote. I may think about the reason for getting down voted, but not interested in knowing who did it. Please keep it for your privacy.

        As for the technical side, I am a strong promoter for threads, and that was where I was getting. I see it much more important to get the direction correct, than anything else.

Re: To Fork for Not to Fork
by castaway (Parson) on Nov 11, 2003 at 08:03 UTC
    I'm surprised no-one mentioned IO::Select here yet, which negates the need for either threads or forks. (Though of course with IO::Select, everything is done sequentially)..

    C.

Re: To Fork for Not to Fork
by BUU (Prior) on Nov 11, 2003 at 07:58 UTC
    Your problem, at least on the serverside, is your extra loop in the 'else' statement. It shouldn't be there.
    else { while ($line = <>) { print $client $line; } }
    That part. Replace the while(){} with next. Then put an exit after the else's closing bracket. What happens is the code accepts a connection, then it forks and the child process goes into an infinite loop reading from the client, while the parent process goes into an infinite loop reading from stdin <>. Thus the child never exits and the server never accepts another client.

    Cannonical example from perl cook book:
    #assuming $server initialized while($child = $server->accept() ) { next if $pid = fork(); #CHILD STUFF exit; #kill the child process }
    Forking servers are vastly simpler then single process servers using select.
Re: To Fork for Not to Fork
by Anonymous Monk on Nov 11, 2003 at 04:36 UTC

    As I've learned it, the best way to handle multiple clients on the server side without forking is with select($$$$). This will allow you to poll for which client happens to have sent some bytes this time around.

    Also, as written, the server will never send text that it reads from STDIN to any client except the first one, since <> will keep returning undef.