#!/usr/bin/perl use strict; use warnings; use IO::Socket; use IO::Select; use Socket qw( sockaddr_in inet_ntoa ); # Socket to receive commands my $controllers_listener = IO::Socket::INET->new( Proto => 'udp', LocalPort => 50605, ReuseAddr => 1, ) or die "new socket: $!"; # Socket for listening to incoming connections my $clients_listener = IO::Socket::INET->new( Proto => 'tcp', LocalPort => 50605, ReuseAddr => 1, Listen => 3, ) or die "new socket: $!"; my %clients; # Use select to coordinate requests my $sel = IO::Select->new($controllers_listener, $clients_listener); my @slides = load_slides(); $|++; my $slide = 0; # Tracks current slide my %shown_marker_for; # Tracks slides sent to different clients my %callback_for = ( # Available commands 'first' => sub { goto_slide(0); }, 'last' => sub { goto_slide($#slides); }, 'next' => sub { goto_slide($slide + 1); }, previous => sub { goto_slide($slide - 1); }, show => sub { my $slide = shift; goto_slide($slide - 1) if $slide; }, ); # Main loop while (my @ready = $sel->can_read()) { FILEHANDLE: for my $fh (@ready) { if ($fh == $clients_listener) { # add new client my ($socket, $peer) = $clients_listener->accept(); exhaust_input($socket); # ignore input data $clients{$socket} = [$socket, $peer]; $sel->add($socket); activity($peer, 'client entered'); # Send the current slide print_header($socket); print_slide($socket, $slide); } ## end if ($fh == $clients_listener) elsif (exists $clients{$fh}) { # close connection activity($clients{$fh}[1], 'client exited'); $sel->remove($fh); $fh->close(); delete $clients{$fh}; } else { # Command received, via UDP # Command from controller my $peer = $fh->recv(my $command_string, 1500) or next FILEHANDLE; next FILEHANDLE unless $command_string; for my $command_line (split /\n+/, $command_string) { next unless $command_line =~ /\S/; my ($command, @args) = split /\s+/, $command_line; activity($peer, "trying command '$command_line'"); $callback_for{$command}->(@args) if exists $callback_for{$command}; } } ## end else [ if ($fh == $controllers_listener) } ## end for my $fh (@ready) } ## end while (@ready = $sel->can_read... # Log on STDOUT about what's happening sub activity { my ($peer, $msg) = @_; my ($port, $iaddr) = sockaddr_in($peer); my $addr = inet_ntoa($iaddr); print {*STDOUT} "$addr:$port - $msg\n"; return; } # Send header to the client sub print_header { my ($socket) = @_; print {$socket} "HTTP/1.1 200 OK\r\n"; print {$socket} "Content-type: text/html\r\n\r\n"; print {$socket} "\n"; } ## end sub print_header sub hide_slide { my ($socket, $slide) = @_; print {$socket} "\n"; } sub print_slide { my ($socket, $slide) = @_; if ($shown_marker_for{$socket}{$slide}) { # just resume print {$socket} "\n"; } else { print {$socket} qq(
$slides[$slide]
); } $shown_marker_for{$socket}{$slide} = 1; } ## end sub print_slide # Transition function, for each client hides the previous # and shows the new sub goto_slide { my ($new_slide) = @_; return unless $new_slide =~ /\A\d+\z/mxs; return unless $new_slide < @slides; my $old_slide = $slide; print {*STDOUT} "going to $new_slide from $old_slide\n"; for my $client (values %clients) { hide_slide($client->[0], $old_slide); print_slide($client->[0], $new_slide); } $slide = $new_slide; return; } ## end sub goto_slide # Eliminate input data coming from clients sub exhaust_input { my ($socket) = @_; my $sel = IO::Select->new($socket); while ($sel->can_read(0)) { $socket->sysread(my $input, 1500); return 0 unless length $input; } return 1; # for many reasons... } ## end sub exhaust_input # Dummy function, loads test slides after __END__ sub load_slides { local $/ = "\n----------\n"; chomp(@slides = ); return @slides; } __END__

This is slide 1

Here you can find the contents for slide #1. You can insert images:

Perl flowers

as well as other elements:

----------

This is slide 2

The contents are completely different here.

solution ----------

This is slide 3

The contents are completely different here, with respect to the other two pages.

solution

I live here! #### shell$ netcat -q 0 -u 50605 <<<'next' shell$ netcat -q 0 -u 50605 <<<'next' shell$ netcat -q 0 -u 50605 <<<'next' shell$ netcat -q 0 -u 50605 <<<'first' shell$ netcat -q 0 -u 50605 <<<'show 2' shell$ netcat -q 0 -u 50605 <<<'previous' shell$ netcat -q 0 -u 50605 <<<'last'