11. Die Netzwerkfunktionen von Python
Wir befassen uns nun mit den Netzwerkfunktionen von Python, die es uns ermöglichen, TCP / IP (Transfer Control Protocol / Internet Protocol) zu programmieren.
![]() |
11.1. Den Namen oder die Adresse IP eines Rechners im Internet abrufen
import sys, socket
#------------------------------------------------
def getIPandName(nomMachine):
#nomMachine: Name des Rechners, dessen Adresse benötigt wird IP
# nomMachine --> Adresse IP
try:
ip=socket.gethostbyname(nomMachine)
print "ip[%s]=%s" % (nomMachine,ip)
except socket.error, erreur:
print "ip[%s]=%s" % (nomMachine,erreur)
return
# Adresse IP --> nomMachine
try:
name=socket.gethostbyaddr(ip)
print "name[%s]=%s" % (ip,name[0])
except socket.error, erreur:
print "name[%s]=%s" % (ip,erreur)
return
# ---------------------------------------- Hauptseite
# Konstanten
HOTES=["istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","localhost","","xx"]
# Adressen IP der Maschinen von HOTES
for i in range(len(HOTES)):
getIPandName(HOTES[i])
# Ende
sys.exit()
Hinweise:
- Zeile 1: Die Netzwerkfunktionen von Python sind im Modul socket gekapselt.
11.2. Ein Webclient
Ein Skript, mit dem der Inhalt des Index-Absatzes einer Website abgerufen werden kann.
import sys,socket
#-----------------------------------------------------------------------
def getIndex(site):
# liest die Seite URL und speichert sie in der Datei site.html
# zunächst kein Fehler
erreur=""
# Erstellung der Datei site.html
try:
html=open("%s.html" % (site),"w")
except IOError, erreur:
pass
# Fehler?
if erreur:
return "Erreur (%s) lors de la création du fichier %s.html" % (erreur,site)
# Verbindung zum Port 80 der Website hergestellt
try:
connexion=socket.create_connection((site,80))
except socket.error, erreur:
pass
# Rückkehr im Fehlerfall
if erreur:
return "Echec de la connexion au site (%s,80) : %s" % (site,erreur)
# Die Verbindung stellt einen bidirektionalen Kommunikationsfluss dar
# zwischen dem Client (diesem Programm) und dem angerufenen Webserver
# Dieser Kanal wird für den Austausch von Befehlen und Informationen genutzt
# Das Dialogprotokoll lautet HTTP
# Der Client sendet den Befehl „get“, um die URL anzufordern /
# „get“-Syntax: URL HTTP/1.0
# Die Header des HTTP-Protokolls müssen mit einer Leerzeile enden
connexion.send("GET / HTTP/1.0\n\n")
# Der Server antwortet nun auf dem Verbindungskanal. Er sendet alle
# seine Daten senden und anschließend den Kanal schließen. Der Client liest alles, was über den Verbindungskanal
# bis zum Schließen des Kanals
ligne=connexion.recv(1000)
while(ligne):
html.write(ligne)
ligne=connexion.recv(1000)
# schließlich schließt der Client seinerseits die Verbindung
connexion.close()
# Schließen der HTML-Datei
html.close()
# Zurück
return "Transfert reussi du paragraphe index du site %s" % (site)
# --------------------------------------------- main
# Text HTML von URL abrufen
# Liste der Websites
SITES=("istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","xx")
# Lesen der Indexseiten der Websites aus der Tabelle SITES
for i in range(len(SITES)):
# Auslesen der Indexseite der Website SITES[i]
resultat=getIndex(SITES[i])
# Anzeige des Ergebnisses
print resultat
# Ende
sys.exit()
Hinweise:
- Zeile 57: Die Liste der URLs der Websites, deren Startseite abgerufen werden soll. Diese ist in der Textdatei [nomsite.html] gespeichert;
- Zeile 62: Die Funktion getIndex führt die Aufgabe aus;
- Zeile 4: die Funktion getIndex;
- Zeile 20: Die Methode create_connection((site,port)) ermöglicht es, eine Verbindung zu einem Dienst TCP / IP herzustellen, der auf dem Port port des Rechners „site“ läuft;
- Zeile 35: Mit der Methode send können Daten über eine Verbindung TCP / IP gesendet werden. Hier wird Text gesendet. Dieser Text folgt dem Protokoll HTTP (HyperText Transfer Protocol);
- Zeile 40: Mit der Methode recv können Daten über eine Verbindung TCP / IP empfangen werden. Hier wird die Antwort des Webservers in Blöcken von jeweils 1000 Zeichen gelesen und in der Textdatei [nomsite.html] gespeichert.
Transfert reussi du paragraphe index du site istia.univ-angers.fr
Transfert reussi du paragraphe index du site www.univ-angers.fr
Transfert reussi du paragraphe index du site www.ibm.com
Echec de la connexion au site (xx,80) : [Errno 11001] getaddrinfo failed
Die für die Website [www.ibm.com] empfangene Datei:
- Die Zeilen 1–11 sind die HTTP-Header der Serverantwort;
- Zeile 1: Der Server fordert den Client auf, sich zu der in Zeile 8 angegebenen URL-Adresse weiterzuleiten;
- Zeile 2: Datum und Uhrzeit der Antwort;
- Zeile 3: Kennung des Webservers;
- Zeile 4: Vom Server gesendeter Inhalt. Hier eine Seite mit der ID HTML, die in Zeile 13 beginnt;
- Zeile 12: die leere Zeile, die die Header HTTP abschließt;
- Zeilen 13–19: die vom Webserver gesendete Seite HTML.
11.3. Ein SMTP-Client
Unter den Protokollen TCP / IP ist SMTP (SendMail Transfer Protocol) das Kommunikationsprotokoll des Nachrichtendienstes.
# -*- coding=utf-8 -*-
import sys,socket
#-----------------------------------------------------------------------
def getInfos(fichier):
# gibt die Informationen (SMTP, Absender, Empfänger, Nachricht) aus der Textdatei [fichier] aus
# Zeile 1: SMTP, Absender, Empfänger
# folgende Zeilen: der Text der Nachricht
# Öffnen von [fichier]
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# Lesen der ersten Zeile
ligne=infos.readline()
# Entfernen des Zeilenendezeichens
ligne=cutNewLineChar(ligne)
# Abruf der Felder smtp, Absender, Empfänger
champs=ligne.split(",")
# Ist die Anzahl der Felder korrekt?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# „Verarbeitung“ der abgerufenen Informationen
# Aus jedem der drei Felder werden die „Leerzeichen“ vor oder nach den relevanten Informationen entfernt
for i in range(3):
champs[i]=champs[i].strip()
# Abruf der Felder
(smtpServer,expediteur,destinataire)=champs
message=""
# Lesen des restlichen Textes
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# Rückgabe
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# Sendet die Nachricht im Namen des Absenders an den SMTP-Server „smtpserver“
# an den Empfänger. Wenn „verbose=True“, wird der Client-Server-Austausch protokolliert
# Der Name des Clients wird abgerufen
try:
client=socket.gethostbyaddr(socket.gethostbyname("localhost"))[0]
except socket.error, erreur:
return "Erreur IP / Nom du client : %s" % (erreur)
# Aufbau einer Verbindung auf Port 25 von smtpServer
try:
connexion=socket.create_connection((smtpServer,25))
except socket.error, erreur:
return "Echec de la connexion au site (%s,25) : %s" % (smtpServer,erreur)
# Die Verbindung stellt einen bidirektionalen Kommunikationsfluss dar
# zwischen dem Client (diesem Programm) und dem kontaktierten SMTP-Server
# Dieser Kanal wird für den Austausch von Befehlen und Informationen genutzt
# Nach dem Verbindungsaufbau sendet der Server eine Begrüßungsnachricht, die gelesen wird
erreur=sendCommand(connexion,"",verbose,1)
if(erreur) :
connexion.close()
return erreur
# Befehl „ehlo“:
erreur=sendCommand(connexion,"EHLO %s" % (client),verbose,1)
if erreur :
connexion.close()
return erreur
# Befehl „mail from“:
erreur=sendCommand(connexion,"MAIL FROM: <%s>" % (expediteur),verbose,1)
if erreur :
connexion.close()
return erreur
# Befehl „rcpt to“:
erreur=sendCommand(connexion,"RCPT TO: <%s>" % (destinataire),verbose,1)
if erreur :
connexion.close()
return erreur
# Befehl „data“
erreur=sendCommand(connexion,"DATA",verbose,1)
if erreur :
connexion.close()
return erreur
# Vorbereitung der zu versendenden Nachricht
# sie muss folgende Zeilen enthalten
# Von: Absender
# An: Empfänger
# Leere Zeile
# Nachricht
# .
data="From: %s\r\nTo: %s\r\n%s\r\n.\r\n" % (expediteur,destinataire,message)
# Nachricht senden
erreur=sendCommand(connexion,data,verbose,0)
if erreur :
connexion.close()
return erreur
# Befehl „quit“
erreur=sendCommand(connexion,"QUIT",verbose,1)
if erreur :
connexion.close()
return erreur
# Ende
connexion.close()
return "Message envoye"
# --------------------------------------------------------------------------
def sendCommand(connexion,commande,verbose,withRCLF):
# Befehl im Verbindungskanal senden
# Ausführlicher Modus, wenn verbose=1
# Wenn withRCLF=1, fügt die Sequenz RCLF zum Befehl hinzu
# Daten
RCLF="\r\n" if withRCLF else ""
# Befehl senden, wenn Befehl nicht leer ist
if commande:
connexion.send("%s%s" % (commande,RCLF))
# eventuelles Echo
if verbose:
affiche(commande,1)
# Antwort mit weniger als 1000 Zeichen lesen
reponse=connexion.recv(1000)
# eventuelles Echo
if verbose:
affiche(reponse,2)
# Fehlercode abrufen
codeErreur=reponse[0:3]
# Vom Server zurückgemeldeter Fehler?
if int(codeErreur) >=500:
return reponse[4:]
# Rückgabe ohne Fehler
return ""
# --------------------------------------------------------------------------
def affiche(echange,sens):
# Austausch auf dem Bildschirm anzeigen?
# wenn sens=1, dann -->Austausch anzeigen
# wenn „sens“ = 2, wird <-- Austausch ohne die letzten beiden Zeichen angezeigt RCLF
if sens==1:
print "--> [%s]" % (echange)
return
elif sens==2:
l=len(echange)
print "<-- [%s]" % echange[0:l-2]
return
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# Das Zeilenendezeichen von „[ligne]“ wird entfernt, falls vorhanden
l=len(ligne)
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l])
# main ----------------------------------------------------------------
# Client SMTP (SendMail Transfer Protocol) zum Senden einer Nachricht
# Die Informationen werden aus einer Datei INFOS entnommen, die folgende Zeilen enthält
# Zeile 1: SMTP, Absender, Empfänger
# folgende Zeilen: der Text der Nachricht
# Absender: E-Mail-Adresse des Absenders
# Empfänger: E-Mail-Adresse des Empfängers
# SMTP: Name des zu verwendenden SMTP-Servers
# Kommunikationsprotokoll SMTP Client-Server
# -> Der Client verbindet sich über Port 25 mit dem SMTP-Server
# <- Der Server sendet ihm eine Begrüßungsnachricht
# -> Der Client sendet den Befehl EHLO: Name seines Rechners
# <- Der Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „mail from: <Absender>“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „rcpt to: <Empfänger>“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „data“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet alle Zeilen seiner Nachricht und schließt mit einer Zeile ab, die nur das Zeichen enthält.
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „quit“
# <- Der Server antwortet mit „OK“ oder nicht
# Die Antworten des Servers haben die Form „xxx Text“, wobei „xxx“ eine dreistellige Zahl ist. Jede Zahl „xxx“ >= 500
# deutet auf einen Fehler hin. Die Antwort kann mehrere Zeilen umfassen, die alle mit „xxx-“ beginnen – mit Ausnahme der letzten
# in der Form xxx (Leerzeichen)
# Die ausgetauschten Textzeilen müssen mit den Zeichen RC(#13) und LF(#10) enden
# # die Parameter für den E-Mail-Versand
MAIL="mail2.txt"
# Die E-Mail-Parameter werden abgerufen
res=getInfos(MAIL)
# Fehler?
if res[0]:
print "%s" % (erreur)
sys.exit()
# Versenden der E-Mail im ausführlichen Modus
(smtpServer,expediteur,destinataire,message)=res[1:]
print "Envoi du message [%s,%s,%s]" % (smtpServer,expediteur,destinataire)
resultat=sendmail(smtpServer,expediteur,destinataire,message,True)
print "Resultat de l'envoi : %s" % (resultat)
# Ende
sys.exit()
Hinweise:
- Auf einem Windows-Rechner mit Antivirenprogramm verhindert dieses möglicherweise, dass das Python-Skript eine Verbindung zum Port 25 eines SMTP-Servers herstellt. In diesem Fall muss das Antivirenprogramm deaktiviert werden. Für McAfee kann man beispielsweise wie folgt vorgehen:
![]() |
- bei [1] die Konsole VirusScan aktivieren
- bei [2] wird der Dienst [Protection lors de l'accès] beendet
- bei [3] ist er gestoppt
Die Datei infos.txt:
smtp.univ-angers.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr
Subject: test
ligne1
ligne2
ligne3
Die Bildschirmausgabe:
Die vom E-Mail-Client Thunderbird gelesene Nachricht:
![]() |
11.4. Ein zweiter SMTP-Client
Dieses zweite Skript führt dieselben Schritte aus wie das vorherige, nutzt dabei jedoch die Funktionen des Moduls [smtplib].
# -*- coding=utf-8 -*-
import sys,socket, smtplib
#-----------------------------------------------------------------------
def getInfos(fichier):
# gibt die Informationen (SMTP, Absender, Empfänger, Nachricht) aus der Textdatei [fichier] zurück
# Zeile 1: SMTP, Absender, Empfänger
# folgende Zeilen: der Text der Nachricht
# Öffnen von [fichier]
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# Lesen der ersten Zeile
ligne=infos.readline()
# Entfernen des Zeilenendezeichens
ligne=cutNewLineChar(ligne)
# Abruf der Felder smtp, Absender, Empfänger
champs=ligne.split(",")
# Ist die Anzahl der Felder korrekt?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# „Aufbereitung“ der abgerufenen Informationen – vorangestellte oder nachfolgende „Leerzeichen“ werden entfernt
for i in range(3):
champs[i]=champs[i].strip()
# Abruf der Felder
(smtpServer,expediteur,destinataire)=champs
# Lesen der zu sendenden Nachricht
message=""
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# Rückgabe
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# Sendet die Nachricht im Namen des Absenders an den SMTP-Server „smtpserver“
# an den Empfänger. Wenn verbose=True, wird der Client-Server-Austausch protokolliert
# Es wird die Bibliothek „smtplib“ verwendet
try:
server = smtplib.SMTP(smtpServer)
if verbose:
server.set_debuglevel(1)
server.sendmail(expediteur, destinataire, message)
server.quit()
except Exception, erreur:
return "Erreur envoi du message : %s" % (erreur)
# Ende
return "Message envoye"
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# Das Zeilenendezeichen von [ligne] wird entfernt, falls vorhanden
l=len(ligne)
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l])
# Hauptprogramm ----------------------------------------------------------------
# Client SMTP (SendMail-Übertragungsprotokoll) zum Versenden einer Nachricht
# Die Informationen werden aus einer Datei INFOS entnommen, die folgende Zeilen enthält
# Zeile 1: SMTP, Absender, Empfänger
# folgende Zeilen: der Text der Nachricht
# Absender: E-Mail-Adresse des Absenders
# Empfänger: E-Mail-Adresse des Empfängers
# SMTP: Name des zu verwendenden SMTP-Servers
# Kommunikationsprotokoll SMTP Client-Server
# -> Der Client verbindet sich über Port 25 mit dem SMTP-Server
# <- Der Server sendet ihm eine Willkommensnachricht
# -> Der Client sendet den Befehl EHLO: Name seines Rechners
# <- Der Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „mail from: <Absender>“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „rcpt to: <Empfänger>“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „data“
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet alle Zeilen seiner Nachricht und schließt mit einer Zeile ab, die nur das Zeichen enthält.
# <- Server antwortet mit OK oder nicht
# -> Der Client sendet den Befehl „quit“
# <- Der Server antwortet mit „OK“ oder nicht
# Die Antworten des Servers haben die Form „xxx Text“, wobei „xxx“ eine dreistellige Zahl ist. Jede Zahl „xxx“ >= 500
# deutet auf einen Fehler hin. Die Antwort kann mehrere Zeilen umfassen, die alle mit „xxx-“ beginnen – mit Ausnahme der letzten
# in der Form xxx (Leerzeichen)
# Die ausgetauschten Textzeilen müssen mit den Zeichen RC(#13) und LF(#10) enden
# # die Parameter für den E-Mail-Versand
MAIL="mail2.txt"
# Die E-Mail-Parameter werden abgerufen
res=getInfos(MAIL)
# Fehler?
if res[0]:
print "%s" % (erreur)
sys.exit()
# Versenden der E-Mail im ausführlichen Modus
(smtpServer,expediteur,destinataire,message)=res[1:]
print "Envoi du message [%s,%s,%s]" % (smtpServer,expediteur,destinataire)
resultat=sendmail(smtpServer,expediteur,destinataire,message,True)
print "Resultat de l'envoi : %s" % (resultat)
# Ende
sys.exit()
Hinweise:
- Dieses Skript ist bis auf die Funktion sendmail identisch mit dem vorherigen. Diese Funktion nutzt nun die Funktionen des Moduls [smtplib] (Zeile 3).
Die Datei mail2.txt:
smtp.univ-angers.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr
From: serge.tahe@univ-angers.fr
To: serge.tahe@univ-angers.fr
Subject: test
ligne1
ligne2
ligne3
Die Bildschirmausgabe:
11.5. Echo-Client/Server
Es wird ein Echo-Dienst erstellt. Der Server gibt alle Textzeilen, die ihm der Client sendet, in Großbuchstaben zurück. Der Dienst kann dank der Verwendung von Threads mehrere Clients gleichzeitig bedienen.
# -*- coding=utf-8 -*-
# Generischer TCP-Server unter Windows
# Laden der Header-Dateien
import re,sys,SocketServer,threading
# Multithread-TCP-Server
class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
# aktueller Thread
cur_thread = threading.currentThread()
# Client-Daten
self.data="on"
# Abbruch bei leerer Zeichenkette
while self.data:
# Die Textzeilen des Clients werden mit der Methode „readline“ gelesen
self.data =self.rfile.readline().strip()
# Konsolenausgabe
print "client %s : %s (%s)" % (self.client_address[0],self.data,cur_thread.getName())
# Antwort an den Client senden
response = "%s: %s" % (cur_thread.getName(), self.data.upper())
# self.wfile ist der Schreibstrom zum Client
self.wfile.write(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
# ------------------------------------------------------ main
# Aufrufsyntax: argv[0] Port
# Der Server wird auf dem genannten Port gestartet
# Daten
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- Der Aufruf wird überprüft
# Es muss genau ein Argument vorhanden sein
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# Der Port muss eine Zahl sein
port=sys.argv[1]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s n'est pas un nombre entier positif\n" % (port)
sys.exit(2)
# Start des Servers – bedient jeden Client in einem eigenen Thread
host="localhost"
server = ThreadedTCPServer((host,int(port)), ThreadedTCPRequestHandler)
# Der Server wird in einem Thread gestartet
# Jeder Client wird in einem zusätzlichen Thread bedient
server_thread = threading.Thread(target=server.serve_forever)
# Startet den Server – Endlosschleife zum Warten auf Clients
server_thread.start()
# Weiterverfolgung
print "Serveur d'echo a l'ecoute sur le port % s" % (port)
Hinweise:
- Zeile 39: sys.argv steht für die Skriptparameter. Diese müssen hier die Form nom_du_script Port haben. Es müssen also zwei sein. sys.argv[0] wird dann zu nom_du_script und sys.argv[1] wird zu port ;
- Zeile 53: Der Echo-Server ist eine Instanz der Klasse ThreadedTcpServer. Der Konstruktor dieser Klasse erwartet zwei Parameter:
- Parameter 1: ein Tupel mit zwei Elementen (host, port), das den Rechner und den Listening-Port des Servers festlegt;
- Parameter 2: der Name der Klasse, die für die Bearbeitung der Anfragen eines Clients zuständig ist.
- Zeile 57: Ein Thread wird erstellt (aber noch nicht gestartet). Dieser Thread führt die Methode [serve_forever] des Servers TCP aus. Diese Methode ist eine Schleife zum Abhören von Client-Verbindungen. Sobald eine Client-Verbindung erkannt wird, wird eine Instanz der Klasse ThreadedTCPRequestHandler erstellt. Ihre Methode handle ist für den Dialog mit dem Client zuständig;
- Zeile 59: Der Thread des Echo-Dienstes wird gestartet. Ab diesem Zeitpunkt können sich Clients mit dem Dienst verbinden;
- Zeile 27: Die Klasse des Echo-Servers. Sie leitet sich von zwei Klassen ab: SocketServer.ThreadingMixIn und SocketServer.TCPServer. Damit handelt es sich um einen multithreaded TCP-Server: Der Server läuft in einem Thread, und jeder Client wird in einem zusätzlichen Thread bedient;
- Zeile 9: Die Klasse, die die Anfragen der Clients verarbeitet. Sie leitet sich von der Klasse SocketServer.StreamRequestHandler ab. Damit erbt sie zwei Attribute:
- rfile: Dies ist der Lesestrom für die vom Client gesendeten Daten – kann wie eine Textdatei behandelt werden;
- wfile: Dies ist der Schreibstrom, über den Daten an den Kunden gesendet werden können – er kann wie eine Textdatei behandelt werden.
- Zeile 11: Die Methode handle verarbeitet die Anfragen der Clients;
- Zeile 13: Der Thread, der diese Methode ausführt;
- Zeile 17: Schleife zur Verarbeitung der Client-Anfragen. Die Schleife endet, wenn der Client eine leere Zeile sendet;
- Zeile 19: Einlesen der Client-Anfrage;
- Zeile 21: self.client_address[0] steht für die Adresse IP des Kunden. cur_thread.getName() ist der Name des Threads, der die Methode „handle“ ausführt;
- Zeile 23: Die Antwort an den Kunden besteht aus zwei Komponenten – dem Namen des Threads, der den Kunden bedient, und dem von ihm gesendeten Befehl, der in Großbuchstaben übergeben wird.
# -*- coding=utf-8 -*-
import re,sys,socket
# ------------------------------------------------------ main
# generischer TCP-Client
# Aufrufsyntax: argv[0] Host Port
# Der Client stellt eine Verbindung zum Echo-Dienst her (Host, Port)
# Der Server gibt die vom Client eingegebenen Zeilen zurück
# Syntax
syntaxe="syntaxe : %s hote port" % (sys.argv[0])
#------------------------------------- Der Aufruf wird überprüft
# Es müssen zwei Argumente vorhanden sein
nbArguments=len(sys.argv)
if nbArguments!=3:
print syntaxe
sys.exit(1)
# Die Argumente werden abgerufen
hote=sys.argv[1]
# Der Port muss eine Zahl sein
port=sys.argv[2]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s foit être un nombre entier positif" % (port)
sys.exit(2)
try:
# Verbindung vom Client zum Server
connexion=socket.create_connection((hote,int(port)))
except socket.error, erreur:
print "Echec de la connexion au site (%s,%s) : %s" % (hote,port,erreur)
sys.exit(3)
try:
# Eingabeschleife
ligne=raw_input("Commande (rien pour arreter): ").strip()
while ligne!="":
# Die Zeile wird an den Server gesendet
connexion.send("%s\n" % (ligne))
# Warten auf die Antwort
reponse=connexion.recv(1000)
print "<-- %s" % (reponse)
ligne=raw_input("Commande (rien pour arreter): ")
except socket.error, erreur:
print "Echec de la connexion au site (%s,%s) : %s" % (hote,port,erreur)
sys.exit(3)
finally:
# Die Verbindung wird beendet
connexion.close()
Der Server wird in einem ersten Befehlsfenster gestartet:
Ein erster Client wird in einem zweiten Fenster gestartet:
Der Client erhält als Antwort auf den Befehl, den er an den Server sendet, genau denselben Befehl in Großbuchstaben. Ein zweiter Client wird in einem dritten Fenster gestartet:
Die Serverkonsole sieht dann wie folgt aus:
Die Clients werden tatsächlich in verschiedenen Threads bedient. Um einen Client zu beenden, genügt es, einen leeren Befehl einzugeben.
11.6. Generischer TCP-Server
Wir wollen ein Python-Skript schreiben, das
- als TCP-Server fungiert, der jeweils einen Client bedienen kann,
- vom Client gesendete Textzeilen akzeptiert,
- Textzeilen von der Tastatur entgegennimmt und diese als Antwort an den Client sendet.
Somit fungiert der Benutzer an der Tastatur als Server:
- Er sieht auf seiner Konsole die vom Client gesendeten Textzeilen;
- er antwortet diesem, indem er die Antwort über die Tastatur eingibt.
So kann er sich an jede Art von Client anpassen. Aus diesem Grund wird er als „generischer TCP-Server“ bezeichnet. Er ist ein praktisches Werkzeug, um TCP-Kommunikationsprotokolle zu erkunden. Im folgenden Beispiel ist der TCP-Client ein Webbrowser, wodurch wir das von Web-Clients verwendete HTTP-Protokoll erkunden können.
![]() |
# -*- coding=utf-8 -*-
# generischer TCP-Server
# Laden der Header-Dateien
import re,sys,SocketServer,threading
# generischer TCP-Server
class MyTCPHandler(SocketServer.StreamRequestHandler):
def handle(self):
# Der Client wird angezeigt
print "client %s" % (self.client_address[0])
# Es wird ein Thread zum Lesen der Befehle des Clients erstellt
thread_lecture = threading.Thread(target=self.lecture)
thread_lecture.start()
# Beenden bei Befehl „bye“
# Befehl von der Tastatur lesen
cmde=raw_input("--> ")
while cmde!="bye":
# Befehl wird an den Client gesendet. self.wfile ist der Schreibstrom zum Client
self.wfile.write("%s\n" % (cmde))
# Lesen des nächsten Befehls
cmde=raw_input("--> ")
def lecture(self):
# Es werden alle vom Client gesendeten Zeilen angezeigt, bis der Befehl „bye“ empfangen wird
ligne=""
while ligne!="bye":
# Die Textzeilen des Kunden werden mit der Methode „readline“ gelesen
ligne = self.rfile.readline().strip()
# Konsolenausgabe
print "<--- %s : %s" % (self.client_address[0], ligne)
# ------------------------------------------------------ main
# Aufrufsyntax: argv[0] Port
# Der Server wird auf dem angegebenen Port gestartet
# Daten
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- Der Aufruf wird überprüft
# Es muss genau ein Argument vorhanden sein
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# Der Port muss eine Zahl sein
port=sys.argv[1]
modele=r"^\s*\d+\s*$"
if not re.match(modele,port):
print "Le port %s n'est pas un nombre entier positif\n" % (port)
sys.exit(2)
# Start des Servers
host="localhost"
server = SocketServer.TCPServer((host, int(port)), MyTCPHandler)
print "Service tcp generique lance sur le port %s. Arret par Ctrl-C" % (port)
server.serve_forever()
Anmerkungen:
- Zeile 57: Der TCP-Server ist eine Instanz der Klasse SocketServer.TCPServer. Der Konstruktor dieser Klasse akzeptiert zwei Parameter:
- Der erste Parameter ist ein Tupel mit zwei Elementen (Host, Port), wobei host der Rechner ist, auf dem der Dienst läuft (in der Regel localhost), und port der Port, auf dem der Dienst auf Anfragen von Clients wartet (lauscht);
- der zweite Parameter gibt die Dienstklasse eines Clients an. Wenn sich ein Client verbindet, wird eine Instanz der Dienstklasse erstellt, und deren Methode handle muss die Verbindung zum Client verwalten.
Der TCP-Server SocketServer.TCPServer ist nicht multithreaded. Er bedient jeweils nur einen Client;
- Zeile 59: Die Methode serve_forever des TCP-Servers wird ausgeführt. Es handelt sich um eine Endlosschleife, in der auf Clients gewartet wird;
- Zeile 9: Der TCP-Server ist hier eine von der Klasse SocketServer.StreamRequestHandler abgeleitete Klasse. Dadurch können die mit dem Client ausgetauschten Datenströme als Textdateien betrachtet werden. Diese Klasse ist uns bereits begegnet. Uns stehen folgende Methoden zur Verfügung:
- readline zum Lesen einer Textzeile vom Client;
- write, um Textzeilen an den Kunden zurückzusenden.
- Zeile 12: self.client_address[0] ist die IP-Adresse des Kunden;
- Zeile 14: Der TCP-Server kommuniziert mit dem Client mithilfe von zwei Threads
- ein Thread zum Lesen der Zeilen vom Client;
- ein Thread zum Schreiben von Zeilen an den Client.
- Zeile 14: Der Thread zum Lesen der Zeilen vom Client wird erstellt. Sein Parameter target legt die vom Thread ausgeführte Methode fest. Es handelt sich um die in Zeile 25 definierte Methode;
- Zeile 25: Der Lese-Thread wird gestartet;
- Zeilen 25–32: Die vom Lese-Thread ausgeführte Methode;
- Zeile 28: Der Lesethread liest alle vom Client gesendeten Textzeilen, bis die Zeile „bye“ empfangen wird;
- Zeile 18: Wir befinden uns hier im Schreib-Thread für den Client. Das Prinzip besteht darin, alle vom Benutzer über die Tastatur eingegebenen Textzeilen an den Client zu senden.
Der Server
Der Client-Browser
Die vom Server empfangene Anfrage
Die vom Benutzer über die Tastatur eingegebene Antwort an den Server (ohne das Zeichen -->)
- Zeilen 1–5: An den Client gesendete HTTP-Antwort;
- Zeile 6: die an den Client gesendete HTML-Seite;
- Zeile 7: Ende des Dialogs mit dem Client. Der Kundenservice wird beendet und die Verbindung geschlossen, wodurch der Thread zum Lesen der vom Client gesendeten Textzeilen abrupt unterbrochen wird;
- Die Zeilen 1–5 der an den Client gesendeten HTTP-Antwort haben folgende Bedeutung:
- Zeile 1: Die vom Client angeforderte Ressource wurde gefunden;
- Zeile 2: Identifikation des Servers;
- Zeile 3: Der Server wird die Verbindung nach dem Senden der Ressource schließen;
- Zeile 4: Art der vom Server gesendeten Ressource: ein HTML-Dokument;
- Zeile 5: eine leere Zeile.
Die vom Browser angezeigte Seite [1]:
![]() |
Wenn man den vom Browser [2] empfangenen Quellcode anzeigt, stellt man fest, dass der vom Webbrowser empfangene Code HTML tatsächlich derjenige ist, der an ihn gesendet wurde.




