ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Socket.pm
Revision: 1.49
Committed: Fri May 29 07:01:18 2009 UTC (15 years ago) by root
Branch: MAIN
CVS Tags: rel-5_132
Changes since 1.48: +1 -1 lines
Log Message:
5.132

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 =over 4
31
32 =cut
33
34 package Coro::Socket;
35
36 no warnings "uninitialized";
37
38 use strict;
39
40 use Errno ();
41 use Carp qw(croak);
42 use Socket;
43 use IO::Socket::INET ();
44
45 use Coro::Util ();
46
47 use base qw(Coro::Handle IO::Socket::INET);
48
49 our $VERSION = 5.132;
50
51 our (%_proto, %_port);
52
53 sub _proto($) {
54 $_proto{$_[0]} ||= do {
55 ((getprotobyname $_[0])[2] || (getprotobynumber $_[0])[2])
56 or croak "unsupported protocol: $_[0]";
57 };
58 }
59
60 sub _port($$) {
61 $_port{$_[0],$_[1]} ||= do {
62 return $_[0] if $_[0] =~ /^\d+$/;
63
64 $_[0] =~ /([^(]+)\s*(?:\((\d+)\))?/x
65 or croak "unparsable port number: $_[0]";
66 ((getservbyname $1, $_[1])[2]
67 || (getservbyport $1, $_[1])[2]
68 || $2)
69 or croak "unknown port: $_[0]";
70 };
71 }
72
73 sub _sa($$$) {
74 my ($host, $port, $proto) = @_;
75
76 $port or $host =~ s/:([^:]+)$// and $port = $1;
77
78 my $_proto = _proto($proto);
79 my $_port = _port($port, $proto);
80
81 my $_host = Coro::Util::inet_aton $host
82 or croak "$host: unable to resolve";
83
84 pack_sockaddr_in $_port, $_host
85 }
86
87 =item $fh = new Coro::Socket param => value, ...
88
89 Create a new non-blocking tcp handle and connect to the given host
90 and port. The parameter names and values are mostly the same as for
91 IO::Socket::INET (as ugly as I think they are).
92
93 The parameters officially supported currently are: C<ReuseAddr>,
94 C<LocalPort>, C<LocalHost>, C<PeerPort>, C<PeerHost>, C<Listen>, C<Timeout>,
95 C<SO_RCVBUF>, C<SO_SNDBUF>.
96
97 $fh = new Coro::Socket PeerHost => "localhost", PeerPort => 'finger';
98
99 =cut
100
101 sub _prepare_socket {
102 my ($self, $arg) = @_;
103
104 $self
105 }
106
107 sub new {
108 my ($class, %arg) = @_;
109
110 $arg{Proto} ||= 'tcp';
111 $arg{LocalHost} ||= delete $arg{LocalAddr};
112 $arg{PeerHost} ||= delete $arg{PeerAddr};
113 defined ($arg{Type}) or $arg{Type} = $arg{Proto} eq "tcp" ? SOCK_STREAM : SOCK_DGRAM;
114
115 socket my $fh, PF_INET, $arg{Type}, _proto ($arg{Proto})
116 or return;
117
118 my $self = bless Coro::Handle->new_from_fh (
119 $fh,
120 timeout => $arg{Timeout},
121 forward_class => $arg{forward_class},
122 partial => $arg{partial},
123 ), $class
124 or return;
125
126 $self->configure (\%arg)
127 }
128
129 sub configure {
130 my ($self, $arg) = @_;
131
132 if ($arg->{ReuseAddr}) {
133 $self->setsockopt (SOL_SOCKET, SO_REUSEADDR, 1)
134 or croak "setsockopt(SO_REUSEADDR): $!";
135 }
136
137 if ($arg->{ReusePort}) {
138 $self->setsockopt (SOL_SOCKET, SO_REUSEPORT, 1)
139 or croak "setsockopt(SO_REUSEPORT): $!";
140 }
141
142 if ($arg->{Broadcast}) {
143 $self->setsockopt (SOL_SOCKET, SO_BROADCAST, 1)
144 or croak "setsockopt(SO_BROADCAST): $!";
145 }
146
147 if ($arg->{SO_RCVBUF}) {
148 $self->setsockopt (SOL_SOCKET, SO_RCVBUF, $arg->{SO_RCVBUF})
149 or croak "setsockopt(SO_RCVBUF): $!";
150 }
151
152 if ($arg->{SO_SNDBUF}) {
153 $self->setsockopt (SOL_SOCKET, SO_SNDBUF, $arg->{SO_SNDBUF})
154 or croak "setsockopt(SO_SNDBUF): $!";
155 }
156
157 if ($arg->{LocalPort} || $arg->{LocalHost}) {
158 my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort} || 0, $arg->{Proto});
159 $self->bind ($sa[0])
160 or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!";
161 }
162
163 if ($arg->{PeerHost}) {
164 my @sa = _sa ($arg->{PeerHost}, $arg->{PeerPort}, $arg->{Proto});
165
166 for (@sa) {
167 $! = 0;
168
169 if ($self->connect ($_)) {
170 next unless writable $self;
171 $! = unpack "i", $self->getsockopt (SOL_SOCKET, SO_ERROR);
172 }
173
174 $! or last;
175
176 $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH}
177 or return;
178 }
179 } elsif (exists $arg->{Listen}) {
180 $self->listen ($arg->{Listen})
181 or return;
182 }
183
184 $self
185 }
186
187 1;
188
189 =back
190
191 =head1 AUTHOR
192
193 Marc Lehmann <schmorp@schmorp.de>
194 http://home.schmorp.de/
195
196 =cut
197