|
Erste Perl-Server und ClientsDie in diesem und den folgenden Kapiteln einfachen Beispielserver wird auch wiederdie Fehlerbehandlung nur eingeschräkt verwendet. Das ist beim Erweiternder Programme für eigene Anwendungen zu berücksichtigen.Mit den Perl-Modul IO::Socket::INET kann man relativ einfach Socketverbindungen programmieren.Zum Einbinden reicht der Perl-Befehl use IO::Socket. Die Generierung eines konkreten Sockets geschieht über den Konstruktor vonIO::Socket::INET, dessen Argumente darüber entscheiden, ob es ein Server- oder ein Client-Socket wird. Beispiel für das Einrichten eines Client-seitigen Socket:
my $socket = IO::Socket::INET -> new(PeerAddr => $remote_host, PeerPort => $remote_port, Proto=> "tcp", Type => SOCK_STREAM)or die "Couldn't connect to $remote_host:$remote_port: $@\n";Beispiel fürdas Einrichten eines Server-seitigen Socket:
$server = IO::Socket::INET -> new(LocalPort => $server_port,Type=> SOCK_STREAM,Reuse => 1,Listen=> SOMAXCONN) or die "Couldn't be a tcp server on port $server_port : $@\n";
Beim Server-Socket braucht man keine Rechneradresse angeben, das ist ja
automatisch die Adresse des Rechners, auf der das Socket läuft; nur
einen Port muß man festlegen.
Wird der Server-Socket nicht ordnungsgemäß geschlossen, so kann
normalerweise derselbe Port nicht sofort wieder benutzt, sondern auf einen
Timeout gewartet werden. Dies kann man allerdings durch Setzen des Parameters
Reuse umgehen, was insbesondere für die Phase der Programmentwicklung
nützlich ist, wo man den Server öfter mal abbricht.
'Listen' gibt an, bis zu wie viele Anfragen in eine Warteschleife
aufgenommen werden sollen. Ist diese Warteschleife voll, so wird die Anfrage
nicht bearbeitet und der Client erhält eine entsprechende Meldung.
SOMAXCONN ist eine Systemkonstante die angibt, wie viele Anfragen
das System maximal in der Warteschleife zulässt.
Auf der Client-Seite ist das Öffnen des Socket praktisch schon alles. Man
kann $socket jetzt wie ein Filehandle benutzen, d.h. hineinschreiben,
auslesen und es schließen, mit denselben Befehlen, die man auch für
Filehandles benutzt. Hier ein Beispiel für ein Client-Programm, das einen
Socket einrichtet, in eine Meldung hineinschreibt, die Antwort ausliest
und den Socket wieder schließt:
#!/usr/bin/perl -w
use IO::Socket;
use strict;
my $remote_host = "atlas.ee.fhm.edu";
my $remote_port = 2000;
my $socket = IO::Socket::INET->new(PeerAddr => $remote_host,
PeerPort => $remote_port,
Proto => "tcp",
Type => SOCK_STREAM)
or die "Couldn't connect to $remote_host:$remote_port: $@\n";
print $socket "Hallo\n";
my $answer = <$socket>;
print "Receiving: $answer\n";
close($socket);
Der Server wartet gewöhnlich in einer Endlos-Schleife auf hereinkommende
Anfragen. Die Methode dafür ist accept. Solange
keine Anfrage erfolgt, bleibt das Programm beim 'accept'-Aufruf stehen.
Kommt dann eine Anfrage, gibt 'accept' die neue Verbindung zum anfragenden
Client zurück und die Anfrage kann bearbeitet werden. Hier ein Beispiel:
#!/usr/bin/perl -w
use IO::Socket;
use strict;
my $server_port = 2000;
my $server = IO::Socket::INET->new(LocalPort => $server_port,
Type => SOCK_STREAM,
Reuse => 1,
Listen => SOMAXCONN )
or die "Couldn't be a tcp server on port $server_port : $@\n";
while (my $client = $server->accept())
{
# $client is the new connection
my $request = <$client>;
chomp($request);
print "Request: $request\n";
print $client "Selber $request\n";
close($client);
print "\nWaiting for the next connection ...\n\n";
}
close($server);
Beide Programme verlassen sich darauf, daß die zu erwartende Nachricht nur
aus einer Zeile besteht. Sonst müsste man wie bei einem Filehandle auch
eine Schleife über <client> laufen lassen.
Gepufferte Ein- und Ausgabe
Bevor Daten an ein Filehandle geschrieben werden, puffert sie das Betriebssystem.
Es wird also gewartet, bis eine bestimmte Menge Daten zusammengekommen ist, und erst
dann werden die Daten tatsächlich übertragen. Bei Sockets ist
dies gewöhnlich so lange kein Problem, wie Zeilen übertragen
werden. Sollen Zeichen oder Zeichenketten ohne abschließendes Newline
übertragen werden, muß man dafür sorgen, daß die Puffer
geleert werden. Dies kann man über Einstellen der Perl-Variablen $|
erreichen. Ist diese Systemvariable auf 1 gesetzt, dann wird der Bufferinhalt nach
jedem Ausgabe-Befehl losgeschickt. Für die Standardausgabe genügt also die
Zeile
$| = 1;
Um andere Handles genauso zu behandeln, bedient man sich eines Tricks. Man wechselt
das Handle und STDOUT, so daß die Zuweisung auf das Datei- oder Socket-Handle
wirkt. Danach wird wieder die ursprüngliche Zuordnung hergestellt. Dazu wird
das Filehandle von STDOUT zwischengespeichert. Den Wechsel erreicht man mit
select():
my $old_fh = select(SOCK); # Ungepufferte Ausgabe
$| = 1; # fuer SOCK einstellen
select($old_fh);
|
|
|