Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

P2P Homework

by Beatnik (Parson)
on Dec 09, 2002 at 22:29 UTC ( #218674=sourcecode: print w/ replies, xml ) Need Help??

Category: Networking Code
Author/Contact Info Hendrik Van Belleghem - beatnik - at - quickndirty dot org
Description: This is one of 2 homeworks for Distributed Systems class in my final year of Comp Sci. (The other homework being a webserver and a web client). I already turned this in BTW. This is a classic P2P, Napster style. One core server handles IPs and filenames. Client forks into client & server. PS: This is my 666th node. This homework is probably doomed to get a D mark or something.
#!/usr/bin/perl

#Pear Core server version 0.05

use IO::Socket::INET;
use strict; 
use Time::HiRes; 
#No Go on windows :)
use vars qw(%filebase $socket);

$socket = IO::Socket::INET->new(Listen   => 1, 
                                LocalPort => 9876,
                                PeerAddr => "192.168.0.2",
                                #Change IP
                                Proto    => "tcp",
                                ReuseAddr => 1,
                                Type     => SOCK_STREAM,
);

if (!$socket) { die "No Socket"; }

$|++;

sub timer
{ for my $ip (keys %filebase) 
  { $filebase{$ip}{timestamp}++;
    if ($filebase{$ip}{timestamp} > 5) 
    { logrequest("Deleted $ip -> $filebase{$ip}{timestamp}");
      delete($filebase{$ip}); 
    }
  }
}

#Increase counter every 30 secs
#If above ceiling, remove host
$SIG{ALRM} = \&timer;

if ($^O ne "MSWin32")
{ Time::HiRes::ualarm(30_000_000,30_000_000); }

$socket->autoflush(1);
print "Accepting clients...\n";
#Complex Data Structure met files and IPs
while(my $client = $socket->accept())
{ my $line = <$client>;
  chomp $line;
  my $clientaddress = $client->peerhost;
  $client->autoflush(1);
  #Log it
  if ($line eq "Pear Pear")
  #Send file list
  { $line = <$client>;
    chomp $line;
    while($line)
    { push(@{$filebase{$clientaddress}{files}},{filename=>$line});
      #Stick files in data struc based on IP
      logrequest("$line located at $clientaddress");
      $line = <$client>;
      chomp $line;
    }
    close($client);
  }

  if ($line eq "Orange Orange")
  #Send Time event
  { $filebase{$clientaddress}{timestamp} = 0; 
    logrequest("$clientaddress checked in...");
    close($client);
  }

  if ($line eq "Apple Apple")
  #Query file??
  { $line = <$client>;
    chomp $line;
    if ($line =~ /^Pluck ([^\s]*)$/)
    #extract filename :  Pluck filename
    { my $fruit = $1;
      my @query = ();
      for my $ip (keys %filebase)
      { for my $filerecord (@{$filebase{$ip}{files}})
        { my $filename = ${$filerecord}{filename};
          if ($ip ne $clientaddress && $filename =~ /$fruit/i)
           { push(@query,"$ip;$filename"); }
           #Check data struc for files
        }
      }
      logrequest("$fruit queried by $clientaddress");
      for(@query) { print $client $_,"\n"; }
      #Send IP list to client
    }
    close($client);
  }
} 
close($socket);

sub logrequest
{ open(LOG,">>pear.log") || die $!;
  my $hour = sprintf "%02d",(localtime(time))[2];
  my $minute = sprintf "%02d",(localtime(time))[1];
  my $second = sprintf "%02d",(localtime(time))[0];
  my $day = sprintf "%02d",(localtime(time))[3];
  my $month = sprintf "%02d",(localtime(time))[4]+1;
  my $year = 1900+(localtime(time))[5];
  #sprintf - Zero padding
  
  my $datestring = "$hour:$minute:$second $day/$month/$year ";
  print LOG $datestring,join(" - ",@_),"\n";
  close(LOG);
  #Log connections
}

# ------  Client

#!/usr/bin/perl

use IO::Socket;
use vars qw($pid $server $debug);
use Term::ReadLine;
use Time::HiRes;
use File::Find;
use strict;

$debug = 1;
#1 -> yes
#0 -> no

#Socket to Core
sub createsocket
{ $server = IO::Socket::INET->new(PeerPort => 9876,
                                  PeerAddr => '192.168.0.2',
                                  #Change Core IP
                                  Proto    => 'tcp',
                                  ReuseAddr => 1,
                                  Type     => SOCK_STREAM,
                                );
  $server->autoflush(1);
  debuglog("Created Core Server Connection");
  if (!$server) { return 0; } else { return 1; }
}

sub closesocket
{ close($server); debuglog("Closing Core Server Connection"); }

#Send timestamp
sub sendtime { createsocket(); print $server "Orange Orange\n"; closes
+ocket(); }

#Send file list
sub sendfiles 
{ createsocket();
  print $server "Pear Pear\n";
  #Init file list
  if ($^O ne "MSWin32")
  { File::Find::find({ wanted=> sub 
    { my $filename = $File::Find::name;
      $filename =~ s/^\.\/(.*)$/$1/;
      print $server $filename,"\n" if !-d $File::Find::name; 
    }, follow=>1 }, ".");
  } else
  { sub dodir 
    { opendir(DIR,$_[0]);
      my $dir = $_[0];
      if ($dir !~ /\/$/) { $dir .= "/"; }
      for my $file(readdir(DIR)) 
      { $file =~ s/^\.\/(.*)$/$1/;
        if ($file =~ /^\.\.?$/)
        { next; }
        my $fullpath = $dir.$file;
        if (-d $fullpath) { dodir($fullpath); }
        else { print $server $fullpath,"\n"; }
      }
      closedir(DIR);
    }
    dodir(".");
  }
  debuglog("Sending filelist to Core server");
  closesocket();
}

#Query Core for files
sub queryfile
{ createsocket();
  my $filename = shift;
  print $server "Apple Apple\n";
  print $server "Pluck $filename\n";
  #Query filename
  my @IP = ();
  while(my $line = <$server>) { chomp $line; push(@IP,$line); }
  debuglog("Querying files on Core server");
  closesocket();
  return \@IP;
}

sendtime(); 
sendfiles();

#Send sig every 60 secs
$SIG{ALRM} = \&sendtime;
if ($^O ne "MSWin32")
{ Time::HiRes::ualarm(60_000_000,60_000_000); }

die "can't fork: $!" unless defined($pid = fork());

if ($pid) # client
{ my $term = new Term::ReadLine 'Pear Client';
  my $label = "Keyword : ";
  my $keyword;
  my @db;
  my $file;
  #Fancy input
  while ( defined ($keyword = $term->readline($label)) ) 
  { if ($keyword =~ /^update$/i)
    { sendtime(); sendfiles(); }
    if ($keyword =~ /^(quit|bye|exit)$/i)
    { kill 9,$pid; exit; }
    if ($keyword =~ /^help$/i)
    { print "help:\t\t\tThis page\nupdate:\t\t\tUpdate filelist and ti
+me\nfind <filename>:\tFind filename\nget <number>:\t\tGet file from h
+ost indicated by number\n";
      print "quit:\t\t\tQuit Pear\n";
     }
    if ($keyword =~ /^find ([^\s]*)$/i)
    { $file = $1;
      #Find filename -> Core returns array
      my @foo = @{queryfile($file)};
      my $c = 0;
      for(@foo) 
      { my ($ip,$filename) = split(/;/,$_); push(@db,{IP=>$ip,Filename
+=>$filename}); }
      for(@db) { print join("\t","[$c]",$_->{Filename},$_->{IP}."\n");
+ $c++ }
    }
    if ($keyword =~ /^get (\d*)$/i)
    { my $index = $1;
      my $ip = $db[$index]->{IP};
      #Displays list with index
      my $socket = IO::Socket::INET->new(PeerPort => 6789,
                                         PeerAddr => $ip,
                                         Proto    => "tcp",
                                         ReuseAddr => 1,
                                         Type     => SOCK_STREAM,
                                       );
      if (!$socket) { debuglog("IP $ip is not online"); next; }
      $socket->autoflush(1);
      my $file = $db[$index]->{Filename};
      debuglog("Fetching $file from $ip");
      print $socket "Peach Peach $file\n";
      my $filesize = <$socket>;
      chomp $filesize;
      if ($filesize eq "ERROR") { next; }
      my $data;
      my $buffersize = 4096;
      my $returnsize;
      my $readsize;
      #Dump to file
      if(open(OUTFILE,">$file"))
      { while($returnsize = read($socket,$data,$buffersize) && $readsi
+ze != $filesize) 
        { print OUTFILE $data;
          $readsize += $returnsize;
        }
        close(OUTFILE);
      } else { debuglog("Error opening $file for writing ($ip)"); }
      close($socket); 
    }
  }
} else 
{ my $socket = IO::Socket::INET->new(Listen   => 1, 
                                     LocalPort => 6789,
                                     PeerAddr => "192.168.0.2",
                                     Proto    => "tcp",
                                     ReuseAddr => 1,
                                     Type     => SOCK_STREAM,
  );
 
  if (!$socket) { die "No Socket"; }

  $|++;

  $socket->autoflush(1);

  print "Pear Server Accepting clients...\n";
  while(my $client = $socket->accept())
  { $client->autoflush(1);
    my $line = <$client>;
    chomp $line;
    if ($line =~ /^Peach Peach ([^\s]*)/)
    { my $filename = $1; 
      my $filesize = -s $filename;
      print $client "$filesize\n";
      if(open(FILE,"<$filename"))
      { print $client "$filesize\n";
        my $data;
        my $buffersize = 4096;
        my $returnsize;
        my $readsize;
        while($returnsize = read(FILE,$data,$buffersize) && $readsize 
+!= $filesize) 
        { print $client $data; 
          $readsize += $returnsize;
        }
        close(FILE);
      } else { print $client "ERROR\n"; debuglog("Error opening $filen
+ame for reading - ".$client->peerhost); }
      close($client);
   }
 }
}

sub debuglog
{ if (!$debug) { return }
  open(LOG,">>debug.log") || die $!;
  my $hour = sprintf "%02d",(localtime(time))[2];
  my $minute = sprintf "%02d",(localtime(time))[1];
  my $second = sprintf "%02d",(localtime(time))[0];
  my $day = sprintf "%02d",(localtime(time))[3];
  my $month = sprintf "%02d",(localtime(time))[4]+1;
  my $year = 1900+(localtime(time))[5];
  
  my $datestring = "$hour:$minute:$second $day/$month/$year ";
  print LOG $datestring,join(" - ",@_),"\n";
  close(LOG);
  #Log connections
}  

Comment on P2P Homework
Download Code
Re: P2P Homework
by MZSanford (Curate) on Dec 10, 2002 at 15:17 UTC

    Just a small thing to think about. both logrequest and debuglog call time() and localtime() 6 times. It seems that logrequest() could be called quite a bit, so it may be a worth-while optimization to use @date = localtime(time), and then reformat from there...

    You mentioned this was an assignment, so i assume you went for readability ... so that may explain.

    Just my €0.02
    from the frivolous to the serious

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://218674]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (4)
As of 2014-08-21 04:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (127 votes), past polls