http://www.perlmonks.org?node_id=6535

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

I'm playing with sockets (I'm on 'use Socket' not 'use IO::Socket' - I want to play with the low-level stuff then I'll look at the high-level version) I'm doing ok except for the infuriating 'does the same as the system call of the same name' which I find thoughout the manual. Problem is a windows system doesn't document unix system call very well! - Back to the point - my accept is working ok - but is a 'blocking' call (i.e: waits until data arrives before returning). I assume there is a non-blocking version of accept which I can use to check if there is data present but will return immediately if none is present. Problem is I don't know how to do this. I'm assuming it's a setsockopt of some kind - or a parameter to accept, but the manual won't tell me...;-(. Help please. Many thanks - cool web site by the way! Richard.

Replies are listed 'Best First'.
Re: How do I do a non-blocking accept?
by thecap (Initiate) on Apr 07, 2000 at 07:09 UTC
    You should check out perl-chat in Tkil's Perl Examples. It uses the low level calls to select, sysread and syswrite. The diffcult thing about using select is that you can't use <SOCK> and print SOCK with nonblocking io.

    When you move to the higher level IO::* you might find Tkil's port-forward a useful guide.

Re: How do I do a non-blocking accept?
by chromatic (Archbishop) on Mar 30, 2000 at 22:01 UTC
    My notes recommend something like this (assuming Connection is your socket descriptor?):
    use Fcntl; fcntl(Connection, F_SETFL, O_NONBLOCK) or die "can't set non blocking: $!";
    I suppose you could also wrap your Socket->accept() in alarm() calls....
Re: How do I do a non-blocking accept?
by Matts (Deacon) on Aug 25, 2004 at 11:06 UTC
    First, you setup your server:
    my $server = IO::Socket::INET->new( %params, Blocking => 0 );
    Then make it non-blocking, for perls that don't obey the Blocking param above:
    IO::Handle::blocking($server, 0);
    Then you add that server to your select vector watching for read events. When a client is waiting to be accept()ed, the select will return your server as ready to read. Just compare the sockets using == to determine if the socket is your server, and if it is, issue accept(). Note that by the time you get to it the client may have changed his mind, and so your accept could return EWOULDBLOCK or EAGAIN, so you have to check for that.
Re: How do I do a non-blocking accept?
by mattr (Curate) on Sep 01, 2000 at 09:24 UTC
    If you like playing with lowlevel interfaces, you might like to install Cygwin (sources.redhat.com/cygwin) which will give you a unix shell and a basic system with compiler, man pages, etc. Then maybe you could download packages from gnu.org and read their manpages or try them out from the command line. Seems to use Unix sockets with the option of recompiling things to use Windows sockets according to the faq.

    Hope this helps,
    Matt

Re: How do I do a non-blocking accept?
by davidnicol (Acolyte) on Aug 22, 2004 at 04:48 UTC
    you have to set the listening socket to nonblocking, as documented in the man page for accept. This can be done with fcntl rather than setsockopt. As explained above here at How do I do a non-blocking accept?.
