Reputation: 40499
The following piece of code is supposed to read a few, undetermined in number, lines from a socket.
use warnings;
use strict;
use IO::Socket::INET;
my $server = shift;
my $port = shift;
my $sock = new IO::Socket::INET (
PeerAddr => $server,
PeerPort => $port,
Proto => 'tcp',
Timeout => 1,
Blocking => 0
)
or die "Could not connect";
while (my $in = <$sock>) {
print "$in";
}
print "Received last line\n";
Unfortunately, the $in = <$sock>
part is blocking although I have set Blocking => 0
and the server does not send any more text. Hence, Received last line
won't be printed.
So, I tried to improve the behavior with use IO::Select
:
use warnings;
use strict;
use IO::Socket::INET;
use IO::Select;
my $server = shift;
my $port = shift;
my $sock = new IO::Socket::INET (
PeerAddr => $server,
PeerPort => $port,
Proto => 'tcp',
Timeout => 1,
Blocking => 1
)
or die "Could not connect";
my $select = new IO::Select;
$select -> add($sock);
sleep 1;
while ($select -> can_read) {
my $in = <$sock>;
print $in;
}
This second approach only prints the first sent line, then seems to block for ever.
Since I have seen such examples working, I believe the problem is Windows, on which I am trying to run these scripts.
Is there a way how I can achieve a non blocking read?
Upvotes: 0
Views: 82
Reputation: 385847
When using select
(used by can_read
), it defies the purpose to follow up with blocking IO. You must also avoid buffering IO because the system (i.e. select
) doesn't know about any data in your library's buffers. This means you can't mix select
with read
, readline
(aka <>
and <$fh>
) and eof
. You must use sysread
.
my %clients;
for my $fh ($select->can_read()) {
my $client = $clients{$fh} //= {};
our $buf; local *buf = $client->{buf} //= ''; # alias my $buf = $client->{buf};
my $rv = sysread($sock, $buf, 64*1024, length($buf));
if (!defined($rv)) {
my $error = $!;
$select->remove($fh);
delete($clients{$fh});
# ... Handle error ...
next;
}
if (!$rv) {
$select->remove($fh);
delete($clients{$fh});
# ... Handle EOF ... # Don't forget to check if there's anything in $buf.
next;
}
... remove any complete messages from $buf and handle them ...
}
If you want to read a line at a time, you'd use
while ($buf =~ s/^([^\n]*)\n//) {
process_msg($client, $1);
}
Upvotes: 1