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