11. Le funzioni di rete di Python
Passiamo ora alle funzioni di rete di Python che ci consentono di programmare in TCP / IP (Transfer Control Protocol / Internet Protocol).
![]() |
11.1. Ottenere il nome o l'indirizzo IP di un computer su Internet
import sys, socket
#------------------------------------------------
def getIPandName(nomMachine):
#nomMachine: nome del computer di cui si desidera l'indirizzo IP
# nomMachine-->indirizzo IP
try:
ip=socket.gethostbyname(nomMachine)
print "ip[%s]=%s" % (nomMachine,ip)
except socket.error, erreur:
print "ip[%s]=%s" % (nomMachine,erreur)
return
# indirizzo 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
# ---------------------------------------- main
# costanti
HOTES=["istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","localhost","","xx"]
# indirizzi IP delle macchine di HOTES
for i in range(len(HOTES)):
getIPandName(HOTES[i])
# fine
sys.exit()
Note:
- riga 1: le funzioni di rete di Python sono incapsulate nel modulo socket.
11.2. Un client web
Uno script che consente di ottenere il contenuto del paragrafo "index" di un sito web.
import sys,socket
#-----------------------------------------------------------------------
def getIndex(site):
# legge il sito URL e lo salva nel file site.html
# inizialmente nessun errore
erreur=""
# creazione del file site.html
try:
html=open("%s.html" % (site),"w")
except IOError, erreur:
pass
# errore?
if erreur:
return "Erreur (%s) lors de la création du fichier %s.html" % (erreur,site)
# apertura di una connessione sulla porta 80 del sito
try:
connexion=socket.create_connection((site,80))
except socket.error, erreur:
pass
# ritorno in caso di errore
if erreur:
return "Echec de la connexion au site (%s,80) : %s" % (site,erreur)
# la connessione rappresenta un flusso di comunicazione bidirezionale
# tra il client (questo programma) e il server web contattato
# questo canale viene utilizzato per lo scambio di comandi e informazioni
# il protocollo di comunicazione è HTTP
# il client invia il comando GET per richiedere l'URL /
# sintassi GET URL HTTP/1.0
# le intestazioni (header) del protocollo HTTP devono terminare con una riga vuota
connexion.send("GET / HTTP/1.0\n\n")
# il server risponderà ora sul canale di connessione. Invierà tutti
# i propri dati, quindi chiuderà il canale. Il client legge tutto ciò che arriva dal canale di connessione
# fino alla chiusura del canale
ligne=connexion.recv(1000)
while(ligne):
html.write(ligne)
ligne=connexion.recv(1000)
# il client chiude a sua volta la connessione
connexion.close()
# chiusura del file HTML
html.close()
# ritorno
return "Transfert reussi du paragraphe index du site %s" % (site)
# --------------------------------------------- main
# recupera il testo HTML da URL
# elenco dei siti web
SITES=("istia.univ-angers.fr","www.univ-angers.fr","www.ibm.com","xx")
# lettura delle pagine indice dei siti della tabella SITES
for i in range(len(SITES)):
# lettura della pagina indice del sito SITES[i]
resultat=getIndex(SITES[i])
# visualizzazione del risultato
print resultat
# fine
sys.exit()
Note:
- riga 57: l'elenco degli URL dei siti web di cui si desidera la pagina iniziale. Questo elenco è memorizzato nel file di testo [nomsite.html];
- riga 62: la funzione getIndex esegue l'operazione;
- riga 4: la funzione getIndex;
- riga 20: il metodo create_connection((site,port)) consente di creare una connessione con un servizio TCP / IP in esecuzione sulla porta port del computer site;
- riga 35: il metodo send consente di inviare dati tramite una connessione TCP / IP. In questo caso viene inviato del testo. Tale testo segue il protocollo HTTP (HyperText Transfer Protocol);
- riga 40: il metodo recv consente di ricevere dati tramite una connessione TCP / IP. In questo caso, la risposta del server web viene letta in blocchi da 1000 caratteri e salvata nel file di testo [nomsite.html].
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
Il file ricevuto per il sito [www.ibm.com]:
- le righe da 1 a 11 sono le intestazioni HTTP della risposta del server;
- riga 1: il server richiede al client di reindirizzarsi all'indirizzo URL indicato alla riga 8;
- riga 2: data e ora della risposta;
- riga 3: identità del server web;
- riga 4: contenuto inviato dal server. In questo caso una pagina HTML che inizia alla riga 13;
- riga 12: la riga vuota che conclude le intestazioni HTTP;
- righe 13-19: la pagina HTML inviata dal server web.
11.3. Un client SMTP
Tra i protocolli TCP / IP, SMTP (SendMail Transfer Protocol) è il protocollo di comunicazione del servizio di invio dei messaggi.
# -*- coding=utf-8 -*-
import sys,socket
#-----------------------------------------------------------------------
def getInfos(fichier):
# restituisce le informazioni (smtp, mittente, destinatario, messaggio) ricavate dal file di testo [fichier]
# riga 1: smtp, mittente, destinatario
# righe seguenti: il testo del messaggio
# apertura di [fichier]
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# lettura della prima riga
ligne=infos.readline()
# eliminazione del carattere di fine riga
ligne=cutNewLineChar(ligne)
# recupero dei campi smtp, mittente, destinatario
champs=ligne.split(",")
# il numero di campi è corretto?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# "elaborazione" delle informazioni recuperate
# rimozione degli "spazi" che precedono o seguono le informazioni utili in ciascuno dei 3 campi
for i in range(3):
champs[i]=champs[i].strip()
# recupero dei campi
(smtpServer,expediteur,destinataire)=champs
message=""
# lettura del resto del messaggio
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# ritorno
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# invia il messaggio al server SMTP smtpserver a nome del mittente
# per il destinatario. Se verbose=True, tiene traccia degli scambi client-server
# si recupera il nome del client
try:
client=socket.gethostbyaddr(socket.gethostbyname("localhost"))[0]
except socket.error, erreur:
return "Erreur IP / Nom du client : %s" % (erreur)
# apertura di una connessione sulla porta 25 di smtpServer
try:
connexion=socket.create_connection((smtpServer,25))
except socket.error, erreur:
return "Echec de la connexion au site (%s,25) : %s" % (smtpServer,erreur)
# la connessione rappresenta un flusso di comunicazione bidirezionale
# tra il cliente (questo programma) e il server SMTP contattato
# questo canale viene utilizzato per lo scambio di comandi e informazioni
# dopo la connessione, il server invia un messaggio di benvenuto che viene letto
erreur=sendCommand(connexion,"",verbose,1)
if(erreur) :
connexion.close()
return erreur
# comando ehlo:
erreur=sendCommand(connexion,"EHLO %s" % (client),verbose,1)
if erreur :
connexion.close()
return erreur
# comando mail from:
erreur=sendCommand(connexion,"MAIL FROM: <%s>" % (expediteur),verbose,1)
if erreur :
connexion.close()
return erreur
# comando rcpt to:
erreur=sendCommand(connexion,"RCPT TO: <%s>" % (destinataire),verbose,1)
if erreur :
connexion.close()
return erreur
# comando data
erreur=sendCommand(connexion,"DATA",verbose,1)
if erreur :
connexion.close()
return erreur
# preparazione del messaggio da inviare
# deve contenere le righe
# Da: mittente
# A: destinatario
# riga vuota
# Messaggio
# .
data="From: %s\r\nTo: %s\r\n%s\r\n.\r\n" % (expediteur,destinataire,message)
# invio del messaggio
erreur=sendCommand(connexion,data,verbose,0)
if erreur :
connexion.close()
return erreur
# comando di uscita
erreur=sendCommand(connexion,"QUIT",verbose,1)
if erreur :
connexion.close()
return erreur
# fine
connexion.close()
return "Message envoye"
# --------------------------------------------------------------------------
def sendCommand(connexion,commande,verbose,withRCLF):
# invia comando nel canale di connessione
# modalità verbosa se verbose=1
# se withRCLF=1, aggiunge la sequenza RCLF al comando
# dati
RCLF="\r\n" if withRCLF else ""
# invio comando se il comando non è vuoto
if commande:
connexion.send("%s%s" % (commande,RCLF))
# eventuale eco
if verbose:
affiche(commande,1)
# lettura della risposta con meno di 1000 caratteri
reponse=connexion.recv(1000)
# eventuale eco
if verbose:
affiche(reponse,2)
# recupero codice di errore
codeErreur=reponse[0:3]
# errore restituito dal server?
if int(codeErreur) >=500:
return reponse[4:]
# risposta senza errori
return ""
# --------------------------------------------------------------------------
def affiche(echange,sens):
# visualizza lo scambio sullo schermo?
# se sens=1 visualizza -->scambio
# se sens=2 visualizza <-- scambio senza gli ultimi 2 caratteri RCLF
if sens==1:
print "--> [%s]" % (echange)
return
elif sens==2:
l=len(echange)
print "<-- [%s]" % echange[0:l-2]
return
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# elimina il carattere di fine riga da [ligne] se presente
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) che consente di inviare un messaggio
# le informazioni vengono prelevate da un file INFOS contenente le seguenti righe
# riga 1: smtp, mittente, destinatario
# righe successive: il testo del messaggio
# mittente: email del mittente
# destinatario: email del destinatario
# SMTP: nome del server SMTP da utilizzare
# protocollo di comunicazione client-server SMTP
# -> il client si connette alla porta 25 del server SMTP
# <- il server gli invia un messaggio di benvenuto
# -> il client invia il comando EHLO: nome del proprio computer
# <- il server risponde con OK oppure no
# -> il client invia il comando mail from: <mittente>
# <- il server risponde con OK oppure no
# -> il client invia il comando rcpt to: <destinatario>
# <- il server risponde con OK oppure no
# -> il client invia il comando data
# <- il server risponde con OK oppure no
# -> il client invia tutte le righe del proprio messaggio e termina con una riga contenente un solo carattere .
# <- il server risponde con OK oppure no
# -> il client invia il comando quit
# <- il server risponde con OK oppure no
# le risposte del server hanno il formato xxx testo, dove xxx è un numero a 3 cifre. Qualsiasi numero xxx >=500
# segnala un errore. La risposta può contenere più righe, tutte inizianti con xxx-, tranne l'ultima
# del formato xxx(spazio)
# le righe di testo scambiate devono terminare con i caratteri RC(#13) e LF(#10)
# # i parametri di invio dell’e-mail
MAIL="mail2.txt"
# si recuperano i parametri dell’e-mail
res=getInfos(MAIL)
# errore?
if res[0]:
print "%s" % (erreur)
sys.exit()
# invio della posta in modalità verbosa
(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)
# fine
sys.exit()
Note:
- su un computer Windows dotato di antivirus, quest’ultimo potrebbe impedire allo script Python di connettersi alla porta 25 di un server SMTP. In tal caso è necessario disattivare l’antivirus. Per McAfee, ad esempio, è possibile procedere come segue:
![]() |
- in [1], si attiva la console VirusScan
- da [2], si arresta il servizio [Protection lors de l'accès]
- in [3], il servizio è arrestato
Il file infos.txt:
smtp.univ-angers.fr, serge.tahe@univ-angers.fr , serge.tahe@univ-angers.fr
Subject: test
ligne1
ligne2
ligne3
Risultati sullo schermo:
Il messaggio visualizzato dal client di posta Thunderbird:
![]() |
11.4. Un secondo client SMTP
Questo secondo script fa la stessa cosa del precedente, ma utilizzando le funzionalità del modulo [smtplib].
# -*- coding=utf-8 -*-
import sys,socket, smtplib
#-----------------------------------------------------------------------
def getInfos(fichier):
# restituisce le informazioni (SMTP, mittente, destinatario, messaggio) ricavate dal file di testo [fichier]
# riga 1: SMTP, mittente, destinatario
# righe successive: il testo del messaggio
# apertura di [fichier]
erreur=""
try:
infos=open(fichier,"r")
except IOError, erreur:
return ("Le fichier %s n'a pu etre ouvert en lecture : %s" % (fichier, erreur))
# lettura della prima riga
ligne=infos.readline()
# rimozione del carattere di fine riga
ligne=cutNewLineChar(ligne)
# recupero dei campi smtp, mittente, destinatario
champs=ligne.split(",")
# il numero di campi è corretto?
if len(champs)!=3 :
return ("La ligne 1 du fichier %s (serveur smtp, expediteur, destinataire) a un nombre de champs incorrect" % (fichier))
# "elaborazione" delle informazioni recuperate - rimozione degli "spazi" che le precedono o le seguono
for i in range(3):
champs[i]=champs[i].strip()
# recupero dei campi
(smtpServer,expediteur,destinataire)=champs
# lettura del messaggio da inviare
message=""
ligne=infos.readline()
while ligne!='':
message+=ligne
ligne=infos.readline()
infos.close()
# ritorno
return ("",smtpServer,expediteur,destinataire,message)
#-----------------------------------------------------------------------
def sendmail(smtpServer,expediteur,destinataire,message,verbose):
# invia il messaggio al server SMTP smtpserver a nome del mittente
# per il destinatario. Se verbose=True, tiene traccia degli scambi client-server
# si utilizza la libreria smtplib
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)
# fine
return "Message envoye"
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# si rimuove il carattere di fine riga da [ligne] se presente
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) che consente di inviare un messaggio
# le informazioni vengono prelevate da un file INFOS contenente le seguenti righe
# riga 1: smtp, mittente, destinatario
# righe successive: il testo del messaggio
# mittente: email del mittente
# destinatario: email del destinatario
# SMTP: nome del server SMTP da utilizzare
# protocollo di comunicazione client-server SMTP
# -> il client si connette alla porta 25 del server SMTP
# <- il server gli invia un messaggio di benvenuto
# -> il client invia il comando EHLO: nome del proprio computer
# <- il server risponde con OK oppure no
# -> il client invia il comando mail from: <mittente>
# <- il server risponde con OK oppure no
# -> il client invia il comando rcpt to: <destinatario>
# <- il server risponde con OK oppure no
# -> il client invia il comando data
# <- il server risponde con OK oppure no
# -> il client invia tutte le righe del proprio messaggio e termina con una riga contenente un solo carattere .
# <- il server risponde con OK oppure no
# -> il client invia il comando quit
# <- il server risponde con OK oppure no
# le risposte del server hanno il formato xxx testo, dove xxx è un numero a 3 cifre. Qualsiasi numero xxx >=500
# segnala un errore. La risposta può contenere più righe, tutte inizianti con xxx-, tranne l'ultima
# del formato xxx(spazio)
# le righe di testo scambiate devono terminare con i caratteri RC(#13) e LF(#10)
# # i parametri di invio dell’e-mail
MAIL="mail2.txt"
# si recuperano i parametri dell’e-mail
res=getInfos(MAIL)
# errore?
if res[0]:
print "%s" % (erreur)
sys.exit()
# invio dell'e-mail in modalità verbosa
(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)
# fine
sys.exit()
Note:
- questo script è identico al precedente, fatta eccezione per la funzione sendmail. Questa funzione utilizza ora le funzionalità del modulo [smtplib] (riga 3).
Il file 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
Risultati sullo schermo:
11.5. Client/server di eco
Si crea un servizio di eco. Il server restituisce in maiuscolo tutte le righe di testo che gli vengono inviate dal client. Il servizio può servire più client contemporaneamente grazie all'utilizzo di thread.
# -*- coding=utf-8 -*-
# server TCP generico su Windows
# caricamento dei file di intestazione
import re,sys,SocketServer,threading
# server TCP multithread
class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):
def handle(self):
# thread corrente
cur_thread = threading.currentThread()
# dati del client
self.data="on"
# interruzione su stringa vuota
while self.data:
# le righe di testo del client vengono lette con il metodo readline
self.data =self.rfile.readline().strip()
# monitoraggio della console
print "client %s : %s (%s)" % (self.client_address[0],self.data,cur_thread.getName())
# invio della risposta al cliente
response = "%s: %s" % (cur_thread.getName(), self.data.upper())
# self.wfile è il flusso di scrittura verso il client
self.wfile.write(response)
class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
# ------------------------------------------------------ main
# sintassi di chiamata: argv[0] porta
# il server viene avviato sulla porta indicata
# Dati
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- si verifica la chiamata
# deve esserci un solo argomento
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# la porta deve essere un numero
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)
# avvio del server - serve ogni client su un thread
host="localhost"
server = ThreadedTCPServer((host,int(port)), ThreadedTCPRequestHandler)
# il server viene avviato in un thread
# ogni client verrà servito in un thread aggiuntivo
server_thread = threading.Thread(target=server.serve_forever)
# avvia il server - ciclo infinito di attesa dei clienti
server_thread.start()
# monitoraggio
print "Serveur d'echo a l'ecoute sur le port % s" % (port)
Note:
- riga 39: sys.argv rappresenta i parametri dello script. Devono avere la forma: nom_du_script porta. Devono quindi essercene due. sys.argv[0] diventerà quindi nom_du_script e sys.argv[1] diventerà port ;
- riga 53: il server di eco è un'istanza della classe ThreadedTcpServer. Il costruttore di questa classe richiede due parametri:
- parametro 1: una tupla di due elementi (host,port) che specifica il computer e la porta di ascolto del server;
- parametro 2: il nome della classe incaricata di elaborare le richieste di un client.
- riga 57: viene creato un thread (ma non ancora avviato). Questo thread esegue il metodo [serve_forever] del server TCP. Questo metodo è un ciclo di ascolto delle connessioni dei client. Non appena viene rilevata una connessione client, verrà creata un'istanza della classe ThreadedTCPRequestHandler. Il suo metodo handle è incaricato del dialogo con il client;
- riga 59: viene avviato il thread del servizio di eco. Da questo momento in poi i client possono connettersi al servizio;
- riga 27: la classe del server di eco. Deriva da due classi: SocketServer.ThreadingMixIn e SocketServer.TCPServer. Ciò lo rende un server TCP multithread: il server viene eseguito in un thread e ogni cliente viene servito in un thread aggiuntivo;
- riga 9: la classe che gestisce le richieste dei clienti. Deriva dalla classe SocketServer.StreamRequestHandler. Eredita quindi due attributi:
- rfile: che rappresenta il flusso di lettura dei dati inviati dal cliente – può essere trattato come un file di testo;
- wfile: che rappresenta il flusso di scrittura che consente di inviare dati al cliente; può essere trattato come un file di testo.
- riga 11: il metodo handle gestisce le richieste dei clienti;
- riga 13: il thread che esegue questo metodo;
- riga 17: ciclo di elaborazione delle richieste del cliente. Il ciclo termina quando il cliente invia una riga vuota;
- riga 19: lettura della richiesta del cliente;
- riga 21: self.client_address[0] rappresenta l’indirizzo IP del cliente. cur_thread.getName() è il nome del thread che esegue il metodo handle;
- riga 23: la risposta al cliente è composta da due parti: il nome del thread che serve il cliente e il comando che questi ha inviato, scritto in maiuscolo.
# -*- coding=utf-8 -*-
import re,sys,socket
# ------------------------------------------------------ main
# client TCP generico
# sintassi della chiamata: argv[0] host porta
# il client si connette al servizio echo (host, porta)
# il server restituisce le righe digitate dal cliente
# sintassi
syntaxe="syntaxe : %s hote port" % (sys.argv[0])
#------------------------------------- si verifica la chiamata
# devono esserci due argomenti
nbArguments=len(sys.argv)
if nbArguments!=3:
print syntaxe
sys.exit(1)
# si recuperano gli argomenti
hote=sys.argv[1]
# la porta deve essere un numero
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:
# connessione del client al 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:
# ciclo di immissione
ligne=raw_input("Commande (rien pour arreter): ").strip()
while ligne!="":
# si invia la riga al server
connexion.send("%s\n" % (ligne))
# si attende la risposta
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:
# si chiude la connessione
connexion.close()
Il server viene avviato in una prima finestra di comando:
Un primo client viene avviato in una seconda finestra:
Il client riceve correttamente, in risposta al comando inviato al server, lo stesso comando scritto in maiuscolo. Viene avviato un secondo client in una terza finestra:
La console del server appare quindi come segue:
I client vengono effettivamente gestiti in thread diversi. Per terminare un client, è sufficiente digitare un comando vuoto.
11.6. Server TCP generico
Ci proponiamo di scrivere uno script Python che
- funga da server TCP in grado di servire un solo client alla volta,
- accetti righe di testo inviate dal client,
- accetti righe di testo provenienti dalla tastiera e che vengano inviate in risposta al client.
In questo modo è l'utente alla tastiera a fungere da server:
- vede sulla propria console le righe di testo inviate dal cliente;
- risponde a quest'ultimo digitando la risposta sulla tastiera.
In questo modo può adattarsi a qualsiasi tipo di client. Per questo motivo lo chiameremo "server TCP generico". Si tratta di uno strumento utile per esplorare i protocolli di comunicazione TCP. Nell'esempio che segue, il client TCP sarà un browser web, il che ci consentirà di esplorare il protocollo HTTP utilizzato dai client web.
![]() |
# -*- coding=utf-8 -*-
# server TCP generico
# caricamento dei file di intestazione
import re,sys,SocketServer,threading
# server TCP generico
class MyTCPHandler(SocketServer.StreamRequestHandler):
def handle(self):
# visualizzazione del client
print "client %s" % (self.client_address[0])
# si crea un thread per la lettura dei comandi del client
thread_lecture = threading.Thread(target=self.lecture)
thread_lecture.start()
# interruzione al comando 'bye'
# lettura del comando digitato dalla tastiera
cmde=raw_input("--> ")
while cmde!="bye":
# invio del comando al client. self.wfile è il flusso di scrittura verso il client
self.wfile.write("%s\n" % (cmde))
# lettura del comando successivo
cmde=raw_input("--> ")
def lecture(self):
# vengono visualizzate tutte le righe inviate dal client fino alla ricezione del comando "bye"
ligne=""
while ligne!="bye":
# le righe di testo del client vengono lette con il metodo readline
ligne = self.rfile.readline().strip()
# monitoraggio della console
print "<--- %s : %s" % (self.client_address[0], ligne)
# ------------------------------------------------------ main
# sintassi di chiamata: argv[0] porta
# il server viene avviato sulla porta specificata
# Dati
syntaxe="syntaxe : %s port" % (sys.argv[0])
#------------------------------------- si verifica la chiamata
# deve esserci un solo argomento
nbArguments=len(sys.argv)
if nbArguments!=2:
print syntaxe
sys.exit(1)
# la porta deve essere un numero
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)
# avvio del server
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()
Note:
- riga 57: il server TCP sarà un'istanza della classe SocketServer.TCPServer. Il suo costruttore accetta due parametri:
- il primo parametro è una tupla di due elementi (host, porta) in cui host è la macchina su cui opera il servizio (generalmente localhost) e port, la porta su cui il servizio attende (ascolta) le richieste dei client;
- il secondo parametro specifica la classe di servizio di un client. Quando un client si connette, viene creata un’istanza della classe di servizio ed è il suo metodo handle che deve gestire la connessione con il client.
Il server TCP SocketServer.TCPServer non è multithread. Serve un cliente alla volta;
- riga 59: viene eseguito il metodo serve_forever del server TCP. Si tratta di un ciclo infinito di attesa dei clienti;
- riga 9: il server TCP è qui una classe derivata dalla classe SocketServer.StreamRequestHandler. Ciò consente di considerare i flussi di dati scambiati con il client come file di testo. Abbiamo già incontrato questa classe. Abbiamo a disposizione i metodi:
- readline per leggere una riga di testo proveniente dal client;
- write per inviargli delle righe di testo.
- riga 12: self.client_address[0] è l’indirizzo IP del cliente;
- riga 14: il server TCP comunicherà con il client tramite due thread
- un thread per la lettura delle righe dal cliente;
- un thread per la scrittura delle righe al client.
- riga 14: viene creato il thread di lettura delle righe del client. Il suo parametro target definisce il metodo eseguito dal thread. Si tratta di quello definito alla riga 25;
- riga 25: viene avviato il thread di lettura;
- righe 25-32: il metodo eseguito dal thread di lettura;
- riga 28: il thread di lettura legge tutte le righe di testo inviate dal client fino alla ricezione della riga "bye";
- riga 18: ci troviamo nel thread di scrittura verso il client. Il principio è quello di inviare al client tutte le righe di testo digitate dall'utente sulla tastiera.
Il server
Il browser client
La richiesta ricevuta dal server
La risposta del server digitata dall'utente sulla tastiera (senza il segno -->)
- righe 1-5: risposta HTTP inviata al client;
- riga 6: la pagina HTML inviata al client;
- riga 7: fine del dialogo con il cliente. Il servizio al cliente terminerà e la connessione verrà chiusa, il che interromperà bruscamente il thread di lettura delle righe di testo inviate dal cliente;
- le righe 1-5 della risposta HTTP inviata al cliente hanno il seguente significato:
- riga 1: la risorsa richiesta dal cliente è stata trovata;
- riga 2: identificazione del server;
- riga 3: il server chiuderà la connessione dopo aver inviato la risorsa;
- riga 4: tipo di risorsa inviata dal server: un documento HTML;
- riga 5: una riga vuota.
La pagina visualizzata dal browser [1]:
![]() |
Se si visualizza il codice sorgente ricevuto dal browser [2], si nota che il codice HTML ricevuto dal browser web è effettivamente quello che gli era stato inviato.




