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