perl, read blocking using IO::Select and IO::Socket::INET

nonblocking, perl, sockets, unix

Solution

Rather than reading available data, you're reading until you read a newline. Always use `sysread`.

Change

    elsif ($sock == $event_emiter) {
        unless($sock->eof){
            my $recv_data = $sock->getline;
            warn "[event socket] LOL '$recv_data'\n";
        } else {
            $sel->remove($sock);
            $sock->close;
            $event_emiter = undef;
            warn "[socket] disconnect\n";
        }
    }

to

    elsif ($sock == $event_emiter) {
        our $buf; local *buf = \$bufs{$fh};  # alias $buf = $bufs{$fh};

        my $rv = sysread($fh, $buf, 64*1024, length($buf));
        if (!$rv) {
            if (defined($rv)) {  # EOF
                # ... Handle anything left in $buf ...
            } else {  # Error
                # ... Handle error ...
            }

            delete $bufs{$fh};
            $sel->remove($sock);
            $sock->close;
            $event_emiter = undef;
            warn "[socket] disconnect\n";
            next;
        }

        while ($buf =~ s/^(.*)\n//) {
            warn "[event socket] LOL '$1'\n";
        }
    }

And add `my %bufs;` outside the select loop.

Problem

This server works fine but if I do this bash$ (echo -n "abcd" ;sleep 50 ; echo "efgh") | nc localhost 9090 The server blocks for 50 seconds.In my complete code I have more than one `IO::Select::INET`. I have another socket listen other port (1234), and I can't process anything in that port while the server is blocking by the sleep. I try change the `getline` by `getc` but I only read the first letter "a" and it blocks. Someone can help me? ``` use common::sense; use IO::Select; use IO::Socket; use constant PORT1 => 9090; use constant TIMEOUT => 1; my $event_socket = new IO::Socket::INET(Listen => 1, LocalPort => PORT1, ReuseAddr => 1) or die "Can't bind event_socket: $@\n"; my $sel = IO::Select->new; $sel->add($event_socket); my $event_emiter = undef; while(1){ foreach my $sock (my @ready = $sel->can_read(TIMEOUT)) { if ($sock == $event_socket) { my $new = $event_socket->accept; binmode($new, ":encoding(UTF-8)"); $sel->add($new); $event_emiter=$new; warn "[event socket] connect from ",$new->peerhost, "\n"; } elsif ($sock == $event_emiter) { unless($sock->eof){ my $recv_data = $sock->getline; warn "[event socket] LOL '$recv_data'\n"; } else { $sel->remove($sock); $sock->close; $event_emiter = undef; warn "[socket] disconnect\n"; } } else { $sel->remove($sock); $sock->close; warn "[socket] disconnect\n"; } } } ```

Original source