Pretty good in fact, but it has a few flaws:
- A connect can take a long time too
- Writes can block too
- The read can be satisfied in several chunks
- Operations can be interrupted
Here is a (only sligtly tested) version of how I would do it when using IO::Socket::INET and IO::Select (in reality I would use POE). Like you
I will assume the protocol does pipelining, otherwise you
should immediately start checking for readability to drain
pending output that can block the server. I'm also assuming that the server finishes with EOF, otherwise you might already want to start checking for your target string inside the read loop.
#! /usr/bin/perl -w
use strict;
use IO::Socket::INET;
use IO::Select;
use POSIX qw(EWOULDBLOCK EINTR);
my $timeout = 180;
my ($host, $port, $string) = @ARGV;
my $too_late = time()+$timeout;
my $socket = IO::Socket::INET->new(PeerAddr => "$host:$port",
Blocking => 0) ||
die "Could not connect to $host:$port: $!\n";
# Write (also handles connect, since being connected is a writability
+event)
$SIG{PIPE} = "IGNORE";
my $command = "EDMSFT|$string|\@\n";
my $select = IO::Select->new($socket);
while ($command ne "") {
my $left = $too_late - time();
if ($left <= 0) {
print "TIMEDOUT\n";
exit;
}
next unless $select->can_write($left);
if (defined(my $rc = syswrite($socket, $command))) {
substr($command, 0, $rc, "");
} else {
next if $! == EWOULDBLOCK || $! == EINTR;
die "Error writing to $host:$port: $!\n";
}
}
# Read
my $text = "";
while (1) {
my $left = $too_late - time();
if ($left <= 0) {
print "TIMEDOUT\n";
exit;
}
next unless $select->can_read($left);
my $rc = sysread($socket, $text, 4096, length $text);
if (!$rc) {
last if defined $rc; # EOF
next if $! == EWOULDBLOCK || $! == EINTR;
die "Error reading from $host:$port: $!\n";
}
}
$text =~ /\Q$string\E/ ||
die "Unexpected answer from $host:$port: $text";
print "GOOD\n";
|