On May 19, 2011, at 03:18, Gabor Szabo wrote: > On Thu, May 19, 2011 at 5:28 AM, Rocco Caputo <rcap...@pobox.com> wrote: >> On May 18, 2011, at 09:30, Gabor Szabo wrote: >> >>> I have tried to setup a small application that would listen on a TCP >>> port and get commands 'start' and 'stop', >>> When receiving start it should create a new POE::Session to count. >>> When receiving stop it should stop the counting and destroy that >>> POE::Session. It seems I managed to do it but I'd be glad to receive >>> comments about the code: >> >> [code removed] >> >> Hi, Gabor. Please see the revised code below. The major change was to >> eliminate the counting session. They didn't seem necessary, and their >> presence just complicated things. > > thanks for your reply. That looks indeed more simple. Reading again > the documentation, > do I understand correctly that already the ClientConnected and > ClientInput are callbacks of > the individual, connection related POE::Session?
Yes, all the /^Client/ callbacks are for individual connection related POE::Session instances. > Further working on the application I am writing, I'll need to have > several counters > and keep the counters work even after the original requester disconnected. > Trying with one, in my example it worked as the counter Session was not > related > to the connection. > I wonder if in your solution, can I tell the session to remain active > even after > the user disconnected? Can I also remove it from the concurrency counter? > > Would in this case the separate POE::Session work better? Yes. I assume your independent counters have names. I've attached a revised design to account for multiple named timers. Since the code's getting larger, I modularized it a bit, making it even larger still. Sample output: 1) macbookpoe:~% nc localhost 12345 Type 'start', 'stop', or 'list': list No known counters. start aaa Starting counter aaa... start bbb Starting counter bbb... start ccc Starting counter ccc... list Known counters at Sun May 22 01:56:51 2011: aaa:30152, bbb:13406, ccc:3525 stop bbb Stopping counter bbb... list Known counters at Sun May 22 01:56:54 2011: aaa:50326, ccc:23699 ^C 1) macbookpoe:~% nc localhost 12345 Type 'start', 'stop', or 'list': list Known counters at Sun May 22 01:56:57 2011: aaa:70739, ccc:44112 ^C -- Rocco Caputo <rcap...@pobox.com>
#!/usr/bin/perl use warnings; use strict; use 5.010; use POE qw(Component::Server::TCP); { package Counter; use warnings; use strict; use POE; # For KERNEL, HEAP, ARG0, etc. my %counter; # Spawn the Counter session. # There may be only one due to the scoping of %counter. # # A design incorporating multiple Counter sessions would imply each # had its own counter namespace. I'd use $_[HEAP]{counter} in that # case. # # But for now I'm using %counter in closures to simplify the code. sub spawn { POE::Session->create( inline_states => { _start => sub { # The alias is sufficient to keep this session # around as long as other sessions exist. $_[KERNEL]->alias_set("counter"); }, count => sub { my ($kernel, $counter_name) = @_[KERNEL, ARG0]; # The counter was deleted in stop_counter? # By returning here, we stop perpetuating the recursive # yield() that drives this counter. return unless exists $counter{$counter_name}; print "Counter $counter_name = ", ++$counter{$counter_name}, "\n"; # The yield() here perpetuates the counter. $kernel->yield( count => $counter_name ); }, start_counter => sub { my ($kernel, $counter_name) = @_[KERNEL, ARG0]; # Don't start a counter that's already running. return if exists $counter{$counter_name}; $counter{$counter_name} = 0; $kernel->yield( count => $counter_name ); }, stop_counter => sub { my ($kernel, $counter_name) = @_[KERNEL, ARG0]; # Don't stop a counter that's not running. return unless exists $counter{$counter_name}; # Deleting the counter stops it. delete $counter{$counter_name}; }, }, ); } # These subs that follow are plain class methods. # # I've put the POE callbacks into anonymous coderefs above to keep # them visually distinct from plain methods. I often also use # prefixes like _poe_foo() to indicate private POE callbacks. sub start { my ($class, $counter_name) = @_; # start() passes a message from the client connection session to # the counter session. The counters run in a dedicated session # separate from each connection, so connections can come and go # independently from counters. $poe_kernel->post( counter => start_counter => $counter_name ); } sub stop { my ($class, $counter_name) = @_; delete $counter{$counter_name}; } sub list { return %counter; } } ### Abstract the server into its own package for tidiness. { package Server; use warnings; use strict; use POE; # For KERNEL, HEAP, ARG0, etc. sub spawn { my ($class, $port) = @_; POE::Component::Server::TCP->new( Port => $port, ClientInput => \&_poe_client_input, ClientConnected => \&_poe_client_connected, ); print "Counter server is listening on *:$port...\n"; } sub _poe_client_input { my ($kernel, $heap, $input) = @_[KERNEL, HEAP, ARG0]; print "client input: $input\n"; if ($input =~ /^start\s+(\S+)/) { $heap->{client}->put("Starting counter $1..."); Counter->start($1); return; } if ($input =~ /^stop\s+(\S+)/) { $heap->{client}->put("Stopping counter $1..."); Counter->stop($1); return; } if ($input eq 'list') { my %counters = Counter->list(); my @counter_names = sort keys %counters; if (scalar keys %counters) { $heap->{client}->put( "Known counters at " . scalar(localtime) . ": " . join(", ", map { "$_:$counters{$_}" } sort keys %counters) ); } else { $heap->{client}->put("No known counters."); } return; } $heap->{client}->put("Invalid command '$input'"); return; } sub _poe_client_connected { $_[HEAP]{client}->put("Type 'start', 'stop', or 'list':"); } } ### Main program. # # The TCP server could also be abstracted into its own package. Counter->spawn(); Server->spawn(12345); POE::Kernel->run(); exit;