Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Sockets

by radagast (Sexton)
on Nov 03, 2000 at 16:38 UTC ( [id://39821]=perlquestion: print w/replies, xml ) Need Help??

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

I am working on a peer-peer chat system (which later should involve some interesting encryption). However, now I'm only doing the basics. I've set up one side to be a server and am using the Telnet in Windows to connect to the script on my unix box. My server is the following:

#!/usr/bin/perl use IO::Socket; $SIG{CHLD} = sub {wait()}; $main_sock = new IO::Socket::INET( LocalHost => 'b5', LocalPort => 6666, Listen => 5, Proto => 'tcp', Reuse => 1) or die $!; $line = ""; while ($new_sock = $main_sock->accept()) { $pid = fork(); die "Cannot fork: $!" unless defined($pid); if ($pid == 0) { # Child process while (defined ($buf = <$new_sock>)) { print "Client said: $buf\n"; } exit(0); } else { $line = <STDIN>; print $new_sock "$line"; } }

My question is with regards to non-blocking. When I first connect, I can write from my telnet session to my server no problem but I can only send one thing I type from my server. Somehow it is not getting to the print $new_sock "$line"; statement. I'm doing forking because it seemed less hairier than using IO::Select. This code has been borrowed and modified from Advanced Perl Programming.

Replies are listed 'Best First'.
Re: Sockets
by Fastolfe (Vicar) on Nov 03, 2000 at 18:58 UTC
    If you want to go with a forking model, the goal is for each thread to handle communications to/from the client. This way each thread has full state information about its individual client. For that reason, you probably want to set up some pipes as you fork off your children, and have the parent intercommunicate with the children using those pipes (instead of sending directly to the socket).

    Basically let the parent manage the children, let it pass information between the children, and let the children do everything related to communicating with the clients.

    The thing is, if you're forking already, do you really need non-blocking sockets? (Note: Your code isn't using non-blocking sockets as it is now.) In any event, when you're doing real-time interaction like this, you want to turn off buffering:

    $|=1; # and perhaps after your accept() call: $new_sock->autoflush;
    Once you get this working, you might consider trying to re-write it using IO::Select, using non-blocking sockets, via something like this:
    my $res = $sock->fcntl(F_GETFL, 0); $sock->fcntl(F_SETFL, $res | O_NONBLOCK);
    Getting something fully robust is going to take a lot more coding than you're doing now. You'll learn a lot in the progress, however. Good luck.
      If you want to go with a forking model, the goal is for each thread to handle communications to/from the client. This way each thread has full state information about its individual client. For that reason, you probably want to set up some pipes as you fork off your children, and have the parent intercommunicate with the children using those pipes (instead of sending directly to the socket).

      I'm interested in setting up pipes as the kids are forked! What manpages/books are best for reading how to do this? Could you cook up a code snippet or direct me to one?
RE: Sockets
by Corion (Patriarch) on Nov 03, 2000 at 18:27 UTC

    I have written a non-forking non-blocking server, which is available in the Code Vaults as Push HTTP Server. This is an implementation of a server that notifies all clients when data changes, and all in one thread.

    The protocol is some mutated http protocol, so I was able to test it with both, telnet and Internet Explorer.

RE: Sockets
by clemburg (Curate) on Nov 03, 2000 at 18:39 UTC

    Turn on autoflushing. It works OK like this on my NT box (fork() is there, too - kudos to ActiveState for fork implementation!).

    #!/usr/bin/perl use IO::Socket; $SIG{CHLD} = sub {wait()}; # turn autoflushing on $| = 1; $main_sock = new IO::Socket::INET( LocalHost => '127.0.0.1', LocalPort => 6666, Listen => 5, Proto => 'tcp', Reuse => 1) or die $!; $line = ""; while ($new_sock = $main_sock->accept()) { $pid = fork(); die "Cannot fork: $!" unless defined($pid); if ($pid == 0) { # Child process while (defined ($buf = <$new_sock>)) { print "Client said: $buf\n"; } exit(0); } else { $line = <STDIN>; print $new_sock "$line"; } }

    NOTE: (added to node later) - sorry - I did not get your point right - the script does not remedy the problem you talked about - however, turning autoflushing on is a good idea for networking code, anyway.

    Christian Lemburg
    Brainbench MVP for Perl
    http://www.brainbench.com

      Curious, what did this snippet do for you?
      $SIG{CHLD} = sub {wait()};

      and what about this part?
      } else { $line = <STDIN>; print $new_sock "$line"; }


      Also, how do you get the server to hang up on the client? I tried something like:
      chomp($buf); exit(0) if ($buf eq 'q');

      But the client (I use telnet) connected to the server holds fast until I hit <CR> on the console that's running the server.

        Well, I just copied the code over from the question to illustrate, but ...

        • $SIG{CHLD} = sub {wait()};

          This ensures proper cleanup of child resources after child terminates. "When a process exits, its parent is sent a CHLD signal by the kernel and the process becomes a zombie until the parent calls wait or waitpid. If you start another process in Perl using anything except fork, Perl takes care of reaping your zombied children, but if you use a raw fork, you're expected to clean up after yourself." (from Camel, 3rd ed.). I don't know really if this is needed on Win32, but it is supported, since "wait() and waitpid() can be passed a pseudo-process ID returned by fork(). These calls will properly wait for the termination of the pseudo-process and return its status." (from ActiveState Perl docs).

        • The else clause is the main server process. It blocks reading one line from STDIN and sends that to the newly forked socket connection.

        This is probably not the best example code for writing servers and clients. Have a look at the Perl Cookbook, chapter 17.

        Christian Lemburg
        Brainbench MVP for Perl
        http://www.brainbench.com

Re: Sockets
by fundflow (Chaplain) on Nov 03, 2000 at 18:28 UTC
    It seems okay and works here (Solaris 2.6).
    Maybe try a different telnet client.
    It doesn't accept multiple clients though.

    BTW, since you are doing only the server side, why do you implement a client side as well?

    Two other small comments:
    1. You don't need the $line=""
    2. You can remove the 'defined'

Re: Sockets
by meonkeys (Chaplain) on Apr 24, 2001 at 13:51 UTC
    Curious, what did this snippet do for you?
    $SIG{CHLD} = sub {wait()};

    and what about this part?
    } else { $line = <STDIN>; print $new_sock "$line"; }


    Also, how do you get the server to hang up on the client? I tried something like:
    chomp($buf); exit(0) if ($buf eq 'q');

    But the client (I use telnet) connected to the server holds fast until I hit <CR> on the console that's running the server.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://39821]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (3)
As of 2024-04-20 02:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found