Partially tested. Specifically, I've tested the following:
- If it's the first time you can write to a socket, then it has just been connected.
- A socket will be flagged as readable when it gets disconnected. eof will return true on this socket.
You should find the following code very flexible. Think of Client as a state machine.
use IO::Select ();
use IO::Socket::INET ();
use Time::HiRes qw( time ); # Optional.
{
package Client;
use constant ST_CONNECTING => 0;
use constant ST_SOMETHING1 => 1;
use constant ST_SOMETHING2 => 2;
use constant ST_SOMETHING3 => 3;
# ...
sub new {
my ($class) = @_;
my $self = bless({
sock => $sock,
timeout => time() + 30,
wannna_d => 0, # True if we want to disconnect the socket.
wannna_w => 1, # True if we want to write to the socket.
state => ST_CONNECTING,
# ...
});
}
sub connected {
my ($self) = @_;
$self->{state } = ST_SOMETHING1;
$self->{wanna_w} = 0;
# ...
}
sub disconnected {
my ($self) = @_;
# ...
}
sub timed_out {
my ($self) = @_;
# ...
}
sub data_available {
my ($self) = @_;
$self->reset_timeout();
# ...
}
sub ready_to_write {
my ($self) = @_;
$self->reset_timeout();
my $state = $self->{state};
if ($state == ST_CONNECTING) {
$self->connected();
return;
}
# ...
}
sub reset_timeout {
my ($self) = @_;
$self->{timeout} = $time + 30;
}
}
{
my %clients;
{
my $sock = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => '...',
Blocking => 0, # How well does this work in Windows?
);
$clients{$sock} = Client->new($sock);
}
for (;;) {
my $time = time;
my $min_timeout = 3600; # Just so it isn't undef.
my $r_sel = IO::Select->new();
my $w_sel = IO::Select->new();
foreach my $client (values %clients) {
my $sock = $client->{sock};
my $wanna_d = $client->{wanna_d};
my $wanna_w = $client->{wanna_w};
my $timeout = $client->{timeout} - $time;
if ($wanna_d) {
delete($clients{$sock});
next;
}
if ($timeout <= 0) {
delete($clients{$sock});
$client->timed_out();
next;
}
$min_timeout = $timeout if $min_timeout > $timeout;
$r_sel->add($sock);
$w_sel->add($sock) if $wanna_w;
}
# In case a "$client->timed_out();" takes a long time.
$timeout -= time() - $time;
$timeout = 0.001 if $timeout < 0;
last if not %clients;
my ($r, $w) =
IO::Select->select($r_sel, $w_sel, undef, $timeout);
foreach my $sock (@$r) {
my $client = $clients{$sock};
next if not $client; # Just in case.
if (eof($sock)) {
delete($clients{$sock});
$client->disconnected();
} else {
$client->data_available();
}
}
foreach my $sock (@$w) {
my $client = $clients{$sock};
next if not $client;
$client->ready_to_write();
}
}
}
|