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

Hello! Im looking to write a chat server for a collage project, where there can be say, 10 connections then each connection can chat with each other IRC Style, i am having great problems doing this, i have tried Folking, but that does not work (due to copying pf parent data), I would just like a simple, well commented non-folking using socket.pm demo, chat or not! Thanks for you time!

Replies are listed 'Best First'.
RE: Socket.pm nonblocking
by ahunter (Monk) on Jun 10, 2000 at 20:23 UTC
    I presume you mean forking rather than folking <g>

    I've written this sort of program quite a lot in the past - the call you want is select(), which will wait for data to arrive on a filehandle. You can also use IO::Select, but there is a major caveat - whatever you do, do *not* ever use the buffered IO functions, because madness lies that way. All the IO functions except sysread() and syswrite() are buffered. I've always been a bit dubious about what IO::Socket does, too, so I've always stuck with the standard Socket library and done things the hard way. I've actually written a module to deal with all this in a transparent way, and there are others available on CPAN.

    The vital bit from that code is:

    # Create the list of filedescriptors to check for activity $bits = $bobs = ''; foreach my $listen (@listeners) { vec($bits, $listen->{fd}->fileno(), 1) = 1; } # Do the business my ($rout, $wout, $eout); my $nfound = select($rout=$bits, $wout='', $eout='', undef); # Get our noses rubbed in it foreach my $listen (@listeners) { my $fn = $listen->{fd}->fileno(); # Check if this filedescriptor has data waiting if (vec($rout, $fn, 1)) { # This filedescriptor ($listen) is ready for # reading (or has been closed at the remote # end) } }

    Hopefully you can decipher that... When you get data on a file descriptor, read one byte using sysread() to avoid blocking. If sysread() returns undef, you have an error, and if it returns 0, you have EOF.

    In a chat room environment, it may be useful to have timeouts and things - the Time::HiRes library is useful for this. The last argument to select() is a timeout value, in decimal seconds. Use a function like this (UNTESTED!) to add timeouts:

    use Time::HiRes qw/time/; # Code will work without this, but works bet +ter with my @timers; sub addTimeout { my ($timeout, $callback, $calldata) = @_; push @timers, { when => time()+$timeout, callback => $callback calldata => $calldata }; @timers = sort { $a->{when} <=> $b->{when} } @timers; }
    And when you come to do the select, you call callbacks and work out the last value for select like so:
    $now = time(); while ($timers[0]->{when} <= $now) { my $timeout = shift @timers; &{$timeout->{callback}}($timeout->{calldata}); } select($rout=$bits, $wout='', $eout='', $timers[0]->{when}-$now);
    If a timer expires, $rout will be empty, and the callback will be called when the select() loop is next entered.

    Andrew.

    UPDATE: Tidied up the code, as I noticed it was a bit ugly, and not very good perl in places. Added some more helpful comments
    UPDATE: Timeouts are useful in this sort of environment, too.

Re: Socket.pm nonblocking
by Corion (Patriarch) on Jun 10, 2000 at 13:51 UTC

    I have a (small?) proof-of-concept-demo server I did, that was nonblocking, but this is a HTTP server (it also has push HTTP, I was thinking of using it as a shared database for volatile data like internet connection status and other status information of the network.

    The server assumes that it can always gobble in a complete client request/client line at one time, I was too lazy to also implement blocking for reading and not only for writing... You will have to add a buffer there as well ... Or maybe I've added that too - I don't remember, as this code is about 1 year old ;)

    The commands it has are "GET", "SET" and "UNSET" and some more, it has (if I remember right) even the possibility to access it with a "normal" http-browser, but it prefers clients that keep the connection open.

    The code should work as-is on any OS, but if you are setting it up for public use, you will surely add use warnings; (which I do via the command line) and use strict; (which I didn't use back then).

    Update : I added some documentation in the code, mainly around the select() statements (which aren't even select() but IO::Select statements ...).

    Update : Thanks to mdillon for suggesting I put the /o modifier on the constant REs. Also, by looking again over the code, this code readily will execute arbitrary Perl code with the SOURCE command. While this allowed me to create cool commands/keys like WHO in cgi-bin fashion, this will be a major security issue for you (or maybe not, as you will have 10 clients on your server that could be well-behaved ...)

    Update: I've moved the code into the Code Catacombs, it is now available (together with the ad-hoc documentation) at Push HTTP Server.

    Update: Currently the link is broken because the code node has vanished. We are doing the necessary rituals for revivification of the node. Please stand by.

      I was stupid again and instead of asking vroom to simply move this node, I created a new node with the documentation here. Stupid me. D'oh. Sorry for the inconvenience.