edited to reflect the moving around of the demo files
[urisagit/Stem.git] / lib / Stem / UDPMsg.pm
CommitLineData
4536f655 1# File: Stem/UDPMsg.pm
2
3# This file is part of Stem.
4# Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
5
6# Stem is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10
11# Stem is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15
16# You should have received a copy of the GNU General Public License
17# along with Stem; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20# For a license to use the Stem under conditions other than those
21# described here, to purchase support for this software, or to purchase a
22# commercial warranty contract, please contact Stem Systems at:
23
24# Stem Systems, Inc. 781-643-7504
25# 79 Everett St. info@stemsystems.com
26# Arlington, MA 02474
27# USA
28
29package Stem::UDPMsg ;
30
31use strict ;
32
33use Data::Dumper ;
34use IO::Socket ;
35
36my $attr_spec = [
37
38 {
39 'name' => 'reg_name',
40 'help' => <<HELP,
41The registration name for this Cell
42HELP
43 },
44
45 {
46 'name' => 'bind_host',
47 'help' => <<HELP,
48The UDP socket is bound to this host for receiving or sending packets
49HELP
50 },
51
52 {
53 'name' => 'bind_port',
54 'help' => <<HELP,
55The UDP socket is bound to this port for receiving or sending packets
56HELP
57 },
58 {
59 'name' => 'send_host',
60 'help' => <<HELP,
61The UDP packet is sent to this host if the send message has no host
62HELP
63 },
64 {
65 'name' => 'send_port',
66 'help' => <<HELP,
67The UDP packet is sent to this port if the send message has no port
68HELP
69 },
70 {
71 'name' => 'bind_port',
72 'help' => <<HELP,
73The UDP socket is bound to this port for receiving or sending packets
74HELP
75 },
76 {
77 'name' => 'server',
78 'type' => 'boolean',
79 'help' => <<HELP,
80Marks this socket as a server and it expect to receive UDP packets
81HELP
82 },
83 {
84 'name' => 'max_recv_size',
85 'default' => 4096,
86 'help' => <<HELP,
87Maximum size of received UDP packets.
88
89HELP
90 },
91 {
92 'name' => 'data_addr',
93 'help' => <<HELP,
94Send received UDP packets as 'udp_data' type messages to this address
95HELP
96 },
97 {
98 'name' => 'error_addr',
99 'help' => <<HELP,
100Send received UDP errors as 'udp_error' type messages to this address
101HELP
102 },
103 {
104 'name' => 'timeout_addr',
105 'help' => <<HELP,
106Send UDP timeouts as 'udp_timeout' type messages to this address
107HELP
108 },
109 {
110 'name' => 'object',
111 'help' => <<HELP,
112This object will get the callbacks
113HELP
114 },
115 {
116 'name' => 'timeout',
117 'help' => <<HELP,
118This sets the timeout period to wait for UDP data. If no data has been
119received since the timer started, a timeout message or callback will
120be triggered.
121HELP
122 },
123 {
124 'name' => 'recv_method',
125 'default' => 'udp_received',
126 'help' => <<HELP,
127This method will be called in the object when a UDP packet has been received
128HELP
129 },
130 {
131 'name' => 'error_method',
132 'default' => 'udp_error',
133 'help' => <<HELP,
134This method will be called in the object when a UDP had been detected
135HELP
136 },
137 {
138 'name' => 'timeout_method',
139 'default' => 'udp_timeout',
140 'help' => <<HELP,
141This method will be called in the object when no UDP data has been received
142after the timeout period.
143HELP
144 },
145 {
146 'name' => 'log_name',
147 'help' => <<HELP,
148Log to send store sent and received messages
149HELP
150 },
151] ;
152
153
154sub new {
155
156 my( $class ) = shift ;
157
158 my $self = Stem::Class::parse_args( $attr_spec, @_ ) ;
159 return $self unless ref $self ;
160
161 my $info_text = '' ;
162
163 my $socket = IO::Socket::INET->new( 'Proto' => 'udp' ) ;
164 $self->{'socket'} = $socket ;
165
166 if ( my $bind_port = $self->{'bind_port'} ) {
167
168 $info_text .= "Port: $bind_port\n" ;
169
170 my $bind_ip ;
171 my $bind_host = $self->{'bind_host'} ;
172
173 if ( length $bind_host ) {
174
175 $bind_ip = inet_aton( $bind_host ) ;
176 $info_text .= "Host: $bind_host\n" ;
177 }
178 else {
179
180 $bind_ip = INADDR_ANY ;
181 $info_text .= "Host: INADDR_ANY\n" ;
182 }
183
184 $socket->bind( $bind_port, $bind_ip ) ;
185 }
186
187 my @timeout_args = ( $self->{'timeout'} ) ?
188 ( 'timeout' => $self->{'timeout'} ) : () ;
189
190
191 if ( $self->{'server'} ) {
192
193 $self->{'read_event'} = Stem::Event::Read->new(
194 'object' => $self,
195 'fh' => $socket,
196 @timeout_args,
197 ) ;
198 }
199
200 my $reg_name = $self->{'reg_name'} || 'NONE' ;
201 my $sock_host = $socket->sockhost ;
202 my $sock_port = $socket->sockport ;
203
204 $self->{'info'} = <<INFO ;
205---------------------
206UDPMsg
207
208Cell name: $reg_name
209Port: $sock_port
210---
211$info_text
212---------------------
213
214INFO
215
216 return $self ;
217}
218
219sub status_cmd {
220
221 my ( $self ) = @_ ;
222
223 return $self->{'info'} ;
224}
225
226
227sub readable {
228
229 my( $self ) = @_ ;
230
231#print "UDP readable\n" ;
232
233 my $udp_data ;
234
235 my $udp_addr = $self->{'socket'}->recv( $udp_data,
236 $self->{'max_recv_size'} ) ;
237
238#print "UDP READ [$udp_data]\n" ;
239
240# handle errors
241
242 unless( defined( $udp_addr ) ) {
243
244 if ( my $error_addr = $self->{'error_addr'} ) {
245
246 my $msg = Stem::Msg->new(
247 'to' => $error_addr,
248 'from' => $self->{'from_addr'},
249 'type' => 'udp_error',
250 'data' => \"$!",
251 ) ;
252
253#print $msg->dump( 'UDP error' ) ;
254 $msg->dispatch() ;
255 return ;
256 }
257
258# send the data via a callback
259
260 if ( my $obj = $self->{'object'} ) {
261
262 my $method = $self->{'error_method'} ;
263 $obj->$method( \"$!" ) ;
264 }
265
266 return ;
267 }
268
269 my( $from_port, $from_host ) = unpack_sockaddr_in( $udp_addr ) ;
270
271 $from_host = inet_ntoa( $from_host ) ;
272
273# send out the data as a stem message
274
275#print "ADDR [$self->{'data_addr'}]\n" ;
276
277 if ( my $data_addr = $self->{'data_addr'} ) {
278
279 my $msg = Stem::Msg->new(
280 'to' => $data_addr,
281 'from' => $self->{'reg_name'},
282 'type' => 'udp_data',
283 'data' => {
284 'data' => \$udp_data,
285 'from_port' => $from_port,
286 'from_host' => $from_host,
287 },
288 ) ;
289
290#print $msg->dump( 'UDP recv' ) ;
291 $msg->dispatch() ;
292 return ;
293 }
294
295# send the data via a callback
296
297 if ( my $obj = $self->{'object'} ) {
298
299 my $method = $self->{'recv_method'} ;
300 $obj->$method( \$udp_data, $from_port, $from_host ) ;
301 }
302
303 return ;
304}
305
306sub read_timeout {
307
308 my( $self ) = @_ ;
309
310#print "UDP timeout\n" ;
311
312# send out the timeout as a stem message
313
314 if ( my $timeout_addr = $self->{'timeout_addr'} ) {
315
316 my $msg = Stem::Msg->new(
317 'to' => $timeout_addr,
318 'from' => $self->{'reg_name'},
319 'type' => 'udp_timeout',
320 ) ;
321
322#print $msg->dump( 'UDP timeout' ) ;
323 $msg->dispatch() ;
324 return ;
325 }
326
327# send the timeout via a callback
328
329 if ( my $obj = $self->{'object'} ) {
330
331 my $method = $self->{'timeout_method'} ;
332 $obj->$method() ;
333 }
334
335 return ;
336}
337
338
339sub send_cmd {
340
341 my ( $self, $msg ) = @_ ;
342
343#print $msg->dump( 'UDP send' ) ;
344 my $msg_data = $msg->data() ;
345
346 my $send_port = $msg_data->{'send_port'} || $self->{'send_port'} ;
347 my $send_host = $msg_data->{'send_host'} || $self->{'send_host'} ;
348
349 my $udp_data = $msg_data->{'data'} ;
350
351 return $self->_send( $udp_data, $send_port, $send_host ) ;
352}
353
354sub send {
355
356 my ( $self, $data, %args ) = @_ ;
357
358 my $send_port = $args{'send_port'} || $self->{'send_port'} ;
359 my $send_host = $args{'send_host'} || $self->{'send_host'} ;
360
361 return $self->_send( $data, $send_port, $send_host ) ;
362}
363
364sub _send {
365
366 my( $self, $data, $port, $host ) = @_ ;
367
368 $host or return "Missing send_host for UDP send" ;
369 $port or return "Missing send_port for UDP send" ;
370
371#print "P $port H $host\n" ;
372
373 my $host_ip = inet_aton( $host ) ;
374 $host_ip or return "Bad host '$host'" ;
375
376 my $send_addr = pack_sockaddr_in( $port, $host_ip ) ;
377
378 $data = $$data if ref $data ;
379
380 my $byte_cnt = $self->{'socket'}->send( $data, 0, $send_addr ) ;
381
382#print "BYTES [$byte_cnt]\n" ;
383
384 return "send error: $!" unless defined $byte_cnt ;
385 return ;
386}
387
388
389sub shut_down_cmd {
390
391 my ( $self, $msg ) = @_ ;
392
393#print $msg->dump( 'SHUT' ) ;
394
395 $self->shut_down() ;
396
397 return ;
398}
399
400sub shut_down {
401
402 my ( $self ) = @_ ;
403
404 if ( my $read_event = delete $self->{'read_event'} ) {
405
406 $read_event->cancel() ;
407 }
408
409 delete $self->{'object'} ;
410
411 my $socket = delete $self->{'socket'} ;
412
413 close $socket ;
414}
415
4161 ;