ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Socket.pm
Revision: 1.17
Committed: Tue Oct 9 14:07:02 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-4_22, rel-4_21, rel-4_3, rel-4_13, rel-4_11, rel-4_4, rel-4_1, rel-4_2, rel-4_31, rel-4_32, rel-4_33, rel-4_34, rel-4_35, rel-4_36, rel-4_37
Changes since 1.16: +2 -1 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Coro::Socket - non-blocking socket-io
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 implements socket-handles in a coroutine-compatible way,
23 that is, other coroutines can run while reads or writes block on the
24 handle. See L<Coro::Handle>, especially the note about prefering method
25 calls.
26
27 =over 4
28
29 =cut
30
31 package Coro::Socket;
32
33 no warnings "uninitialized";
34
35 use strict;
36
37 use Errno ();
38 use Carp qw(croak);
39 use Socket;
40 use IO::Socket::INET ();
41
42 use Coro::Util ();
43
44 use base qw(Coro::Handle IO::Socket::INET);
45
46 our $VERSION = 1.9;
47
48 our (%_proto, %_port);
49
50 sub _proto($) {
51 $_proto{$_[0]} ||= do {
52 ((getprotobyname $_[0])[2] || (getprotobynumber $_[0])[2])
53 or croak "unsupported protocol: $_[0]";
54 };
55 }
56
57 sub _port($$) {
58 $_port{$_[0],$_[1]} ||= do {
59 return $_[0] if $_[0] =~ /^\d+$/;
60
61 $_[0] =~ /([^(]+)\s*(?:\((\d+)\))?/x
62 or croak "unparsable port number: $_[0]";
63 ((getservbyname $1, $_[1])[2]
64 || (getservbyport $1, $_[1])[2]
65 || $2)
66 or croak "unknown port: $_[0]";
67 };
68 }
69
70 sub _sa($$$) {
71 my ($host, $port, $proto) = @_;
72 $port or $host =~ s/:([^:]+)$// and $port = $1;
73 my $_proto = _proto($proto);
74 my $_port = _port($port, $proto);
75
76 # optimize this a bit for a common case
77 if (Coro::Util::dotted_quad $host) {
78 return pack_sockaddr_in ($_port, inet_aton $host);
79 } else {
80 my (undef, undef, undef, undef, @host) = Coro::Util::gethostbyname $host
81 or croak "unknown host: $host";
82 map pack_sockaddr_in ($_port,$_), @host;
83 }
84 }
85
86 =item $fh = new Coro::Socket param => value, ...
87
88 Create a new non-blocking tcp handle and connect to the given host
89 and port. The parameter names and values are mostly the same as in
90 IO::Socket::INET (as ugly as I think they are).
91
92 If the host is unreachable or otherwise cannot be connected to this method
93 returns undef. On all other errors ot croak's.
94
95 Multihomed is always enabled.
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->{LocalPort} || $arg->{LocalHost}) {
143 my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort} || 0, $arg->{Proto});
144 $self->bind ($sa[0])
145 or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!";
146 }
147
148 if ($arg->{PeerHost}) {
149 my @sa = _sa ($arg->{PeerHost}, $arg->{PeerPort}, $arg->{Proto});
150
151 for (@sa) {
152 $! = 0;
153
154 if ($self->connect ($_)) {
155 next unless writable $self;
156 $! = unpack "i", $self->getsockopt (SOL_SOCKET, SO_ERROR);
157 }
158
159 $! or last;
160
161 $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH}
162 or return;
163 }
164 } elsif (exists $arg->{Listen}) {
165 $self->listen ($arg->{Listen})
166 or return;
167 }
168
169 $self
170 }
171
172 1;
173
174 =back
175
176 =head1 AUTHOR
177
178 Marc Lehmann <schmorp@schmorp.de>
179 http://home.schmorp.de/
180
181 =cut
182