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

I'm trying to write this bidirectional client/server communication application, it simulates a very basic chat example, the program works in part, it is supposed to asynchronously take input from either the server or the client and then show that input at the opposite end.. for now, it only takes input from the client and show it at the server session but can not accept anything from the server, It's not possible to type in at the server console the same way I do in the client one...

I basically wanted to fork each program into a child and parent, the parent process in the client accepts user input through STDIN, while the client's child process listens to the socket and takes input from the server...on the server side, the parent process would take input from the client whereas the child process is supposed to read STDIN from the console run by the server session (this is not taking place). For the past 2 hours I can not fathom where I am getting stuck, I went through the IO::Socket::INET and other suggested documentations, but it seems there's a trivial thing that I can't point a finger on and hence I am resorting to the Monks' help since I am yet laying my very early foundations on net programming.

#Server.pl #!/usr/local/bin/perl use strict; use warnings; use IO::Socket; my $server = IO::Socket::INET->new( LocalPort=> 1121, Type=>SOCK_STREAM, Reuse=>1, Listen=>1 )or die ("Could not create server"); while(my $client=$server->accept()){ my $child_pid; unless(defined ($child_pid=fork())){die"can not fork\n";} if(defined($child_pid)){ while(my $line = <$client>){ print "CLIENT says:$line\n"; } }else{ while(my $line = <>){ print $client "$line\n"; } } } close($server); <>;

#Client.pl use strict; use warnings; use IO::Socket; my $socket = IO::Socket::INET->new( PeerAddr =>"192.168.1.100", PeerPort =>1121, Proto =>"tcp", Type =>SOCK_STREAM, )or die ("Could not create client"); #fork the process my $child_pid; unless (defined($child_pid=fork())){die("can not fork")}; if(defined($child_pid)){ while(my $line = <STDIN>){ print $socket $line; } }else{ while(my $line = <$socket>){ print "SERVER says: $line\n"; } } close($socket); <>;
Thanking you for input and advice...

update1: The system I run this on is WinXP with Perl v5.10, the same system plays server and client at the same time..unfortunately, I tried different combinations of:

if(defined($child_pid)){} if($child_pid){} if($child_pid==1){} if($child_pid==0){}
on both server and client, the farthest I got it to half work is in the context posted above, I am reading about sockets once again in hopes I find clues but I am yet in need of assistance ...


Excellence is an Endeavor of Persistence. Chance Favors a Prepared Mind.

Replies are listed 'Best First'.
Re: Bidirectional Communication using sockets and forking
by almut (Canon) on Dec 28, 2009 at 12:30 UTC
    if(defined($child_pid)){

    You probably meant if($child_pid){, as testing defined $child_pid is true for both parent and child (pid==0), so they'll both execute the same code — which is most likely not what you want...

      That was the first initial thing I tried, at the Server.pl program, it would allow writing at that terminal session but whatever typed in is not conveyed through into the client session, the same thing is true vice versa...
        That was the first initial thing I tried, at the Server.pl program

        Did you have it in Client.pl, too?  With if (defined($child_pid)), the client would never execute its read loop either, i.e.

        while(my $line = <$socket>){ print "SERVER says: $line\n"; }

        With the mentioned change in both Server.pl and Client.pl, your code works fine for me... — except that you may not want to add another empty line every time the server sends a line to the client  (the superfluous "\n" in print $client "$line\n"; ).

Re: Bidirectional Communication using sockets and forking
by zwon (Abbot) on Dec 28, 2009 at 12:46 UTC

    I'd recommend to use POE or AnyEvent for your program. These packages will take care about low level details, so you should only write handlers for read/write events.

    Update: here's the example that uses AnyEvent. Start without arguments for server, or specify hostname as argument for client.

    use strict; use warnings; use AnyEvent; use AnyEvent::Socket; use AnyEvent::Handle; my $port = 7777; my $cv = AnyEvent->condvar; if ( $ARGV[0] ) { # client tcp_connect $ARGV[0], 7777, sub { my ($fh) = @_ or die "connect failed: $!"; create_handle($fh, 1); }; } else { # server tcp_server undef, 7777, sub { my ( $fh, $host, $port ) = @_; warn "Accepted connection from $host\n"; create_handle($fh, 0); }; } sub create_handle { my ($socket, $exit_on_close) = @_; my ( $shandle, $thandle ); my $destroy = sub { $shandle->destroy; $thandle->destroy; $cv->send if $exit_on_close; }; $shandle = AnyEvent::Handle->new( fh => $socket, on_error => $destroy, on_close => $destroy, ); $thandle = AnyEvent::Handle->new( fh => \*STDIN, on_error => $destroy, on_close => $destroy, ); $shandle->on_read( sub { print $shandle->rbuf; $shandle->rbuf = ''; } ); $thandle->on_read( sub { $shandle->push_write( $thandle->rbuf ); $thandle->rbuf = ''; } ); } $cv->recv;

    Update: fixed some issues

Re: Bidirectional Communication using sockets and forking
by zentara (Cardinal) on Dec 28, 2009 at 13:11 UTC
    ...try this for the client
    #!/usr/bin/perl -w use strict; use IO::Socket; my ( $host, $port, $kidpid, $handle, $line ); ( $host, $port ) = ('localhost',1200); my $name = shift || ''; if($name eq ''){print "What's your name?\n"} chomp ($name = <>); # create a tcp connection to the specified host and port $handle = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port ) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # split the program into two processes, identical twins die "can't fork: $!" unless defined( $kidpid = fork() ); # the if{} block runs only in the parent process if ($kidpid) { # copy the socket to standard output while ( defined( $line = <$handle> ) ) { print STDOUT $line; } kill( "TERM", $kidpid ); # send SIGTERM to child } # the else{} block runs only in the child process else { # copy standard input to the socket while ( defined( $line = <STDIN> ) ) { print $handle "$name->$line"; } }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
Re: Bidirectional Communication using sockets and forking
by zentara (Cardinal) on Dec 28, 2009 at 16:33 UTC
Re: Bidirectional Communication using sockets and forking
by afoken (Chancellor) on Dec 29, 2009 at 13:57 UTC
    The system I run this on is WinXP

    Don't expect fork() and exec() on Windows to behave like they do on Unix-like systems. Windows has neither a fork() nor an exec() system call.

    Perl has a rather thin emulation layer that fakes fork() and exec() well enough (using separate interpreter threads inside the same process) for some common cases. But the emulation is far from being perfect.

    Cygwin has a different approach, it emulates fork() using mutexes and shared memory across different processes, which is quite slow. And even cygwin can't emulate exec().

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)