ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro/Coro/Socket.pm
Revision: 1.6
Committed: Sat Jul 28 01:41:58 2001 UTC (22 years, 10 months ago) by root
Branch: MAIN
Changes since 1.5: +16 -7 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), $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
140 }
141
142 $fh;
143 }
144
145 =item connect, listen, bind, accept, getsockopt, setsockopt,
146 send , recv, getpeername, getsockname
147
148 Do the same thing as the perl builtins (but return true on
149 EINPROGRESS). Remember that these must be method calls.
150
151 =cut
152
153 sub connect { connect tied(${$_[0]})->{fh}, $_[1] or $! == Errno::EINPROGRESS }
154 sub bind { bind tied(${$_[0]})->{fh}, $_[1] }
155 sub listen { listen tied(${$_[0]})->{fh}, $_[1] }
156 sub getsockopt { getsockopt tied(${$_[0]})->{fh}, $_[1], $_[2] }
157 sub setsockopt { setsockopt tied(${$_[0]})->{fh}, $_[1], $_[2], $_[3] }
158 sub send { send tied(${$_[0]})->{fh}, $_[1], $_[2], @_ > 2 ? $_[3] : () }
159 sub recv { recv tied(${$_[0]})->{fh}, $_[1], $_[2], @_ > 2 ? $_[3] : () }
160 sub setsockname { getsockname tied(${$_[0]})->{fh} }
161 sub setpeername { getpeername tied(${$_[0]})->{fh} }
162
163 sub accept {
164 my $fh;
165 accept $fh, tied(${$_[0]})->{fh} and new_from_fh Coro::Handle $fh;
166 }
167
168 1;
169
170 =head1 AUTHOR
171
172 Marc Lehmann <pcg@goof.com>
173 http://www.goof.com/pcg/marc/
174
175 =cut
176