Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
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 about the Monastery: (5)
As of 2015-07-04 22:43 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (60 votes), past polls