ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Socket.pm
Revision: 1.80
Committed: Fri Dec 7 22:37:24 2012 UTC (11 years, 5 months ago) by root
Branch: MAIN
CVS Tags: rel-6_23
Changes since 1.79: +1 -1 lines
Log Message:
6.23

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.43 Coro::Socket - non-blocking socket-I/O
4 root 1.1
5     =head1 SYNOPSIS
6    
7     use Coro::Socket;
8    
9 root 1.14 # listen on an ipv4 socket
10     my $socket = new Coro::Socket PeerHost => "localhost",
11     PeerPort => 'finger';
12    
13     # listen on any other type of socket
14     my $socket = Coro::Socket->new_from_fh
15     (IO::Socket::UNIX->new
16     Local => "/tmp/socket",
17     Type => SOCK_STREAM,
18     );
19    
20 root 1.1 =head1 DESCRIPTION
21    
22 root 1.20 This module is an L<AnyEvent> user, you need to make sure that you use and
23     run a supported event loop.
24    
25 root 1.1 This module implements socket-handles in a coroutine-compatible way,
26     that is, other coroutines can run while reads or writes block on the
27 root 1.17 handle. See L<Coro::Handle>, especially the note about prefering method
28     calls.
29 root 1.1
30 root 1.51 =head1 IPV6 WARNING
31    
32     This module was written to imitate the L<IO::Socket::INET> API, and derive
33     from it. Since IO::Socket::INET does not support IPv6, this module does
34     neither.
35    
36     Therefore it is not recommended to use Coro::Socket in new code. Instead,
37     use L<AnyEvent::Socket> and L<Coro::Handle>, e.g.:
38    
39     use Coro;
40     use Coro::Handle;
41     use AnyEvent::Socket;
42    
43     # use tcp_connect from AnyEvent::Socket
44     # and call Coro::Handle::unblock on it.
45    
46     tcp_connect "www.google.com", 80, Coro::rouse_cb;
47     my $fh = unblock +(Coro::rouse_wait)[0];
48    
49     # now we have a perfectly thread-safe socket handle in $fh
50     print $fh "GET / HTTP/1.0\015\012\015\012";
51     local $/;
52     print <$fh>;
53    
54     Using C<AnyEvent::Socket::tcp_connect> gives you transparent IPv6,
55     multi-homing, SRV-record etc. support.
56    
57     For listening sockets, use C<AnyEvent::Socket::tcp_server>.
58    
59 root 1.1 =over 4
60    
61     =cut
62    
63     package Coro::Socket;
64    
65 root 1.58 use common::sense;
66 root 1.14
67 root 1.1 use Errno ();
68     use Carp qw(croak);
69     use Socket;
70 root 1.14 use IO::Socket::INET ();
71 root 1.1
72 root 1.6 use Coro::Util ();
73    
74 root 1.14 use base qw(Coro::Handle IO::Socket::INET);
75    
76 root 1.80 our $VERSION = 6.23;
77 root 1.1
78 root 1.14 our (%_proto, %_port);
79 root 1.1
80     sub _proto($) {
81     $_proto{$_[0]} ||= do {
82     ((getprotobyname $_[0])[2] || (getprotobynumber $_[0])[2])
83     or croak "unsupported protocol: $_[0]";
84     };
85     }
86    
87     sub _port($$) {
88 root 1.9 $_port{$_[0],$_[1]} ||= do {
89     return $_[0] if $_[0] =~ /^\d+$/;
90    
91     $_[0] =~ /([^(]+)\s*(?:\((\d+)\))?/x
92     or croak "unparsable port number: $_[0]";
93     ((getservbyname $1, $_[1])[2]
94     || (getservbyport $1, $_[1])[2]
95     || $2)
96 root 1.1 or croak "unknown port: $_[0]";
97     };
98     }
99    
100     sub _sa($$$) {
101     my ($host, $port, $proto) = @_;
102 root 1.22
103 root 1.14 $port or $host =~ s/:([^:]+)$// and $port = $1;
104 root 1.22
105 root 1.1 my $_proto = _proto($proto);
106     my $_port = _port($port, $proto);
107    
108 root 1.22 my $_host = Coro::Util::inet_aton $host
109     or croak "$host: unable to resolve";
110    
111     pack_sockaddr_in $_port, $_host
112 root 1.1 }
113    
114 root 1.14 =item $fh = new Coro::Socket param => value, ...
115 root 1.1
116     Create a new non-blocking tcp handle and connect to the given host
117 root 1.21 and port. The parameter names and values are mostly the same as for
118 root 1.1 IO::Socket::INET (as ugly as I think they are).
119    
120 root 1.21 The parameters officially supported currently are: C<ReuseAddr>,
121 root 1.48 C<LocalPort>, C<LocalHost>, C<PeerPort>, C<PeerHost>, C<Listen>, C<Timeout>,
122     C<SO_RCVBUF>, C<SO_SNDBUF>.
123 root 1.1
124 root 1.14 $fh = new Coro::Socket PeerHost => "localhost", PeerPort => 'finger';
125 root 1.1
126     =cut
127    
128     sub _prepare_socket {
129 root 1.14 my ($self, $arg) = @_;
130    
131     $self
132     }
133    
134     sub new {
135     my ($class, %arg) = @_;
136 root 1.1
137 root 1.14 $arg{Proto} ||= 'tcp';
138     $arg{LocalHost} ||= delete $arg{LocalAddr};
139     $arg{PeerHost} ||= delete $arg{PeerAddr};
140     defined ($arg{Type}) or $arg{Type} = $arg{Proto} eq "tcp" ? SOCK_STREAM : SOCK_DGRAM;
141    
142     socket my $fh, PF_INET, $arg{Type}, _proto ($arg{Proto})
143 root 1.1 or return;
144    
145 root 1.14 my $self = bless Coro::Handle->new_from_fh (
146     $fh,
147     timeout => $arg{Timeout},
148     forward_class => $arg{forward_class},
149     partial => $arg{partial},
150     ), $class
151 root 1.1 or return;
152    
153 root 1.14 $self->configure (\%arg)
154     }
155    
156     sub configure {
157     my ($self, $arg) = @_;
158    
159 root 1.1 if ($arg->{ReuseAddr}) {
160 root 1.14 $self->setsockopt (SOL_SOCKET, SO_REUSEADDR, 1)
161 root 1.1 or croak "setsockopt(SO_REUSEADDR): $!";
162     }
163    
164     if ($arg->{ReusePort}) {
165 root 1.14 $self->setsockopt (SOL_SOCKET, SO_REUSEPORT, 1)
166 root 1.1 or croak "setsockopt(SO_REUSEPORT): $!";
167     }
168    
169 root 1.18 if ($arg->{Broadcast}) {
170     $self->setsockopt (SOL_SOCKET, SO_BROADCAST, 1)
171     or croak "setsockopt(SO_BROADCAST): $!";
172     }
173    
174 root 1.48 if ($arg->{SO_RCVBUF}) {
175     $self->setsockopt (SOL_SOCKET, SO_RCVBUF, $arg->{SO_RCVBUF})
176     or croak "setsockopt(SO_RCVBUF): $!";
177     }
178    
179     if ($arg->{SO_SNDBUF}) {
180     $self->setsockopt (SOL_SOCKET, SO_SNDBUF, $arg->{SO_SNDBUF})
181     or croak "setsockopt(SO_SNDBUF): $!";
182     }
183    
184 root 1.14 if ($arg->{LocalPort} || $arg->{LocalHost}) {
185     my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort} || 0, $arg->{Proto});
186     $self->bind ($sa[0])
187 root 1.1 or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!";
188     }
189    
190 root 1.14 if ($arg->{PeerHost}) {
191     my @sa = _sa ($arg->{PeerHost}, $arg->{PeerPort}, $arg->{Proto});
192 root 1.1
193     for (@sa) {
194     $! = 0;
195    
196 root 1.14 if ($self->connect ($_)) {
197     next unless writable $self;
198     $! = unpack "i", $self->getsockopt (SOL_SOCKET, SO_ERROR);
199 root 1.1 }
200    
201     $! or last;
202    
203     $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH}
204     or return;
205     }
206 root 1.14 } elsif (exists $arg->{Listen}) {
207     $self->listen ($arg->{Listen})
208 root 1.1 or return;
209     }
210    
211 root 1.14 $self
212 root 1.1 }
213    
214     1;
215    
216 root 1.14 =back
217    
218 root 1.1 =head1 AUTHOR
219    
220 root 1.14 Marc Lehmann <schmorp@schmorp.de>
221     http://home.schmorp.de/
222 root 1.1
223     =cut
224