ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Socket.pm
Revision: 1.111
Committed: Mon Mar 16 11:12:52 2020 UTC (4 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-6_57, HEAD
Changes since 1.110: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro::Socket - non-blocking socket-I/O
4
5 =head1 SYNOPSIS
6
7 use Coro::Socket;
8
9 # 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 =head1 DESCRIPTION
21
22 This module is an L<AnyEvent> user, you need to make sure that you use and
23 run a supported event loop.
24
25 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 handle. See L<Coro::Handle>, especially the note about prefering method
28 calls.
29
30 =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 =over 4
60
61 =cut
62
63 package Coro::Socket;
64
65 use common::sense;
66
67 use Errno ();
68 use Carp qw(croak);
69 use Socket;
70 use IO::Socket::INET ();
71
72 use Coro::Util ();
73
74 use base qw(Coro::Handle IO::Socket::INET);
75
76 our $VERSION = 6.57;
77
78 our (%_proto, %_port);
79
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 $_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 or croak "unknown port: $_[0]";
97 };
98 }
99
100 sub _sa($$$) {
101 my ($host, $port, $proto) = @_;
102
103 $port or $host =~ s/:([^:]+)$// and $port = $1;
104
105 my $_proto = _proto($proto);
106 my $_port = _port($port, $proto);
107
108 my $_host = Coro::Util::inet_aton $host
109 or croak "$host: unable to resolve";
110
111 pack_sockaddr_in $_port, $_host
112 }
113
114 =item $fh = new Coro::Socket param => value, ...
115
116 Create a new non-blocking tcp handle and connect to the given host
117 and port. The parameter names and values are mostly the same as for
118 IO::Socket::INET (as ugly as I think they are).
119
120 The parameters officially supported currently are: C<ReuseAddr>,
121 C<LocalPort>, C<LocalHost>, C<PeerPort>, C<PeerHost>, C<Listen>, C<Timeout>,
122 C<SO_RCVBUF>, C<SO_SNDBUF>.
123
124 $fh = new Coro::Socket PeerHost => "localhost", PeerPort => 'finger';
125
126 =cut
127
128 sub _prepare_socket {
129 my ($self, $arg) = @_;
130
131 $self
132 }
133
134 sub new {
135 my ($class, %arg) = @_;
136
137 $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 or return;
144
145 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 or return;
152
153 $self->configure (\%arg)
154 }
155
156 sub configure {
157 my ($self, $arg) = @_;
158
159 if ($arg->{ReuseAddr}) {
160 $self->setsockopt (SOL_SOCKET, SO_REUSEADDR, 1)
161 or croak "setsockopt(SO_REUSEADDR): $!";
162 }
163
164 if ($arg->{ReusePort}) {
165 $self->setsockopt (SOL_SOCKET, SO_REUSEPORT, 1)
166 or croak "setsockopt(SO_REUSEPORT): $!";
167 }
168
169 if ($arg->{Broadcast}) {
170 $self->setsockopt (SOL_SOCKET, SO_BROADCAST, 1)
171 or croak "setsockopt(SO_BROADCAST): $!";
172 }
173
174 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 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 or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!";
188 }
189
190 if ($arg->{PeerHost}) {
191 my @sa = _sa ($arg->{PeerHost}, $arg->{PeerPort}, $arg->{Proto});
192
193 for (@sa) {
194 $! = 0;
195
196 if ($self->connect ($_)) {
197 next unless writable $self;
198 $! = unpack "i", $self->getsockopt (SOL_SOCKET, SO_ERROR);
199 }
200
201 $! or last;
202
203 $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH}
204 or return;
205 }
206 } elsif (exists $arg->{Listen}) {
207 $self->listen ($arg->{Listen})
208 or return;
209 }
210
211 $self
212 }
213
214 1;
215
216 =back
217
218 =head1 AUTHOR/SUPPORT/CONTACT
219
220 Marc A. Lehmann <schmorp@schmorp.de>
221 http://software.schmorp.de/pkg/Coro.html
222
223 =cut
224