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

Several days ago I was presented with a new problem at work that required socket communication, and I realised I had zero experience with sockets up until then.

So, off I went to perlipc and had a good read.

Looks easy enough, I thought. Pulled out the "interactive client" scripts and put them to work. The server script appears to have worked fine but...

Problem:

After the client script has finished 'initialising', it gets stuck in a deadlock.

It appears to be the client script that causes the lock, as any form of 'read' on the socket seems to block (even the twin thread for sending data) if there isn't anything there to read...

ioctl

So, reading around a bit more (I think) I found something similar to what I was experiencing. Various MS articles say/imply that INET Sockets are created in blocking mode by default, and what I'm trying to do should be done using non-blocking sockets. (Or am I way off base...?)

Well, I had a look at the method, Blocking  => 1 provided by  IO::Socket::INET however on ActivePerl 5.8.8 it dies with the error "Bad file descriptor"... (Incidentally, I tried this on ActivePerl 5.10.1 and it doesn't die, however the behaviour I'm seeing is still the same...)

So I tried $num = ioctl($handle, 0x8004667e, 1); which is returning undef... and hasn't altered the blocking behaviour... so maybe this is another problem altogether...

I may well have missed something very simple, however I am at my wits end for getting this to work reliably. Any insights and assistance would be much appreciated on how to create a simple pair of scripts for interactive client<->server communication.

(Thus far, my efforts have focused on IO::Socket::INET. I am having a go currently with Socket.pm in the interim incase I have better luck...)

The scripts in their current form (after numerous changes) are below in the ReadMore...

Client:

#!C:/perl/bin/perl -w use strict; use warnings; my $cvs_version = sprintf "%d.%03d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+ +)/; use IO::Socket; my ($host, $port, $kidpid, $handle, $line); unless (@ARGV == 2) { die "usage: $0 host port" } ($host, $port) = @ARGV; $handle = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port, Timeout => .1, # this seems to have no improvement on my + problem... #Blocking => 1, # if this is enabled Perl dies with "Bad +file descriptor" ) or die "can't connect to port $port on $host: $!"; my $num = 'a'; #just proving that ioctl has infact changed this to un +def after running... $num = ioctl($handle, 0x8004667e, 1); print "ioctl return code was [$num]\n"; #with or without the ioctl statement, reading from the socket... #... eventually blocks if nothing is there to be read... $handle->autoflush(1); print STDERR "[Connected to $host:$port]\n"; die "can't fork: $!" unless defined($kidpid = fork()); if ($kidpid) { # parent copies the socket to standard output $| = 1; #set STDOUT to non-buffered to demonstrate that two way... + # ...communication is possible up until there is nothing l +eft to read. my $buff; while (1){ my $num = sysread($handle, $buff, 4); #if there are zero byt +es waiting, the sysread appears ... if ($num >= 4) { #... to block, and eve +n blocks the child thread from writing to $handle... print $buff; } else { print "buffer is [$num] bytes big\n"; } sleep 1; } warn "Read from buffer stopped...\n"; kill("TERM" => $kidpid); } else { # child copies standard input to the socket while (defined ($line = <STDIN>)) { chomp $line; warn "echo before send: [$line]\n"; print $handle "$line\n"; warn "echo after send: [$line]\n"; } } exit;


Server:

#!C:/perl/bin/perl -w use strict; use warnings; my $cvs_version = sprintf "%d.%03d", q$Revision: 1.4 $ =~ /(\d+)\.(\d+ +)/; $| = 1; use IO::Socket; use Net::hostent; my $PORT = 9001; my $server = IO::Socket::INET->new( Proto => 'tcp', LocalPort => $PORT, Listen => 1, ReuseAddr => 1, ); die "can't setup server... $!... $@..." unless $server; print "[Server $0 accepting clients]\n"; my $num = 'a'; $num = ioctl($server, 0x8004667e, 1); print "ioctl return code was [$num]\n"; #returns undef sleep 1; while (my $client = $server->accept()) { $client->autoflush(1); $num = 'a'; $num = ioctl($server, 0x8004667e, 1); print "ioctl return code was [$num]\n"; #returns undef print $client "Welcome to $0; type help for command list.\n"; my $hostinfo = gethostbyaddr($client->peeraddr); printf "[Connect from %s]\n", $hostinfo ? $hostinfo->name : $client +->peerhost; print $client "Command? \n"; sleep 1; while (defined ($_ = <$client>) ){ print STDOUT $_; next unless /\S/; # blank line if (/quit|exit/i) { last; + } elsif (/date|time/i) { printf $client "%s\n", scalar localtime +; } elsif (/hello/i ) { print $client "G'day there\n"; + } else { print $client "Commands: quit date hello\n"; } } continue { print $client "Command? \n"; } close $client; }

Replies are listed 'Best First'.
Re: Sockets, ioctl and non-blocking reading on ActivePerl
by BrowserUk (Patriarch) on Jan 12, 2010 at 09:50 UTC

    Try

    my $arg = 1; $num = ioctl($server, 0x8004667e, \$arg);

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      dam... so simple, I don't know how I missed it. Many, many thanks!

      I'm guessing the reason why is that the OS needs a reference to a string containing a non-zero value, and not an actual integer.

      (incidentally I had also tried \1 thinking it was the same thing, but this complained that Can't coerce readonly REF to string... It makes much more sense now.)
Re: Sockets, ioctl and non-blocking reading on ActivePerl
by Anonymous Monk on Jan 12, 2010 at 10:57 UTC