1 |
package AnyEvent::Socket; |
2 |
|
3 |
no warnings; |
4 |
use strict; |
5 |
|
6 |
use Carp; |
7 |
use Errno qw/ENXIO ETIMEDOUT/; |
8 |
use Socket; |
9 |
use IO::Socket::INET; |
10 |
use AnyEvent; |
11 |
use AnyEvent::Util; |
12 |
use AnyEvent::Handle; |
13 |
|
14 |
our @ISA = qw/AnyEvent::Handle/; |
15 |
|
16 |
=head1 NAME |
17 |
|
18 |
AnyEvent::Socket - Connecting sockets for non-blocking I/O |
19 |
|
20 |
=head1 SYNOPSIS |
21 |
|
22 |
use AnyEvent; |
23 |
use AnyEvent::Socket; |
24 |
|
25 |
my $cv = AnyEvent->condvar; |
26 |
|
27 |
my $ae_sock = |
28 |
AnyEvent::Socket->new ( |
29 |
PeerAddr => "www.google.de:80", |
30 |
on_eof => sub { $cv->broadcast }, |
31 |
on_connect => sub { |
32 |
my ($ae_sock, $error) = @_; |
33 |
if ($error) { |
34 |
warn "couldn't connect: $!"; |
35 |
return; |
36 |
} else { |
37 |
print "connected to ".$ae_sock->fh->peerhost.":".$ae_sock->fh->peerport."\n"; |
38 |
} |
39 |
|
40 |
$ae_sock->on_read (sub { |
41 |
my ($ae_sock) = @_; |
42 |
print "got data: [".${$ae_sock->rbuf}."]\n"; |
43 |
$ae_sock->rbuf = ''; |
44 |
}); |
45 |
|
46 |
$ae_sock->write ("GET / HTTP/1.0\015\012\015\012"); |
47 |
} |
48 |
); |
49 |
|
50 |
$cv->wait; |
51 |
|
52 |
=head1 DESCRIPTION |
53 |
|
54 |
L<AnyEvent::Socket> provides method to connect sockets and accept clients |
55 |
on listening sockets. |
56 |
|
57 |
=head1 EXAMPLES |
58 |
|
59 |
See the C<eg/> directory of the L<AnyEvent> distribution for examples and also |
60 |
the tests in C<t/handle/> can be helpful. |
61 |
|
62 |
=head1 METHODS |
63 |
|
64 |
=over 4 |
65 |
|
66 |
=item B<new (%args)> |
67 |
|
68 |
The constructor gets the same arguments as the L<IO::Socket::INET> constructor. |
69 |
Except that blocking will always be disabled and the hostname lookup is done by |
70 |
L<AnyEvent::Util::inet_aton> before the socket (currently a L<IO::Socket::INET> instance) |
71 |
is created. |
72 |
|
73 |
Additionally you can set the callbacks that can be set in the L<AnyEvent::Handle> |
74 |
constructor and these: |
75 |
|
76 |
=over 4 |
77 |
|
78 |
=item on_connect => $cb |
79 |
|
80 |
Installs a connect callback, that will be called when the name was successfully |
81 |
resolved and the connection was successfully established or an error occured in |
82 |
the lookup or connect. |
83 |
|
84 |
The first argument to the callback C<$cb> will be the L<AnyEvent::Socket> itself |
85 |
and the second is either a true value in case an error occured or undef. |
86 |
The variable C<$!> will be set to one of these values: |
87 |
|
88 |
=over 4 |
89 |
|
90 |
=item ENXIO |
91 |
|
92 |
When the DNS lookup failed. |
93 |
|
94 |
=item ETIMEDOUT |
95 |
|
96 |
When the connect timed out. |
97 |
|
98 |
=item * |
99 |
|
100 |
Or any other errno as set by L<IO::Socket::INET> when it's constructor |
101 |
failed or the connection couldn't be established for any other reason. |
102 |
|
103 |
=back |
104 |
|
105 |
=item on_accept |
106 |
|
107 |
This sets the C<on_accept> callback by calling the C<on_accept> method. |
108 |
See also below. |
109 |
|
110 |
=back |
111 |
|
112 |
=cut |
113 |
|
114 |
sub new { |
115 |
my $this = shift; |
116 |
my $class = ref($this) || $this; |
117 |
my %args = @_; |
118 |
my %self_args; |
119 |
|
120 |
$self_args{$_} = delete $args{$_} |
121 |
for grep { /^on_/ } keys %args; |
122 |
|
123 |
my $self = $class->SUPER::new (%self_args); |
124 |
$self->{sock_args} = \%args; |
125 |
|
126 |
if (exists $args{PeerAddr} || exists $args{PeerHost}) { |
127 |
$self->{on_connect} ||= sub { |
128 |
Carp::croak "Couldn't connect to $args{PeerHost}:$args{PeerPort}: $!" |
129 |
if $_[1]; |
130 |
}; |
131 |
$self->_connect; |
132 |
} |
133 |
|
134 |
if ($self->{on_accept}) { |
135 |
$self->on_accept ($self->{on_accept}); |
136 |
} |
137 |
|
138 |
return $self |
139 |
} |
140 |
|
141 |
sub _connect { |
142 |
my ($self) = @_; |
143 |
|
144 |
if (defined $self->{sock_args}->{Listen}) { |
145 |
Carp::croak "connect can be done on a socket that has 'Listen' set!"; |
146 |
} |
147 |
|
148 |
if ($self->{sock_args}->{PeerAddr} =~ /^([^:]+)(?::(\d+))?$/) { |
149 |
$self->{sock_args}->{PeerHost} = $1; |
150 |
$self->{sock_args}->{PeerPort} = $2 if defined $2; |
151 |
delete $self->{sock_args}->{PeerAddr}; |
152 |
|
153 |
$self->_lookup ($1); |
154 |
return; |
155 |
|
156 |
} elsif (my $h = $self->{sock_args}->{PeerHost}) { |
157 |
$self->_lookup ($h); |
158 |
return; |
159 |
|
160 |
} else { |
161 |
Carp::croak "no PeerAddr or PeerHost provided!"; |
162 |
} |
163 |
} |
164 |
|
165 |
=item B<on_accept ($cb)> |
166 |
|
167 |
When the socket is run in listening mode (the C<Listen> argument of the socket |
168 |
is set) this callback will be called when a new client connected. |
169 |
The first argument to the callback will be the L<AnyEvent::Socket> object itself, |
170 |
the second the L<AnyEvent::Handle> of the client socket and the third |
171 |
is the peer address (depending on what C<accept> of L<IO::Socket> gives you>). |
172 |
|
173 |
=cut |
174 |
|
175 |
sub on_accept { |
176 |
my ($self, $cb) = @_; |
177 |
|
178 |
unless (defined $self->{sock_args}->{Listen}) { |
179 |
$self->{sock_args}->{Listen} = 10; |
180 |
} |
181 |
|
182 |
$self->{fh} = |
183 |
IO::Socket::INET->new (%{$self->{sock_args}}, Blocking => 0) |
184 |
or Carp::croak ("couldn't create listening socket: $!"); |
185 |
|
186 |
$self->{list_w} = |
187 |
AnyEvent->io (poll => 'r', fh => $self->{fh}, cb => sub { |
188 |
my ($new_sock, $paddr) = $self->{fh}->accept (); |
189 |
unless ($new_sock) { |
190 |
$cb->($self); |
191 |
delete $self->{list_w}; |
192 |
return; |
193 |
} |
194 |
my $ae_hdl = AnyEvent::Handle->new (fh => $new_sock); |
195 |
$cb->($self, $ae_hdl, $paddr); |
196 |
}); |
197 |
} |
198 |
|
199 |
sub _lookup { |
200 |
my ($self, $host) = @_; |
201 |
|
202 |
AnyEvent::Util::inet_aton ($host, sub { |
203 |
my ($addr) = @_; |
204 |
|
205 |
if ($addr) { |
206 |
$self->{sock_args}->{PeerHost} = inet_ntoa $addr; |
207 |
$self->_real_connect; |
208 |
|
209 |
} else { |
210 |
$! = ENXIO; |
211 |
$self->{on_connect}->($self, 1); |
212 |
} |
213 |
}); |
214 |
} |
215 |
|
216 |
sub _real_connect { |
217 |
my ($self) = @_; |
218 |
|
219 |
if (defined $self->{sock_args}->{Timeout}) { |
220 |
$self->{dns_tmout} = |
221 |
AnyEvent->timer (after => $self->{sock_args}->{Timeout}, cb => sub { |
222 |
$! = ETIMEDOUT; |
223 |
$self->{on_connect}->($self, 1); |
224 |
}); |
225 |
} |
226 |
|
227 |
$self->{fh} = IO::Socket::INET->new (%{$self->{sock_args}}, Blocking => 0); |
228 |
unless ($self->{fh}) { |
229 |
$self->{on_connect}->($self, 1); |
230 |
return; |
231 |
} |
232 |
|
233 |
$self->{con_w} = |
234 |
AnyEvent->io (poll => 'w', fh => $self->{fh}, cb => sub { |
235 |
delete $self->{con_w}; |
236 |
|
237 |
if ($! = $self->{fh}->sockopt (SO_ERROR)) { |
238 |
$self->{on_connect}->($self, 1); |
239 |
|
240 |
} else { |
241 |
$self->{on_connect}->($self); |
242 |
} |
243 |
}); |
244 |
} |
245 |
|
246 |
=back |
247 |
|
248 |
=head1 AUTHOR |
249 |
|
250 |
Robin Redeker, C<< <elmex at ta-sa.org> >> |
251 |
|
252 |
=cut |
253 |
|
254 |
1; # End of AnyEvent |