ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Event/Socket.pm
Revision: 1.13
Committed: Thu Nov 21 13:08:06 2002 UTC (21 years, 6 months ago) by root
Branch: MAIN
Changes since 1.12: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     Coro::Socket - non-blocking socket-io
4    
5     =head1 SYNOPSIS
6    
7     use Coro::Socket;
8    
9     =head1 DESCRIPTION
10    
11     This module implements socket-handles in a coroutine-compatible way,
12     that is, other coroutines can run while reads or writes block on the
13     handle. L<Coro::Handle>.
14    
15     =over 4
16    
17     =cut
18    
19     package Coro::Socket;
20    
21 root 1.7 no warnings qw(uninitialized);
22 root 1.6
23 root 1.1 use Errno ();
24     use Carp qw(croak);
25     use Socket;
26    
27     use Coro::Util ();
28    
29     use base 'Coro::Handle';
30    
31 root 1.13 $VERSION = 0.6;
32 root 1.1
33     sub _proto($) {
34     $_proto{$_[0]} ||= do {
35     ((getprotobyname $_[0])[2] || (getprotobynumber $_[0])[2])
36     or croak "unsupported protocol: $_[0]";
37     };
38     }
39    
40     sub _port($$) {
41     $_port{$_[0],$_[1]} ||= do {
42     return $_[0] if $_[0] =~ /^\d+$/;
43    
44     $_[0] =~ /([^(]+)\s*(?:\((\d+)\))?/x
45     or croak "unparsable port number: $_[0]";
46     ((getservbyname $1, $_[1])[2]
47     || (getservbyport $1, $_[1])[2]
48     || $2)
49     or croak "unknown port: $_[0]";
50     };
51     }
52    
53     sub _sa($$$) {
54     my ($host, $port, $proto) = @_;
55     my $_proto = _proto($proto);
56     my $_port = _port($port, $proto);
57    
58     # optimize this a bit for a common case
59     if ($host =~ /^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?)
60     \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?)
61     \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?)
62     \.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9][0-9]?)$/) {
63     return pack_sockaddr_in($_port, inet_aton $host);
64     } else {
65     my (undef, undef, undef, undef, @host) = Coro::Util::gethostbyname $host
66     or croak "unknown host: $host";
67     map pack_sockaddr_in($_port,$_), @host;
68     }
69     }
70    
71 root 1.2 =item $fh = new Coro::Socket param => value, ...
72 root 1.1
73     Create a new non-blocking tcp handle and connect to the given host
74     and port. The parameter names and values are mostly the same as in
75     IO::Socket::INET (as ugly as I think they are).
76    
77     If the host is unreachable or otherwise cannot be connected to this method
78     returns undef. On all other errors ot croak's.
79    
80     Multihomed is always enabled.
81    
82     $fh = new_inet Coro::Socket PeerHost => "localhost", PeerPort => 'finger';
83    
84     =cut
85    
86     sub _prepare_socket {
87     my ($class, $arg) = @_;
88     my $fh;
89    
90     socket $fh, PF_INET, $arg->{Type}, _proto($arg->{Proto})
91     or return;
92    
93     $fh = bless Coro::Handle->new_from_fh($fh, timeout => $arg{Timeout}), $class
94     or return;
95    
96     if ($arg->{ReuseAddr}) {
97     $fh->setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
98     or croak "setsockopt(SO_REUSEADDR): $!";
99     }
100    
101     if ($arg->{ReusePort}) {
102     $fh->setsockopt(SOL_SOCKET, SO_REUSEPORT, 1)
103     or croak "setsockopt(SO_REUSEPORT): $!";
104     }
105    
106     if ($arg->{LocalPort}) {
107     my @sa = _sa($arg->{LocalHost} || "0.0.0.0", $arg->{LocalPort}, $arg->{Proto});
108     $fh->bind($sa[0])
109     or croak "bind($arg->{LocalHost}:$arg->{LocalPort}): $!";
110     }
111    
112     $fh;
113     }
114    
115     sub new {
116     my $class = shift;
117     my %arg = @_;
118     my $fh;
119    
120     $arg{Proto} ||= 'tcp';
121     $arg{LocalHost} ||= delete $arg{LocalAddr};
122     $arg{PeerHost} ||= delete $arg{PeerAddr};
123     defined ($arg{Type}) or $arg{Type} = $arg{Proto} eq "tcp" ? SOCK_STREAM : SOCK_DGRAM;
124    
125     if ($arg{PeerHost}) {
126     my @sa = _sa($arg{PeerHost}, $arg{PeerPort}, $arg{Proto});
127    
128     for (@sa) {
129     $fh = $class->_prepare_socket(\%arg)
130     or return;
131    
132     $! = 0;
133    
134     if ($fh->connect($_)) {
135     next unless writable $fh;
136     $! = unpack "i", $fh->getsockopt(SOL_SOCKET, SO_ERROR);
137     }
138    
139     $! or last;
140    
141     $!{ECONNREFUSED} or $!{ENETUNREACH} or $!{ETIMEDOUT} or $!{EHOSTUNREACH}
142     or return;
143 root 1.12
144     undef $fh;
145 root 1.1 }
146     } else {
147     $fh = $class->_prepare_socket(\%arg)
148     or return;
149     if (exists $arg{Listen}) {
150     $fh->listen($arg{Listen})
151     or return;
152     }
153     }
154    
155     $fh;
156     }
157    
158     =item connect, listen, bind, getsockopt, setsockopt,
159 root 1.8 send, recv, peername, sockname, shutdown
160 root 1.1
161 root 1.5 Do the same thing as the perl builtins or IO::Socket methods (but return
162     true on EINPROGRESS). Remember that these must be method calls.
163 root 1.1
164     =cut
165    
166 root 1.3 sub connect { connect tied(${$_[0]})->[0], $_[1] or $! == Errno::EINPROGRESS }
167     sub bind { bind tied(${$_[0]})->[0], $_[1] }
168     sub listen { listen tied(${$_[0]})->[0], $_[1] }
169     sub getsockopt { getsockopt tied(${$_[0]})->[0], $_[1], $_[2] }
170     sub setsockopt { setsockopt tied(${$_[0]})->[0], $_[1], $_[2], $_[3] }
171     sub send { send tied(${$_[0]})->[0], $_[1], $_[2], @_ > 2 ? $_[3] : () }
172     sub recv { recv tied(${$_[0]})->[0], $_[1], $_[2], @_ > 2 ? $_[3] : () }
173 root 1.5 sub sockname { getsockname tied(${$_[0]})->[0] }
174     sub peername { getpeername tied(${$_[0]})->[0] }
175 root 1.9 sub shutdown { shutdown tied(${$_[0]})->[0], $_[1] }
176 root 1.1
177 root 1.10 =item ($fh, $peername) = $listen_fh->accept
178 root 1.1
179     In scalar context, returns the newly accepted socket (or undef) and in
180 root 1.4 list context return the ($fh, $peername) pair (or nothing).
181 root 1.1
182     =cut
183    
184     sub accept {
185     my ($peername, $fh);
186     while () {
187 root 1.3 $peername = accept $fh, tied(${$_[0]})->[0]
188 root 1.4 and return wantarray
189 root 1.11 ? ($_[0]->new_from_fh($fh), $peername)
190     : $_[0]->new_from_fh($fh);
191 root 1.1
192     return unless $!{EAGAIN};
193    
194     $_[0]->readable or return;
195     }
196     }
197    
198     1;
199    
200     =head1 AUTHOR
201    
202     Marc Lehmann <pcg@goof.com>
203     http://www.goof.com/pcg/marc/
204    
205     =cut
206