package AnyEvent::Socket; no warnings; use strict; use Carp; use Errno qw/ENXIO ETIMEDOUT/; use Socket; use IO::Socket::INET; use AnyEvent; use AnyEvent::Util; use AnyEvent::Handle; our @ISA = qw/AnyEvent::Handle/; =head1 NAME AnyEvent::Socket - Connecting sockets for non-blocking I/O =head1 SYNOPSIS use AnyEvent; use AnyEvent::Socket; my $cv = AnyEvent->condvar; my $ae_sock = AnyEvent::Socket->new ( PeerAddr => "www.google.de:80", on_eof => sub { $cv->broadcast }, on_connect => sub { my ($ae_sock, $error) = @_; if ($error) { warn "couldn't connect: $!"; return; } else { print "connected to ".$ae_sock->fh->peerhost.":".$ae_sock->fh->peerport."\n"; } $ae_sock->on_read (sub { my ($ae_sock) = @_; print "got data: [".${$ae_sock->rbuf}."]\n"; $ae_sock->rbuf = ''; }); $ae_sock->write ("GET / HTTP/1.0\015\012\015\012"); } ); $cv->wait; =head1 DESCRIPTION L provides method to connect sockets and accept clients on listening sockets. =head1 EXAMPLES See the C directory of the L distribution for examples and also the tests in C can be helpful. =head1 METHODS =over 4 =item B The constructor gets the same arguments as the L constructor. Except that blocking will always be disabled and the hostname lookup is done by L before the socket (currently a L instance) is created. Additionally you can set the callbacks that can be set in the L constructor and these: =over 4 =item on_connect => $cb Installs a connect callback, that will be called when the name was successfully resolved and the connection was successfully established or an error occured in the lookup or connect. The first argument to the callback C<$cb> will be the L itself and the second is either a true value in case an error occured or undef. The variable C<$!> will be set to one of these values: =over 4 =item ENXIO When the DNS lookup failed. =item ETIMEDOUT When the connect timed out. =item * Or any other errno as set by L when it's constructor failed or the connection couldn't be established for any other reason. =back =item on_accept This sets the C callback by calling the C method. See also below. =back =cut sub new { my $this = shift; my $class = ref($this) || $this; my %args = @_; my %self_args; $self_args{$_} = delete $args{$_} for grep { /^on_/ } keys %args; my $self = $class->SUPER::new (%self_args); $self->{sock_args} = \%args; if (exists $args{PeerAddr} || exists $args{PeerHost}) { $self->{on_connect} ||= sub { Carp::croak "Couldn't connect to $args{PeerHost}:$args{PeerPort}: $!" if $_[1]; }; $self->_connect; } if ($self->{on_accept}) { $self->on_accept ($self->{on_accept}); } return $self } sub _connect { my ($self) = @_; if (defined $self->{sock_args}->{Listen}) { Carp::croak "connect can be done on a socket that has 'Listen' set!"; } if ($self->{sock_args}->{PeerAddr} =~ /^([^:]+)(?::(\d+))?$/) { $self->{sock_args}->{PeerHost} = $1; $self->{sock_args}->{PeerPort} = $2 if defined $2; delete $self->{sock_args}->{PeerAddr}; $self->_lookup ($1); return; } elsif (my $h = $self->{sock_args}->{PeerHost}) { $self->_lookup ($h); return; } else { Carp::croak "no PeerAddr or PeerHost provided!"; } } =item B When the socket is run in listening mode (the C argument of the socket is set) this callback will be called when a new client connected. The first argument to the callback will be the L object itself, the second the L of the client socket and the third is the peer address (depending on what C of L gives you>). =cut sub on_accept { my ($self, $cb) = @_; unless (defined $self->{sock_args}->{Listen}) { $self->{sock_args}->{Listen} = 10; } $self->{fh} = IO::Socket::INET->new (%{$self->{sock_args}}, Blocking => 0) or Carp::croak ("couldn't create listening socket: $!"); $self->{list_w} = AnyEvent->io (poll => 'r', fh => $self->{fh}, cb => sub { my ($new_sock, $paddr) = $self->{fh}->accept (); unless ($new_sock) { $cb->($self); delete $self->{list_w}; return; } my $ae_hdl = AnyEvent::Handle->new (fh => $new_sock); $cb->($self, $ae_hdl, $paddr); }); } sub _lookup { my ($self, $host) = @_; AnyEvent::Util::inet_aton ($host, sub { my ($addr) = @_; if ($addr) { $self->{sock_args}->{PeerHost} = inet_ntoa $addr; $self->_real_connect; } else { $! = ENXIO; $self->{on_connect}->($self, 1); } }); } sub _real_connect { my ($self) = @_; if (defined $self->{sock_args}->{Timeout}) { $self->{dns_tmout} = AnyEvent->timer (after => $self->{sock_args}->{Timeout}, cb => sub { $! = ETIMEDOUT; $self->{on_connect}->($self, 1); }); } $self->{fh} = IO::Socket::INET->new (%{$self->{sock_args}}, Blocking => 0); unless ($self->{fh}) { $self->{on_connect}->($self, 1); return; } $self->{con_w} = AnyEvent->io (poll => 'w', fh => $self->{fh}, cb => sub { delete $self->{con_w}; if ($! = $self->{fh}->sockopt (SO_ERROR)) { $self->{on_connect}->($self, 1); } else { $self->{on_connect}->($self); } }); } =back =head1 AUTHOR Robin Redeker, C<< >> =cut 1; # End of AnyEvent