1# $Id: Receiver.pm 145 2006-12-25 19:09:56Z rcaputo $
2
3=head1 NAME
4
5POE::Stage::Receiver - a simple UDP recv/send component
6
7=head1 SYNOPSIS
8
9	# Note, this is not a complete program.
10	# See the distribution's examples directory.
11
12	use POE::Stage::Receiver;
13	my $stage = POE::Stage::Receiver->new();
14	my $request = POE::Request->new(
15		stage         => $stage,
16		method        => "listen",
17		on_datagram   => "handle_datagram",
18		on_recv_error => "handle_error",
19		on_send_error => "handle_error",
20		args          => {
21			bind_port   => 8675,
22		},
23	);
24
25	# Echo the datagram back to its sender.
26	sub handle_datagram :Handler {
27		my ($rsp, $arg_remote_address, $arg_datagram);
28		$rsp->recall(
29			method            => "send",
30			args              => {
31				remote_address  => $arg_remote_address,
32				datagram        => $arg_datagram,
33			},
34		);
35	}
36
37=head1 DESCRIPTION
38
39POE::Stage::Receiver is a simple UDP receiver/sender stage.  It's
40simple, partly because it's incomplete.
41
42POE::Stage::Receiver has two public methods: listen() and send().  It
43emits a small number of message types: datagram, recv_error, and
44send_error.
45
46=cut
47
48package POE::Stage::Receiver;
49
50use POE::Stage qw(:base req);
51
52use POE::Watcher::Input;
53use IO::Socket::INET;
54use constant DATAGRAM_MAXLEN => 1024;
55
56=head1 PUBLIC COMMANDS
57
58Commands are invoked with POE::Request objects.
59
60=head2 listen bind_port => INTEGER
61
62Bind to a port on all local interfaces and begin listening for
63datagrams.  Per the SYNOPSIS, the listen request should also map
64POE::Stage::Receiver's message types to appropriate handlers.
65
66=cut
67
68sub listen :Handler {
69	my ($self, $args) = @_;
70
71	my $req_bind_port = delete $args->{bind_port};
72
73	my $req_socket = IO::Socket::INET->new(
74		Proto     => 'udp',
75		LocalPort => $req_bind_port,
76	);
77	die "Can't create UDP socket: $!" unless $req_socket;
78
79	my $req_udp_watcher = POE::Watcher::Input->new(
80		handle    => $req_socket,
81		on_input  => "_handle_input"
82	);
83}
84
85sub _handle_input :Handler {
86	my ($self, $args) = @_;
87
88	my $req_socket;
89	my $remote_address = recv(
90		$req_socket,
91		my $datagram = "",
92		DATAGRAM_MAXLEN,
93		0
94	);
95
96	if (defined $remote_address) {
97		req->emit(
98			type              => "datagram",
99			args              => {
100				datagram        => $datagram,
101				remote_address  => $remote_address,
102			},
103		);
104	}
105	else {
106		req->emit(
107			type      => "recv_error",
108			args      => {
109				errnum  => $!+0,
110				errstr  => "$!",
111			},
112		);
113	}
114}
115
116=head2 send datagram => SCALAR, remote_address => ADDRESS
117
118Send a datagram to a remote address.  Usually called via recall() to
119respond to a datagram emitted by the Receiver.
120
121=cut
122
123sub send :Handler {
124	my ($self, $args) = @_;
125
126	my $req_socket;
127	return if send(
128		$req_socket,
129		$args->{datagram},
130		0,
131		$args->{remote_address},
132	) == length($args->{datagram});
133
134	req->emit(
135		type      => "send_error",
136		args      => {
137			errnum  => $!+0,
138			errstr  => "$!",
139		},
140	);
141}
142
1431;
144
145=head1 PUBLIC RESPONSES
146
147Here's what POE::Stage::Resolver will send back.
148
149=head2 "datagram" (datagram, remote_address)
150
151POE::Stage::Receiver emits a "datagram" message whenever it
152successfully recv()s a datagram from some remote peer.  The datagram
153message includes two parameters: "datagram" contains the received
154data, and "remote_address" contains the address that sent the
155datagram.
156
157Both parameters can be passed back to the POE::Stage::Receiver's
158send() method, as is done in the SYNOPSIS.
159
160	sub on_datagram {
161		my ($arg_datagram, $arg_remote_address);
162		my $output = function_of($arg_datagram);
163		my $req->recall(
164			method => "send",
165			args => {
166				remote_address => $arg_remote_address,
167				datagram => $output,
168			}
169		);
170	}
171
172=head2 "recv_error" (errnum, errstr)
173
174The stage encountered an error receiving from a peer.  "errnum" is the
175numeric form of $! after recv() failed.  "errstr" is the error's
176string form.
177
178	sub on_recv_error {
179		goto &on_send_error;
180	}
181
182=head2 "send_error" (errnum, errstr)
183
184The stage encountered an error receiving from a peer.  "errnum" is the
185numeric form of $! after send() failed.  "errstr" is the error's
186string form.
187
188	sub on_send_error {
189		my ($arg_errnum, $arg_errstr);
190		warn "Error $arg_errnum : $arg_errstr.  Shutting down.\n";
191		my $req_receiver = undef;
192	}
193
194=head1 BUGS
195
196See L<http://thirdlobe.com/projects/poe-stage/report/1> for known
197issues.  See L<http://thirdlobe.com/projects/poe-stage/newticket> to
198report one.
199
200POE::Stage is too young for production use.  For example, its syntax
201is still changing.  You probably know what you don't like, or what you
202need that isn't included, so consider fixing or adding that, or at
203least discussing it with the people on POE's mailing list or IRC
204channel.  Your feedback and contributions will bring POE::Stage closer
205to usability.  We appreciate it.
206
207=head1 SEE ALSO
208
209L<POE::Stage> and L<POE::Request>.  The examples/udp-peer.perl program
210in POE::Stage's distribution.
211
212=head1 AUTHORS
213
214Rocco Caputo <rcaputo@cpan.org>.
215
216=head1 LICENSE
217
218POE::Stage::Receiver is Copyright 2005-2006 by Rocco Caputo.  All rights
219are reserved.  You may use, modify, and/or distribute this module
220under the same terms as Perl itself.
221
222=cut
223