#!/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(
as well as other elements:
----------
I live here!