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

Hi, I'm trying to use IO::Select::can_read to implement a server that accepts a connection and waits 5 seconds before returning a response. For some reason I cannot get it to accept more than one connection at a time. I run the code below, and then from two browser windows I hit the url almost simultaneously, but the second browser doesn't get connected until the first has received the response. I was expecting that both connections would get accepted as soon as the requests are sent.
#!/usr/bin/perl -w use strict; use IO::Select; use IO::Socket; my $port = 8080; my $lsn = new IO::Socket::INET(Listen => 5, LocalPort => $port, Proto +=> 'tcp', ReuseAddr => 1); my $sel = new IO::Select( $lsn ); my $id = 1; print "Listening on socket $port\n"; my $error = 0; while(!$error) { my @ready = $sel->can_read(1.0); if (scalar @ready) { for my $fh (@ready) { if($fh == $lsn) { my $client = $lsn->accept; ConnHandler->new($sel, $client, $id++); } elsif(my $hdlr_ref = ConnHandler::GetHandler{"$fh"}) { print "Found handler\n"; $$hdlr_ref->ReadData(); } else { print "ERROR: failed to look up $fh in {" . join(',', ConnHand +ler::GetKeys()) . "}, of size " . scalar ConnHandler::GetKeys() . "\n"; $error = 1; } } } else { # Timeout for my $handler (ConnHandler::GetHandlers()) { if ($handler->WaitExpired()) { $handler->SendMsg("This is only a test"); $handler->Close(); my $fh = $handler->{fh}; ConnHandler::DeleteHandlerByKey("$fh"); } } } } exit 0; ################ package ConnHandler; my %Handlers = (); my $resp_delay = 5; my $debug = 1; sub GetHandler($) { my $key = shift; return \$Handlers{$key}; } sub DeleteHandlerByKey($) { my $key = shift; delete $Handlers{$key}; } sub GetHandlers() { return values %Handlers; } sub GetKeys() { return keys %Handlers; } sub new($$$$) { my $pkg = shift; my $self = bless { sel => shift, fh => shift, id => shift, ts => tim +e } => $pkg; my $fh = $self->{fh}; $self->{sel}->add($fh); $Handlers{"$fh"} = $self; print "ConnHandler::new() [id:" . $self->{id} . "]\n"; } sub ReadData($) { my $self = shift; my $msg; sysread($self->{fh}, $msg, 512); my $ts = localtime; print "[$ts, id=" . $self->{id}. "] RECV $msg\n"; } sub WaitExpired($) { my $self = shift; my $dif = time - $self->{ts}; my $flag = $dif >= 5; my $ts = localtime; if ($flag) { print "[$ts, id=$self->{id}] EXP: $dif\n"; } else { print "[$ts, id=$self->{id}] NOT EXP: $dif\n"; } return $flag; } sub SendMsg($$) { my $self = shift; my $msg = shift; syswrite($self->{fh}, $msg); my $ts = localtime; print "[$ts, id=$self->{id}] SENT $msg\n"; } sub Close($) { my $self = shift; $self->{sel}->remove($self->{fh}); $self->{fh}->close; my $ts = localtime; print "[$ts, id=$self->{id}] CLOSED\n"; }
The output for a run is:
Listening on socket 8080 ConnHandler::new() [id:1] Found handler [Tue Mar 4 06:49:31 2008, id=1] RECV GET /test.htm HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.1 +2) Gecko/20080201 Firefox/2.0.0.12 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 +,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive [Tue Mar 4 06:49:32 2008, id=1] NOT EXP: 1 [Tue Mar 4 06:49:33 2008, id=1] NOT EXP: 2 [Tue Mar 4 06:49:34 2008, id=1] NOT EXP: 3 [Tue Mar 4 06:49:35 2008, id=1] NOT EXP: 4 [Tue Mar 4 06:49:36 2008, id=1] EXP: 5 [Tue Mar 4 06:49:36 2008, id=1] SENT This is only a test [Tue Mar 4 06:49:36 2008, id=1] CLOSED ConnHandler::new() [id:2] Found handler [Tue Mar 4 06:49:37 2008, id=2] RECV GET /test.htm HTTP/1.1 Host: localhost:8080 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.1 +2) Gecko/20080201 Firefox/2.0.0.12 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9 +,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive [Tue Mar 4 06:49:38 2008, id=2] NOT EXP: 1 [Tue Mar 4 06:49:39 2008, id=2] NOT EXP: 2 [Tue Mar 4 06:49:40 2008, id=2] NOT EXP: 3 [Tue Mar 4 06:49:41 2008, id=2] NOT EXP: 4 [Tue Mar 4 06:49:42 2008, id=2] EXP: 5 [Tue Mar 4 06:49:42 2008, id=2] SENT This is only a test [Tue Mar 4 06:49:42 2008, id=2] CLOSED
Is there an option I'm missing when creating the listening socket? Any pointers would be appreciated. Thanks, -jc

Replies are listed 'Best First'.
Re: IO::Select::can_read() is serializing connections.
by pc88mxer (Vicar) on Mar 04, 2008 at 17:01 UTC
    Might be a browser issue. Note the keep-alive option being sent by the browser. It might be waiting to re-use the connection for the second request.

    The above code worked for me using two wgets in two different windows:

    [Tue Mar 4 11:00:25 2008, id=1] NOT EXP: 1 [Tue Mar 4 11:00:25 2008, id=2] NOT EXP: 1 [Tue Mar 4 11:00:26 2008, id=1] NOT EXP: 2 [Tue Mar 4 11:00:26 2008, id=2] NOT EXP: 2 [Tue Mar 4 11:00:27 2008, id=1] NOT EXP: 3 [Tue Mar 4 11:00:27 2008, id=2] NOT EXP: 3 [Tue Mar 4 11:00:28 2008, id=1] NOT EXP: 4 [Tue Mar 4 11:00:28 2008, id=2] NOT EXP: 4 [Tue Mar 4 11:00:29 2008, id=1] EXP: 5 [Tue Mar 4 11:00:29 2008, id=1] SENT This is only a test [Tue Mar 4 11:00:29 2008, id=1] CLOSED [Tue Mar 4 11:00:29 2008, id=2] EXP: 5 [Tue Mar 4 11:00:29 2008, id=2] SENT This is only a test [Tue Mar 4 11:00:29 2008, id=2] CLOSED
      A browser issue it is. How dimwitted is that? Thanks a lot for the help guys! -jc
Re: IO::Select::can_read() is serializing connections.
by ikegami (Patriarch) on Mar 04, 2008 at 16:40 UTC
    Am I blind or did you forget to call $sel->add($lsn);?

    Blind it is :)