Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

RPC-like behavior in a UDP server

by rje (Deacon)
on Jul 13, 2004 at 12:43 UTC ( [id://373948]=perlquestion: print w/replies, xml ) Need Help??

rje has asked for the wisdom of the Perl Monks concerning the following question:

I'm writing a UDP (yes, UDP) server (in Perl) and would like to make the core code invariant: that is, the server just listens and spawns handlers -- or simply handles jobs serially. So far, so good.

What I've got is clients who send what amount to remote procedure calls to the server: a command and a bunch of arguments. So far, so good.

Now, I'd like to put the logic for each 'procedure call' in its own file. One file for 'getFoo' called 'getFoo.pm', one file for 'getBar' called 'getBar.pm' or something like that. The reason I want to do this is that I think it separates the server-side logic admirably, and makes it easy to 'drop in' new 'procedures' without having to change the server much (or at all). I think. Maybe.

Is this more trouble than its worth? Am I going to hit serious runtime delays as my server searches for new code? Is there a module on CPAN that does this already? Am I better off using LWP and essentially going the Webservices route?

Thanks y'all.

Rob

Update. Here's my server. Nothing groundbreaking about it, stolen from stuff found on Perlmonks and in the Cookbook. But I suspect that pg's suggestion may be quite productive.

#!/usr/bin/perl -w $|++; use strict; use IO::Socket; use Sys::Hostname; use DBI; use TolDatabase; my $HOST = 'localhost'; my $db = new TolDatabase(); $db->connect( 'db' => 'test', 'host' => $HOST, 'port' => '', 'socket' => '', 'usr' => '', 'passwd' => '' ); print "Starting server.\n"; my $SERVERPORT = 2001; #my $REPLYPORT = 2000; my $DATAGRAM_SIZE = 4000; my $server = IO::Socket::INET->new(LocalPort => $SERVERPORT, Proto => "udp", Reuse => 1 ) or die "Can't create server: $@"; $server->blocking(0); print "Listening on port $SERVERPORT.\n"; my ($datagram,$flags); my ($omo,$od) = ('',''); while( my $client = $server->recv($datagram,$DATAGRAM_SIZE,$flags) ) { chomp( $datagram ); my ($func, @args) = split ' ', $datagram; next unless $func; my $msg; if ( $func eq 'distance' ) { $msg = distance( @args ); } else { $msg = $db->process( $func, @args ); } my $ipaddr = $server->peerhost; my $port = $server->peerport; my ($s,$m,$h,$d,$mo,$yr) = localtime(time); $mo++; if ( $omo ne $mo || $od ne $d ) { print "\n$mo/$d\n\n"; $omo = $mo; $od = $d; } printf( "%02s:%02s:%02s $ipaddr $datagram\n", $h, $m, $s); $server->send( $msg ); # my $response = IO::Socket::INET->new(Proto=>"udp",PeerHost=>$ipadd +r, PeerPort=>$REPLYPORT); # $response->send( $msg ); } # # Middleware commands to implement: # # breakout ship-id # buyFuel player-id ship-id cash-left # buyShip player-id ship-id cash-left # dock ship-id dock-id # getPlanetData planet-id # getShipData ship-id # getSystemData system-id # jump ship-id # land ship-id landing-field-id # land ship-id planet-surface-id # launch ship-id # load cargo-id ship-id # load passenger-id ship-id # login user name # look location-id # move object-id destination-id # orbit ship-id orbit-id # setJump ship-id destination-id distance # unload cargo-id destination-id # unload passenger-id destination-id # # # Operational rules to automate: # # 1. Update registry for all movement, including freight loading/un +loading and entering jumpspace. # 2. Reset a ship's location and ETA parameters when breaking out of + jump. # 3. Make bank transaction, move ship to landing field, and transfer + ownership when buying a ship. # 4. Set destination and distance, and add route, when filing flight + plans. # 5. Subtract fuel and set ETA when entering jumpspace. # 6. Update free space when loading passengers, freight. # 7. Subtract price when buying fuel. # 8. Unload automatically upon docking/landing. # # # lr-scan system-id range-in-parsecs # sub longRangeScan { my $sysid = shift; my $range = shift; my $sys = "select h.ring,h.ray from starsystem s,hex h where s.hexi +d=h.id and s.id=$sysid"; my $sref = $db->getRows( $sys )->fetchrow_href(); my $ring = $sref->{ring}; my $ray = $sref->{ray}; print "ring/ray: $ring/$ray\n"; my $sql = "select s.*,h.ring,h.ray from starsystem s join hex h on +(s.hexid=h.id)" . " where h.ring > " . ($ring-$range-1) . " and h.ring < " . ($ring+$range+1) . " and h.ray > " . ($ray-$range-1) . " and h.ray < " . ($ray+$range+1) . " and not (h.ring=$ring and h.ray=$ray)"; my $sth = $db->getRows( $sql ); my $href; my $out = ''; while( $href = $sth->fetchrow_hashref() ) { if ( &distance( $ring, $ray, $href->{ring}, $href->{ray} ) <= $r +ange ) { $out .= $db->materializeRow( $href ); } } return $out; } sub distance { my $ring1 = shift; my $ray1 = shift; my $ring2 = shift; my $ray2 = shift; my $a1 = $ring1 + int((1+$ray1)/2); my $a2 = $ring2 + int((1+$ray2)/2); my $d1 = abs( $a1 - $a2 ); my $d2 = abs( $ray1 - $ray2 ); my $d3 = abs( $a1 - $ray1 - $a2 + $ray2 ); my @d = sort ($d1, $d2, $d3); return $d[2]; }

Replies are listed 'Best First'.
Re: RPC-like behavior in a UDP server
by hardburn (Abbot) on Jul 13, 2004 at 13:05 UTC

    Nothing wrong with that approach. It's a simple application of polymorphism. You may want to load up a pre-defined list of modules when the server starts up to avoid delays when the client connects.

    ----
    send money to your kernel via the boot loader.. This and more wisdom available from Markov Hardburn.

Re: RPC-like behavior in a UDP server
by sri (Vicar) on Jul 13, 2004 at 14:13 UTC
    I like to use POE for such things.
    Maybe there is something in the Cookbook for you.
Re: RPC-like behavior in a UDP server
by pg (Canon) on Jul 13, 2004 at 14:56 UTC

    I would suggest a better infrastructure. Split your program into pieces according to functionality:

    • To have a proxy, which handles UDP communication. It receives function calls (simply a UDP packet) from clients, and dispatch them to various servers accordingly.
    • Servers, those are the modules/files you talked about. They receives function calls relevant to them, process and returns.

    This does gives you the ability to extend the capacity of your application freely.

      I'm a little confused at your terminology. When you say 'servers', do you mean that each module is a process, and communication between servers and proxy is via IPC or similar? (This sounds not unlike a POElike solution).

      Or are you simply saying that the proxy 'dispatches' calls to 'servers' by way of calling functions/methods on the appropriate packages/objects? (This sounds not unlike my and others' simple solutions).

      Either way (or some other way), thanks for the input.

      Rob
Re: RPC-like behavior in a UDP server
by bl0rf (Pilgrim) on Jul 13, 2004 at 17:04 UTC
    Hi rje!
    I have experience with reinventing the wheel on this type of thing ( wrote a Java parallel processing framework from scratch )</brag> and I think that its not going to be a lot of work ( you have a good idea for it ) and that you'll have fun doing it.

    You might do something like this:

    opendir( DIR, $command-directory ) || die "cant open dir: $!"; @commands = readdir( DIR ); @commands = grep( m!\.pm$!, @commands ); # only .pm files $torun = "thetime"; $arg1 = '.....'; $arg2 = 'etc.'; foreach ( @commands ) { if( $_ eq $torun ){ $continue = 'true' } else{ $continue = undef } } if( defined( $continue ) ) { require ("$torun" . "\.pm"); ${$torun}::{$torun}->( $arg1, $arg2 ); # run subroutine $torun i +n file $torun.pm } if( !defined( $continue ) ) { print "no such method call: $torun\n"; }
    You could have the procedure subroutine and file share the same name. * only key parts are tested. Good luck!

      This is a nice solution, a good balance between dynamicness and simplicity. Sure I'd have to restart the server to pick up new commands, but this isn't a mission-critical project either, and downtimes are to be expected. I like it!

      Consider your code snippet borrowed!
Re: RPC-like behavior in a UDP server
by dragonchild (Archbishop) on Jul 13, 2004 at 14:17 UTC
    You may be able to modify the various XML-RPC modules to do this ... ?

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://373948]
Approved by Wampa
Front-paged by sri
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-25 20:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found