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