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