sysread won't always give you the amount of bytes you request when reading from something that isn't a plain file.
See Re: A suicidal parent OR death of a forking server for a newline terminated solution, and here's a length-prefix adaptation:
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket::INET qw( );
use IO::Select qw( );
sub process_msg {
my ($client, $msg) = @_;
chomp $msg;
print "$client->{host} said '$msg'\n";
}
sub process_msgs {
my ($client) = @_;
our $buf; local *buf = \($client->{buf});
our $want; local *want = \($client->{want});
for (;;) {
if ($want) {
return if length($buf) < $want;
my $msg = substr($buf, 0, $want, '');
$want = 0;
process_msg($client, $msg);
} else {
return if length($buf) < 8;
$want = 0+substr($buf, 0, 8, '');
}
}
}
my $server = IO::Socket::INET->new(
...
) or die("Couldn't create server socket: $!\n");
my $select = IO::Select->new($server);
my %clients;
while (my @ready = $select->can_read) {
for my $fh (@ready) {
if ($fh == $server) {
my $client_sock = $server->accept;
my $host = $client_sock->peerhost;
print "[Accepted connection from $host]\n";
$select->add($client_sock);
$clients{fileno($client_sock)} = {
host => $host,
buf => '',
want => 0,
};
}
else {
my $client = $clients{fileno($fh)};
our $buf; local *buf = \($client->{buf});
our $want; local *want = \($client->{want});
my $rv = sysread($fh, $buf, 64*1024, length($buf));
if (!$rv) {
my $host = $client->{host}fh->peerhost;
if (defined($rv)) {
print "[Error reading from host $host]\n";
} else {
print "[Connection from $host terminated]\n";
}
process_msgs($client);
print "Incomplete message received from $host]\n"
if $want || length($buf);
delete $clients{fileno($fh)};
$sel->remove($fh);
next;
}
process_msgs($client);
}
}
}