Re: How do I do a non-blocking accept?
by davidnicol (Acolyte) on Aug 23, 2004 at 01:46 UTC
    I was flummoxed by the lack of the nonblocking flags on sockets on microsoft windows, until I realized that inside a nonblocking accept(2) call, accept(2) is just going to have to do something like a select(2).

    So a non-blocking accept IS NEVER REQUIRED because you can either

    • just accept one connection per iteration -- the other ones will still be there on the next iteration or
    • When you have a hot listening socket, accept the first connection AND THEN SELECT AGAIN ON IT AND ONLY IT.

    So instead of

    foreach (@Listeners){ vec($rout,fileno($_),1) or next; # listener is nonblocking, goes until # expected accept failure while (accept(my $NewServer, $_)){ push @Clients, $NewServer
    you just do do
    foreach (@Listeners){ vec($rout,fileno($_),1) or next; # listener is blocking, but we # know this listener is hot if (accept(my $NewServer, $_)){ push @Clients, $NewServer }else{ log "accept: $!" }
    Or, mock up the accept-em-all-NOW method without clumsily making accept fail:
    foreach (@Listeners){ vec($rout,fileno($_),1) or next; # listener is blocking, but we # know this listener is hot acc: accept(my $NewServer, $_) and push @Clients, $NewServer; # select again to see if there's another my $rvec; vec($rvec,fileno($_),1) = 1; select($rvec,undef,undef,0); vec($rvec,fileno($_),1) and goto acc;

    The difference is

    by accepting all connections immediately we will possibly have more connections going, more suddenly. If we have a limit on our number of open connections, we only need to check it once per loop to keep from overrunning it.

    How much can it affect throughput? It's like asking is it better for an office building to have a one-person revolving door that spins fast or a family-size one that spins slow.

Re: How do I do a non-blocking accept?
by btrott (Parson) on Mar 31, 2000 at 01:57 UTC
    Also, you may already know this, but if you use the non-blocking accept, I believe you can check $! for EAGAIN (or EWOULDBLOCK on BSD and perhaps other systems) to check whether there are no connection requests (as opposed to accept just failing for some other reason). Maybe not on your system, though...

    As an aside: I know it's quite annoying to be pointed towards man pages that you don't have on your system... here's a good source of man pages for OpenBSD, and here's some docs (in troff source) for Unix Version 7.

Re: How do I do a non-blocking accept?
by Anonymous Monk on Nov 07, 2001 at 14:32 UTC

    THIS WORKS!!!!

    (Tested on Win32 with indigo and activestate perl)

    vec($bits1,fileno($server),1)=1;
    while(1) {
     $rc=select($rout1=$bits1,$wout1=$bits1,$eout1=$bits1,0.5); # poll
     print "select($rout1,$wout1,$eout1,0)=$rc\n";
    }
    

    When there's a connection, $rout1 gets set.

Re: How do I do a non-blocking accept?
by posicat (Initiate) on Oct 11, 2004 at 19:54 UTC
    I spent a few hours and finally found this paramater you can pass to the IO::Socket::INET ... Timeout =>
    my $sock = new IO::Socket::INET ( LocalPort => '1401', Proto => 'tcp', Listen => 1, Reuse => 1, Timeout => .1, );
    After .1 seconds it times out the accept command and continues on through your loop happily. As an added benefit this helps you prevent race conditions.
Re: How do I do a non-blocking accept?
by Anonymous Monk on Mar 27, 2002 at 04:23 UTC
    fcntl(sockfd,F_SETFL,FNDELAY) can be used to set the file descriptor in to a non blocking mode, any blocking system call on this descriptor including accept will return immediately and set the errno=EWOULDBLOCK regards rakesh soni
Re: How do I do a non-blocking accept?
by Chaosje (Initiate) on Mar 19, 2016 at 15:28 UTC
    Under Windows this will work:
    if ($^O =~ /win/i) { my $nonblocking=1; ioctl($socket, 0x8004667e, \$nonblocking); }
Re: How do I do a non-blocking accept?
by Anonymous Monk on Jul 13, 2001 at 18:21 UTC
    I come across the same problem recently while working with windows,and i found a solution for that. I just know that there r diffrent ways of doing this,but what i don't know is which is the best one. My solution is here: suppose your accept() is in main thread then definitely it would block, so create a worker thread and put the accept in that worker thread,so here we go process never hangs or goes on a indefinite wait... The reason it work ,may be like this: a thread has to return to main thread, if it continues to be in suspended state for some finite state ( It is the OS Windows magic, we don't know what it does inside ???) so keep track of a global variable,if accept() succeeds in the worker thread then tune it, use the same in the main thread to check out the status of the accept() any reply is welcome ---------Fhani

    Originally posted as a Categorized Answer.

Re: How do I do a non-blocking accept?
by posicat (Initiate) on Oct 11, 2004 at 19:52 UTC
    I spent a few hours and finally found this paramater you can pass to the IO::Socket::INET ... Timeout => Ex: my $sock = new IO::Socket::INET ( LocalPort => '1401', Proto => 'tcp', Listen => 1, Reuse => 1, Blocking => 0, Timeout => .1, ); After .1 seconds it times out the accept command and continues on through your loop happily. As an added benefit this helps you prevent race conditions.

    Originally posted as a Categorized Answer.

Re: How do I do a non-blocking accept?
by dl748 (Initiate) on Jun 02, 2001 at 02:53 UTC
    ACCEPT is a blocking function. Under Unix/perl i know you can't set it up as nonblocking.