|
Perl-Clients für Standard-DiensteClient für BinärdatenDas folgende Programm hat eine URL als Parameter. Diese URL wird in Host, Port und Dateipfad gesplittet. Dann öffnet das Programm eine Verbindungzum Host und versucht, per HTTP-Get die Datei zu erhalten. Im Erfolgsfall wirdder HTTP-Header ueberlesen und danach die Binärdaten in ein Programmgeleitet. Das Programm kann dann (hoffentlich) die mp3-Daten abspielen.Analog funktioniert das Programm auch mit anderen Dateien (z.B. Bilderoder Programme).
#!/usr/bin/perl# Ein einfacher TCP-Client zum Abspielen von mp3-Dateien# Verwendung: $0 URLuse strict;use IO::Socket;use constant TIMEOUT => 5;my $SOCK = '';my $reply = '';my $content = ''; my $header = '';my $handler = '/usr/bin/audioplay';# Abspielprogrammmy $url = shift @ARGV; # URL zerlegen$url=~m/http\:\/\/([^\:^\/]*)(?:\:(\d+))?\/(.*)/;
my $host = $1;
my $port = $2;
$port = 80 unless($port);
my $file = '/'.$3;
$SOCK = new IO::Socket::INET(PeerAddr => $ARGV[0],
PeerPort => $ARGV[1],
Proto => 'tcp', Timeout => TIMEOUT)
or die "can't connect to $ARGV[0]:$ARGV[1]: $@\n";
my $old_fh = select(SOCK); # Ungepufferte Ausgabe
$|=1; # fuer SOCK einstellen
select($old_fh);
print "Requesting $file..\n";
print SOCK "GET $file HTTP/1.0\n";
print SOCK "Accept: */*\n";
print SOCK "User-Agent: webamp 007\n\n";
print "Waiting for reply..\n";
$header = <SOCK>;
exit unless($header=~m/200|OK/); # Ende bei Fehlermeldung
while($header = <SOCK>) # Header ueberlesen
{
chomp;
last unless(m/\S/);
}
open(HANDLER, "|$handler") or die "Cannot pipe input to $handler: $!\n";
print "Redirecting HTTP filestream to $handler..\n";
while(read(SOCK, $content, 512))
{
print HANDLER $content; # Perl-Strings sind
} # "binaerfest"
$sock->close() if defined $sock;
Portscanner
Das folgende Programm verwendet IO::Socket, um die TCP-Ports
eines Rechners zu untersuchen. Dazu wird ein Socket eröffnet und ein
Connect auf dem gewünschten Port versucht. Wenn auf dem entsprechenden
Port kein Serverprozeß läuft, würde der Connect hängen bleiben.
Dieser Fall wird über einen Timeout mit der Perl-Funktion alarm()
abgefangen. Der Timeout löst einen Interrupt aus, der die Verbindung schließt.
Per Schleife werden alle Ports zwischen zwei Parameterangaben abgefragt.
Das Programm erlaubt die Angabe der zu untersuchenden Rechner (IP-Adresse) auf
der Kommandozeile.
#!/usr/bin/perl -w
use IO::Socket;
use strict;
my $pinghost = '';
$|=1;
foreach $pinghost (@ARGV)
{
&port_scan($pinghost, 1, 1024);
}
exit;
sub port_scan # ($hostip, $lowport, $highport)
{
my $port = 0;
my $iaddr = 0;
my $paddr = 0;
my $connect_time = 1;
my $protocol_name = "tcp";
my $protocol_id = getprotobyname($protocol_name);
my $hostip = shift;
my $lowport = shift;
my $highport = shift;
print "Portscan von $hostip.\n";
for ($port = $lowport; $port <= $highport; $port++)
{
$SIG{"ALRM"} = sub { close(SOCKET); };
alarm $connect_time;
socket(SOCKET, PF_INET, SOCK_STREAM, $protocol_id);
$iaddr = inet_aton($hostip);
$paddr = sockaddr_in($port, $iaddr);
print " Port $port offen.\n" if (connect(SOCKET, $paddr));
close(SOCKET);
}
}
FTP-Clients
Mit dem Modul Net::FTP von Graham Barr lassen sich Client-FTP-Methoden
in Perl-Programmen einfach realisieren. Alle Methoden geben, soweit es nicht anders
vermerkt ist, einen "wahren" Wert (ungleich Null) im Erfolgsfall zurück und "false"
(gleich Null) bei Fehlern. Bei Methoden, die einen Wert zurückgeben, wird im
Misserfolgsfall 'undef' oder eine leere Liste zurückgeben.
Beispiel: Automatisch Dateien holen
#!/bin/perl
use Net::FTP;
use strict;
my $host = 'localhost';
my $user = 'plate';
my $password = 'geheim';
my $file = '';
my $array_ref = '';
# Neues Net::FTP-Objekt
my $ftp = Net::FTP->new($host,
Timeout => 360,
Debug => 1
);
unless (defined $ftp)
{
print "$@\n";
die "Can't create Net::FTP-Object\n";
}
$ftp->login($user,$password) || die "Can't login $!";
print "Aktuelles Verzeichnis: ", $ftp->pwd() , "\n";
$array_ref = $ftp->ls();
foreach $file (@$array_ref)
{
# Transfermodus in Abhängigkeit von der Dateiendung setzen
if ($file =~ /(\.gif|\.jpg|\.tar|\.tar\.gz|\.tgz|\.zip)$/)
{ $ftp->type(I); }
else { $ftp->type(A); }
$ftp->get($file);
}
$ftp->quit();
Ping
Das Hilfsprogramm ping wird verwendet, um die Ereichbarkeit eines Rechners zu testen.
Net::Ping ist eine Perl-Variante des Programms ping.
Es hat zwar bei weitem nicht alle Features des Originals, läuft dafür aber
überall wo Perl läuft. Falls Router oder Firewalls icmp-Pakete ausfiltern oder
UDP- bzw. TCP-Echo abgeschaltet ist, meldet ping fälschlicherweise,
daß die Maschine unerreichbar ist.
Net::Ping kann mit drei Protokollen verwendet werden.
- UDP: Net::Ping schickt ein UDP-Packet an den echo-Port des
gewünschten Rechners. Falls das gesendete Datagramm mit dem zurückgeliefertem
übereinstimmt, gilt der Rechner als erreichbar.
- TCP: Net::Ping versucht eine TCP-Verbindung zum echo-Port des
gewünschten Rechners aufzubauen. Im Erfolgsfall gilt der Rechner als erreichbar.
- icmp: Net::Ping sendet eine icmp-Nachricht an den gewünschten Rechner.
Falls gesendete und empfangene Daten übereinstimmen gilt der Rechner als
erreichbar. Das Programm muß in diesem Fall unter der von root laufen.
- new([PROTO [,TIMEOUT [, BYTES]]])
Der Constructor für ein neues Net::Ping-Objekt. Alle Parameter sind
optional.
PROTO spezifiziert das zu verwendende Protokoll. Mögliche Werte sind:
tcp, udp (Voreinstellung) oder icmp.
Net::Ping verwendet intern die Funktion alarm(), wenn das
Protokoll tcpverwendet wird. Falls in Ihrem Programm
bereits ein Alarm gesetzt ist, wird dieser überschrieben.
TIMEOUT setzt die Timeoutzeitspanne in Sekunden. Voreinstellung: 5 Sekunden.
Dieser Wert wird für die Methode ping() verwendet.
BYTES spezifiziert die Anzahl Bytes, die für die ping-Anfrage verwendet
werden sollen. Dieser Wert wird ignoriert, wenn TCP verwendet wird. Voreinstellung:
UDP: 1, ICMP: 0.
- ping(HOST [, TIMEOUT])
pingt die entfernte Maschine HOST. Falls die Maschine erreichbar
ist, wird "wahr" zurückgegeben. Falls der Host nicht gefunden werden kann, wird
undef zurückgegeben. In der Praxis sollten Sie 'undef' und 'false' als identisch
betrachten. Beispiel:
if ( $p->ping($host) )
{ print "$host is reachable\n"; }
else { print "$host is unreachable\n"; }
- close()
Schließt die Netzwerkverbindung. Die Verbindung wird
ebenfalls geschlossen, wenn der Scope verlassen wird. Beispiel:
$p->close();
- pingecho(HOST [, TIMEOUT])
Veraltet. Existiert aus Kompatibilitätsgründen zu älteren Versionen.
Das folgende Unterprogramm pingt einen Host (IP-Nummer) mittels ICMP-Protokoll
an. Aufruf beispielsweise pinger(192.168.23.1):
sub pinger # (Host)
{
# Parameter: Host-IP-Nummer
my $host = shift; # zu pingender Host
my $retval = 0; # Ergebnis: 0 nicht erreicht, 1 erreicht, 2 Fehler
# Neues Net-Ping Objekt
my $p = Net::Ping->new('icmp');
unless (defined $p)
{ die "*** can't create Net::Ping object $!";}
# Exceptions auffangen
eval
{
$retval = 1 if ($p->ping($host));
if ($@)
{
print "*** Ping failed\n*** $@\n";
$retval = 2;
}
$p->close;
undef ($p);
sleep(1); # avoid network flooding
return $retval;
}
}
Das Unterprogramm kann verwendet werden, um alle Rechner eines C-Netzes
auf Erreichbarkeit zu testen:
#!/usr/bin/perl -w
use Net::Ping;
use strict;
my $network = '192.168.33';
print "Scanning Network $network.0 \n";
for ($count = 1; $count <= 254; $count++)
{
$pinghost = $network . "." . $count;
$ret = &pinger($pinghost);
if ($ret == 1)
{ print "$pinghost reached\n"; }
}
exit;
Das folgende Beispiel testet die Erreichbarkeit eines Rechners mit
den drei zur Verfügung stehenden Protokollen.
#!/usr/bin/perl -w
use Net::Ping; # Standardmodul
use strict;
my $host = '127.0.0.1';
# Protokoll TCP
my $p = Net::Ping->new('tcp');
unless (defined $p) { die "can't create Net::Ping object $!";}
if ($p->ping($host)) { print "$host reachable via TCP\n" ; }
else { print "$host unreachable via TCP\n"; }
$p->close;
# avoid network flooding
sleep(1);
# Protokoll UDP
$p = Net::Ping->new(); # UDP ist Voreinstellung
unless (defined $p) { die "can't create Net::Ping object $!";}
# Exceptions auffangen
eval
{
if ($p->ping($host)) { print "$host reachable via UDP\n"; }
else { print "$host unreachable via UDP\n"; }
};
if ($@) { print "$@: UDP failed\n"; }
undef $p;
# avoid network flooding
sleep(1);
if ($> == 0)
{
# Falls das Skript als 'root' (UID 0) läuft
# Protokoll 'icmp' verwenden
$p = Net::Ping->new('icmp');
unless (defined $p) { die "can't create Net::Ping object $!";}
if ($p->ping($host)) { print "$host reachable via icmp\n"; }
else { print "$host unreachable via icmp\n"; }
undef $p;
}
exit;
Webapplikationen
Die Programmierung von Webapplikationen setzt gute Kenntnisse des zugrundeliegenden
Protokolls voraus. Sie finden hier eine kurze Einführung in den Umgang mit
HTTP mit dem Modul LWP.
LWPbehandelt zur Durchführung von Interaktionen mindestens folgende
Variablen bzw. Objekte:
- UserAgent-Objekt
- URI
- Request-Objekt
- Response-Objekt
Beispiel:
# UserAgent
use LWP::UserAgent;
$ua = LWP::UserAgent->new();
# URI
$url = 'https://www.netzmafia.de/';
# Request
$Anfrage = HTTP::Request->new('GET', $url);
# Response
$Antwort = $ua->request($Anfrage);
Zur Erfolgskontrolle bzw. Fehlerbehandlung stehen die beiden Methoden
is_success() bzw. is_error() zur Verfügung, zum
Beispiel:
unless ($Antwort->is_success() )
{
print "Fehlernummer : ", $Antwort->code() , "\n";
print "Fehlermeldung: ", $Antwort->message(), "\n";
}
HTTP-Header
Häufig will man nur wissen, wie groß eine Datei ist, ob sie sich seit
dem letzten Zugriff verändert hat oder ob die URI noch exisitert.
HTTP stellt die Methode HEAD für derartige Anfragen
zur Verfügung. Hier einige Beispiele zum Zugriff auf die HTTP-Header mit
LWP-Methoden. Das erste Beispiel zeigt das Holen der HTTP-Header mit LWP::Simple:
use LWP::Simple;
$url = "https://www.netzmafia.de/index.html";
# Header ermitteln
($content_type, $document_length, $modified_time, $expires, $server) = head($url);
# Ergebnisse ausgeben
print "Content-type: ", $content_type, "\n";
print "Document-Length: ", $document_length, "\n";
print "Modified-Time: ", $modified_time, "\n";
print "Expires: ", $expires, "\n";
print "Server: ", $server, "\n";
Falls man nur wissen will, ob die URI noch existiert:
$exists = head($url);
if ($exists) { print "URI existiert\n"; }
else { print "\a\a\aKein Anschluss unter dieser URI.\n"; }
LWP::Simple
LWP::Simple stellt drei Funktionen zur Verfügung:
- get zum Holen eines Dokuments, z. B.:
use LWP::Simple;
$url = 'https://www.netzmafia.de/index.html'
$dokument = get($url);
unless (defined $dokument) { print "ERROR\n"; exit };
- getprint, um das ganze Dokument zu holen und den
Inhalt auszugeben:
use LWP::Simple;
$url = 'https://www.netzmafia.de/index.html'
getprint($url);
- getstore, um das ganze Dokument zu holen und in
einer Datei zu speichern:
use LWP::Simple;
$url = 'https://www.netzmafia.de/index.html'
$localfile = '/home/plate/tmp/index.html';
getstore($url, $localfile);
LWP::UserAgent
Erweiterter Zugriff auf HTTP-Header mit LWP::UserAgent
#!/usr/bin/perl -w
use LWP::UserAgent;
use strict;
my ($url, $ua, $request, $response);
$url = "https://www.netzmafia.de/index.html";
# User Agent
$ua = LWP::UserAgent->new();
# Anfrage mit Methode HEAD
$request = HTTP::Request->new('HEAD', $url);
# Antwort holen
$response = $ua->request($request);
if ($response->is_success())
{
# Header als ASCII-Text ausgeben
print $response->headers_as_string() , "\n"
}
else
{
# Fehlermeldung ausgeben
print $response->message() , "\n";
}
Falls man nur an bestimmten Feldern interessiert ist, kann man die
Methode header(), etwa zum Bestimmen der Grösse der Datei,
verwenden:
...
$size = $response->header('Content-Length');
print "URL: $url Grösse: $size Bytes\n";
...
Den HTTP-Status-Antwort-Header erhält man mit:
#!/usr/bin/perl -w
use LWP::UserAgent;
use strict;
my ($url, $ua, $request, $response);
$url = "https://192.168.33.2/index.html";
# User Agent
$ua = LWP::UserAgent->new();
$request = HTTP::Request->new('HEAD', $url);
$response = $ua->request($request);
print "HTTP-Status-Antwort-Header: ", $response->code , "\n";
Beispiel: Hat sich die URL seit gestern geändert?
Der Request benötigt die Zeit in Unix-Sekunden, daher ein paar Umrechnungsfaktoren:
- Tag: 86400 s
- Woche: 604800 s
- Monat (30,5 Tage): 2635200 s
- Jahr (365 Tage): 31536000 s
use HTTP::Status;
use HTTP::Date;
use LWP::UserAgent;
use strict;
my ($url, $ua, $request, $response);
$url = 'https://192.168.33.2/';
$request = HTTP::Request->new(HEAD, $url );
# Tag => 86400 s
# Woche (7 Tage) => 604800 s
# Monat (30.5 Tage) => 2635200 s
# Jahr (365 Tage) => 31536000 s
# Aktuelle Zeit in UnixSekunden - 1 Tag = gestern
$mtime = time - 86400;
# Request-Header setzen
$request->header('If-Modified-Since' => time2str($mtime));
# User Agent
$ua = LWP::UserAgent->new();
$response = $ua->request($req);
# 304 --> Keine Aenderung seit der angefragten Zeitspanne
if ( $response->code() == RC_NOT_MODIFIED)
{ print "$url wurde seit time2str($mtime) nicht geändert\n"; }
else { print "\aWake up. $url changed\n"; }
Die Methode response() von LWP::UserAgent bietet einen
komfortablen Zugriff auf Dokumente. Neben der verbesserten Möglichkeit zur
Erfolgskontrolle stehen drei Varianten zur Verfügung.
- response($request)
holt das angeforderte Dokument. Der Inhalt des Dokumentes
ist über die Methode content() erreichbar.
use LWP::UserAgent;
$ua = LWP::UserAgent->new();
$url = 'https://www.netzmafia.de/'
$request = HTTP::Request->new('GET', $url);
$response = $ua->request($request);
if ( $response->is_error() )
{
print "Fehlernummer : ", $response->code() , "\n";
print "Fehlermeldung: ", $response->message() , "\n";
}
else
{
print $response->content() , "\n";
}
- response($request, $file)
holt das ageforderte Dokument und speichert den Inhalt in der angegeben
lokalen Datei.
use LWP::UserAgent;
$file = 'local.html';
$ua = LWP::UserAgent->new();
$url = 'https://www.netzmafia.de/'
$request = HTTP::Request->new('GET', $url);
$response = $ua->request($request, $file);
if ( $response->is_error() )
{
print "Fehlernummer : ", $response->code() , "\n";
print "Fehlermeldung: ", $response->message() , "\n";
}
else
{
print $response->content() , "\n";
}
- response($request, \&callback, $Chunk_Size)
holt das angeforderte Dokument häppchenweise. Die Grösse der Happen wird mit
$Chunk_Size festgelegt. Nach dem Erhalt eines jeden Pakets wird
dieses Paket an eine Callbackroutine weitergereicht, die bereits während der
Übertragung die angekommenen Daten verarbeiten kann.
use LWP::UserAgent;
$file = 'local.html';
$ua = LWP::UserAgent->new();
$url = 'https://www.netzmafia.de/'
$request = HTTP::Request->new('GET', $url);
$Chunk_Size = 5 * 1024; # Wie gross soll der Happen sein
$response = $ua->request($request, \&Bearbeite, $Chunk_Size);
sub Bearbeite
{
$Bereits_erhaltene_Daten = shift;
...
++$x;
print "Happen Nummer $x\n\n";
print "$Bereits_erhaltene_Daten\n";
...
}
Diese Vorgehensweise ist beispielsweise sinnvoll bei
- langsamen Netzverbindungen
- umfangreichen Downloads (Datei muß nicht in den Hauptspeicher passen)
- Hintergrundverarbeitung des Inhalts
- Parsen des Dokuments schon während der Übertragung
- Extrahieren von Links
- Verarbeitung des Inhaltes in einem separatem Prozess
- Verarbeitung kontinuierlicher Datenströme
Zugriff auf WWW-Formulare mit GET
Die GET-Methode kann auch verwendet werden, um Daten an den Server (an ein
CGI-Skript) zu senden. An die URI wird ein "?" angehängt, gefolgt vom QueryString.
Ein Name-Wert-Paar wird durch ein '=' zusammengehalten, die Wertepaare werden
jeweils durch '&' getrennt.
use LWP::UserAgent;
$ua = LWP::UserAgent->new();
$url = https://www.netzmafia.de/cgi-bin/info.cgi?Category=Soft&Language=Perl
$request = HTTP::Request->new('GET', $url);
$response = $ua->request($request);
if ( $response->is_error() )
{
print "Fehlernummer : ", $response->code() , "\n";
print "Fehlermeldung: ", $response->message() , "\n";
}
else
{
print $response->content() , "\n";
}
Falls die Daten des Query-Strings Leerzeichen, Sonderzeichen oder ähnliche
kritische Zeichen enthalten, müssen diese entsprechend kodiert werden.
Das Modul URI::Escape stellt die dafür notwendigen Methoden zur
Verfügung:
use URI::Escape;
$querystring = 'Name=Jürgen Plate&Strasse=Gänsemarkt 5';
$safe_querystring = uri_escape($querystring);
print $safe_querystring , "\n";
Liefert Name=J%FCrgen%20Plate&Strasse=G%E4nsemarkt%205.
Nun lässt sich der Request vollständig angeben:
$querystring = 'Name=Jürgen Plate&Strasse=Gänsemarkt 5';
$safe_querystring = uri_escape($querystring);
$url = 'https://www.netzmafia.de/cgi-bin/info.cgi?';
$url .= "$safe_querystring";
...
$request = HTTP::Request->new('GET', $url);
...
|
|
|