Programmazione di applicazioni Client

Transcript

Programmazione di applicazioni Client
Scuola Politecnica e delle Scienze di Base
Corso di Laurea in Ingegneria Informatica
Elaborato finale in PROGRAMMAZIONE I
Programmazione di applicazioni ClientServer in un linguaggio interpretato:
Python
Anno Accademico 2015/2016
Candidato:
Mickael Di Carluccio
matr. N46000541
Indice
Introduzione
1 Python
III
1
1.1
Caratteristiche Fondamentali . . . . . . . . . . . . . . . . . . . . . .
2
1.2
Utilizzi tipici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.3
Primo approccio . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.4
Costrutti di Controllo . . . . . . . . . . . . . . . . . . . . . . . . . .
7
1.5
Ciclo: For e While . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.6
Strutture Dati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
1.7
File . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
18
2 Programmazione Distribuita
21
2.1
Applicazioni Client-Server . . . . . . . . . . . . . . . . . . . . . . .
23
2.2
Architettura Client-Server . . . . . . . . . . . . . . . . . . . . . . .
25
2.3
Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
2.4
Tipi di Socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
3 Programmazione in Python di un’applicazione Client-Server
30
3.1
Primo Esempio . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
3.2
Trasferimento di File . . . . . . . . . . . . . . . . . . . . . . . . . .
33
Propongo di considerare questa
domanda:
"Le macchine sono in grado di
pensare?"
—Alan Turing
Introduzione
Python è un linguaggio di programmazione dinamico orientato agli oggetti utilizzabile per molti tipi di sviluppo software. Offre un forte supporto all’integrazione
con altri linguaggi e programmi, è fornito di una estesa libreria standard e può
essere imparato in pochi giorni. Molti programmatori Python possono confermare
un sostanziale aumento di produttività e ritengono che il linguaggio incoraggi allo
sviluppo di codice di qualità e manutenibilità superiori. Python gira sui più comuni sistemi operativi come Windows, Linux/Unix, Mac OS.
In questo elaborato si descrivono le caratteristiche fondamentali del linguaggio e
si illustrano i fondamenti della programmazione in Python di applicazioni client e
server comunicanti attraverso i protocolli TCP/IP.
Figura 1: Python
III
Capitolo 1
Python
Python è un linguaggio di programmazione ad alto livello, rilasciato pubblicamente
per la prima volta nel 1991 dal suo creatore Guido van Rossum, programmatore
olandese attualmente operativo in Dropbox. Deriva il suo nome dalla commedia
Monty Python’s Flying Circus dei celebri Monty Python, in onda sulla BBC nel
corso degli anni 70. Attualmente, lo sviluppo di Python (grazie e soprattutto
all’enorme e dinamica comunità internazionale di sviluppatori) viene gestito dall’organizzazione no-profit Python Software Foundation. Python supporta diversi
paradigmi di programmazione, come quello object-oriented (con supporto all’ereditarietà multipla), quello imperativo e quello funzionale, ed offre una tipizzazione
dinamica forte. È fornito di una libreria built-in estremamente ricca, che unitamente alla gestione automatica della memoria e a robusti costrutti per la gestione
delle eccezioni fa di Python uno dei linguaggi più ricchi e comodi da usare. Comodo, ma anche semplice da usare e imparare. Python, nelle intenzioni di Guido
van Rossum, è nato per essere un linguaggio immediatamente intuibile. La sua
sintassi è pulita e snella così come i suoi costrutti, decisamente chiari e non ambigui. I blocchi logici vengono costruiti semplicemente allineando le righe allo stesso
modo, incrementando la leggibilità e l’uniformità del codice anche se vi lavorano
diversi autori. Python è un linguaggio pseudocompilato: un interprete si occupa
di analizzare il codice sorgente (semplici file testuali con estensione .py) e, se sin1
Capitolo 1. Python
tatticamente corretto, di eseguirlo. In Python, non esiste una fase di compilazione
separata (come avviene in C, per esempio) che generi un file eseguibile partendo
dal sorgente. L’esser pseudointerpretato rende Python un linguaggio portabile.
Una volta scritto un sorgente, esso può essere interpretato ed eseguito sulla gran
parte delle piattaforme attualmente utilizzate, siano esse di casa Apple (Mac) che
PC (Microsoft Windows e GNU/Linux). Semplicemente, basta la presenza della
versione corretta dell’interprete. Infine, Python è free software: non solo il download dell’interprete per la propria piattaforma, così come l’uso di Python nelle
proprie applicazioni, è completamente gratuito; ma oltre a questo Python può essere liberamente modificato e così ridistribuito, secondo le regole di una licenza
pienamente open-source. Queste caratteristiche hanno fatto di Python il protagonista di un enorme diffusione in tutto il mondo, e anche in Italia, negli ultimi anni.
Questo perché garantisce lo sviluppo rapido (e divertente) di applicazioni di qualsiasi complessità in tutti i contesti: dal desktop al web, passando dallo sviluppo di
videogiochi e dallo scripting di sistema.
1.1
Caratteristiche Fondamentali
1. È free
Python è completamente gratuito ed è possibile usarlo e distribuirlo senza
restrizioni di copyright. Spesso alla parola “free” è associato il concetto di
“software non supportato”, ma questo non è assolutamente vero nel caso di
python: è sufficiente consultare periodicamente il sito ufficiale per rendersene
conto.
2. È orientato agli oggetti
Python è un linguaggio orientato agli oggetti, supporta nozioni avanzate di
polimorfismo, ereditarietà, operatori di overloading il tutto con una semplice
sintassi.
2
Capitolo 1. Python
3. È portabile
Python è un linguaggio portabile sviluppato in ANSI C. È possibile usarlo su
diverse piattaforme come: Unix, Linux, Windows, DOS, Macintosh, Sistemi
Real Time, OS/2, cellulari Nokia e Android, questo perché è interpretato,
quindi lo stesso codice può essere eseguito su qualsiasi piattaforma purché
abbia l’interprete Python installato.
4. È facile da usare
Chi ha fatto esperienza di programmazione con altri linguaggi non troverà
Python complesso da imparare. Si tratta infatti di un linguaggio di alto
livello con una sintassi dalle regole piuttosto semplici.
5. È ricco di librerie
La dotazione standard offre numerose librerie alle quali si aggiungono moduli
di terze parti che crescono continuamente. In Internet si trova materiale
relativo a HTML, PDF, XML, formati grafici, CGI e perfino interi Web
Server.
6. È performante
Python è un linguaggio interpretato. In questo caso “interpretato” non è
sinonimo di lento, infatti python “compila” il proprio codice in un bytecode molto efficiente. Questo permette di raggiungere prestazioni vicine ai
linguaggi in codice nativo. Inoltre python implementa molte strutture dati e funzioni come componente intrinseca del linguaggio. Queste strutture
sono dette “built-in types and tools” e sono state sviluppate con accurata
efficienza.
7. Gestisce la memoria automaticamente
Come in Java, in Python esiste il meccanismo di “garbage collection“, che
permette di liberare il programmatore dall’ansia di allocazione selvaggia della
memoria.
3
Capitolo 1. Python
8. È integrabile ad altri linguaggi
Python può essere integrato ad altri linguaggi come .NET con IronPython o
python per .NET, Java con Jython
1.2
Utilizzi tipici
Python è un linguaggio come detto poc’anzi, molto potente ed infatti sono davvero
tanti gli ambiti in cui è usato:
1. Accesso ai Database:
Python definisce una modalità standard di accesso ai database, la cosiddetta
DB-API [1] attualmente alla v2.0. E’ possibile collegarsi con qualsiasi database esistente, tra cui:
-Oracle
-MySQL
-SQLite
2. Applicazioni desktop:
Python include nella libreria standard i binding alla libreria di sviluppo TkInter [2], ma se desiderate interfacciarvi con altri toolkit grafici, non c’è che
l’imbarazzo della scelta. Tra le più diffuse segnaliamo:
-wxPython (fusione della libreria C++ wxWidgets con Python)
-PyQt (tutorial)
3. Giochi e grafica 3D:
Python è ampiamente usato per lo sviluppo di giochi sia a livello commerciale
che "hobbistico". Tra i principali strumenti ricordiamo:
-PyGame
-PyKyra
4
Capitolo 1. Python
4. Sviluppo software:
Esistono innumerevoli aspetti legati allo sviluppo software, che possono aiutare ogni sviluppatore a diventare un programmatore migliore. Anche Python
non si sottrae a questo compito e fornisce alcuni strumenti a dir poco vitali:
-Trac è uno dei progetti migliori nel suo genere. Si tratta di un wiki che
integra un sistema di bugtracking altamente configurabile
-Buildbot è un framework studiato per costruire e controllare lo sviluppo
software attraverso test e definendo processi di sviluppo controllati
-Roundup è un semplice, ma efficace, sistema di tracciamento estremamente
personalizzabile.
1.3
Primo approccio
Scrivere codici in Python risulta davvero semplice e non ha bisogno di un compiler. Si tratta di un linguaggio interpretato, e questo significa che il programma può
essere eseguito appena saranno finite le modifiche apportate al file. Questo rende
la revisione, la modifica e la risoluzione dei problemi di un programma molto più
rapida rispetto agli altri linguaggi. Come ogni programmatore sa, il primo approccio a qualsiasi linguaggio di programmazione è la verifica dell’output a schermo del
messaggio "Hello, World!".
"Print" è una delle funzioni di base di Python, ed è usata per visualizzare informazioni nel terminale durante un programma. Al contrario di molti altri linguaggi
(es. C, C++), non serve indicare la fine di una linea con ";". Non servirà neppure
usare parentesi graffe () per indicare dei blocchi. Basterà indentare il testo per
indicare la sua inclusione in un blocco.
Una volta che le righe di codice sono finite, basterà salvare il file specificando l’estensione del file ".py" . Assicurarsi di salvare il file in un punto di facile accesso,
perché si dovrà raggiungere il suo percorso dal prompt dei comandi.
5
Capitolo 1. Python
Come avviene l’esecuzione del programma?
Basterà aprire il Prompt dei Comandi o il Terminale e raggiungere il percorso dove
è stato salvato il file. Eseguire, poi, il file digitando "Nome_Programma".py. Nel
caso in esame, per un primo programma in Python, si è scelto di salvare il file con
la denominazione "hello.py".
—-Codice
1
print (" Hello , World ! ")
HelloWorld.py
—–Stampa
1
2
3
4
Microsoft Windows [ Versione 6.1.7601] Copyright ( c ) 2009
Microsoft Corporation . Tutti i diritti riservati .
C :\ Users \ Mickael Di Carluccio > D :\ Desktop \ Tesi \ hello . py
Hello , World !
Come si può notare dalla Stampa, per poter avere accesso alla Directory in
cui risiede il file di interesse ".py" è necessario conoscere esattamente dove è stato
salvato il file e fare un’operazione di "change directory" con l’uso dello slash tra
una cartella e una sottocartella.
6
Capitolo 1. Python
1.4
Costrutti di Controllo
Si veda un esempio di programma con flusso di controllo dove c’è un input da
tastiera, e una verifica dell’età con i costrutti: if, elif, else ed una stampa sul
terminale del messaggio in base all’età inserita.
Codice
1
2
3
4
5
6
7
anni = int ( input (" Inserisci la tua eta ’: "))
if anni <= 12:
print (" Che bello essere un bambino ! ")
elif anni in range (13 , 26):
print (" Sei ancora un ragazzo ! ")
else :
print (" Chiamami Signore ")
—Stampa
1
Microsoft Windows [ Versione 6.1.7601] Copyright ( c ) 2009
2
Microsoft Corporation . Tutti i diritti riservati .
3
C :\ Users \ Mickael Di Carluccio > D :\ Desktop \ Tesi \ Control . py
4
Inserisci la tua eta ’:
5
Sei ancora un ragazzo !
26
Vediamo meglio come viene interpretata la logica usata dai costrutti di controllo
if, elif, else.
-if : nell’esempio di cui sopra "se gli anni inseriti da tastiera sono un numero intero
inferiore o uguale a 12" potranno essere eseguite le istruzioni al suo interno.
N.B. Python non usa le parentesi graffe, bensì i due punti.
-elif : Abbreviazione di elseif è usata per indicare una seconda condizione da
controllare qualora la condizione specificata nel costrutto if non rispetti i parametri
specificati.
N.B. E’ stata usata anche la parola chiave range per indicare quali sono gli estremi
specificati.
else: è il costrutto che prende in esame i valori che non rientrano nelle condizioni
7
Capitolo 1. Python
dell’if e dell’elif.
Nel caso sopra citato qualora il valore di input inserito sia superiore a 26, verrà
mandata a video la stampa "Chiamami Signore".
1.5
Ciclo: For e While
In programmazione, ci sono molti programmi più elementari che usano la ricorsione per eseguire una ripetizione. Questa ripetizione è più comunemente chiamata
iterazione. Dato che l’iterazione è così comune, Python fornisce vari sistemi per
renderla più semplice da implementare. Il primo sistema è l’istruzione while.
Ecco un esempio, ContoAllaRovescia, viene riscritto usando l’istruzione while:
Codice
1
2
3
4
5
def ContoAllaRovescia ( n ):
while n > 0:
print n
n = n -1
print (" Partenza !")
La chiamata ricorsiva è stata rimossa e quindi questa funzione ora non è più
ricorsiva.
Si può leggere il programma con l’istruzione while come fosse scritto in un linguaggio naturale:
"Finchè (while) n è più grande di 0 stampa il valore di n e poi diminuiscilo di 1.
Quando arrivi a 0 stampa la stringa "Partenza!"."
In modo più formale ecco il flusso di esecuzione di un’istruzione while:
1. Valuta la condizione controllando se essa è vera (1) o falsa (0).
2. Se la condizione è falsa esci dal ciclo while e continua l’esecuzione dalla prima
istruzione che lo segue.
3. Se la condizione è vera esegui tutte le istruzioni nel corpo del while e torna
al passo 1.
8
Capitolo 1. Python
Il corpo del ciclo while consiste di tutte le istruzioni che seguono l’intestazione e
che hanno la stessa indentazione. Questo tipo di flusso è chiamato ciclo o loop.
Nota che se la condizione è falsa al primo controllo, le istruzioni del corpo non sono
mai eseguite.
Il corpo del ciclo dovrebbe cambiare il valore di una o più variabili così che la
condizione possa prima o poi diventare falsa e far così terminare il ciclo. In caso
contrario il ciclo si ripeterebbe all’infinito, determinando un ciclo infinito. Nel
caso di ContoAllaRovescia possiamo essere certi che il ciclo è destinato a terminare
visto che n è finito ed il suo valore diventa via via più piccolo fino a diventare pari
a zero. Il ciclo for è un altro dei cicli di iterazione messi a disposizione dal linguaggio Python, si tratta in sostanza di un costrutto, comune anche ad altri linguaggi,
che consente di ripetere un’operazione un certo numero di volte, più tecnicamente "iterare una sequenza o un oggetto", fino alla soddisfazione di una determinata
condizione (implicita) che terminerà il ciclo.
Per chiarire tale dinamica sarà possibile proporre un semplice esempio basato sull’impiego delle liste, queste ultime sono un tipo di dato supportato da Python che
verrà analizzato nel prossimo paragrafo, per il momento basti sapere che una lista
permette di gestire un insieme di valori di diversa natura (stringhe, interi, decimali) delimitati da parentesi quadre e separati tramite una virgola.
Il codice proposto di seguito permetterà di ciclare e visualizzare in output tutti gli
elementi presenti in una lista definita dallo sviluppatore.
––FIBONACCI
1
2
3
4
5
6
# definizione della lista
fibonacci = [1 ,1 ,2 ,3 ,5 ,8 ,13 ,21 ,34 ,55 ,89 ,144]
# iterazione dei valori in lista
for val in fibonacci :
# stampa dei valori iterati
print ( val )
Nell’esempio mostrato la condizione da soddisfare per la terminazione del ciclo
sarà quindi quella di stampare fino all’ultimo valore presente nella lista passata
9
Capitolo 1. Python
come argomento, nel nostro caso "fibonacci".
Un fattore sintattico molto importante da tenere a mente riguarda il fatto che nella
digitazione di un ciclo for l’engine di Python si aspetta che l’istruzione associata
al ciclo venga indentata.
Vedremo nel paragrafo File anche l’istruzione break che è l’istruzione che permette al ciclo di terminare anzitempo. Ossia, qualora si verificasse la condizione
desiderata, dopo le istruzione eseguite all’interno del ciclo While, break termina il
ciclo passando alla prima istruzione fuori dal ciclo.
1.6
Strutture Dati
Vediamo quali sono le strutture dati più usate come Liste, Pile, Code.
1.6.1
Liste
Una lista[4] è una serie ordinata di valori, ognuno identicato da un indice. I valori
che fanno parte della lista sono chiamati elementi. Le liste sono simili alle stringhe
essendo insiemi ordinati di caratteri, fatta eccezione per il fatto che gli elementi di
una lista possono essere di tipo qualsiasi. Liste e stringhe (e altri tipi di dati che si
comportano da insiemi ordinati) sono chiamate sequenze. Ci sono parecchi modi
di creare una lista nuova, e quello più semplice è racchiudere i suoi elementi tra
parentesi quadrate ([]):
1. [10, 20, 30, 40]
2. ["Pippo", "Pluto", "Paperino"]
Il primo esempio è una lista di quattro interi, il secondo una lista di tre stringhe.
Gli elementi di una stessa lista non devono necessariamente essere tutti dello stesso
tipo.
Questa lista, infatti, contiene una stringa, un numero in virgola mobile, un intero
10
Capitolo 1. Python
ed un’altra lista:
["ciao", 2.0, 5, [10, 20]]
Una lista all’interno di un’altra lista è detta lista annidata.
Le liste che contengono numeri interi consecutivi sono così comuni che Python
fornisce un modo semplice per crearle:
1
2
range (1 ,5)
[1 , 2 , 3 , 4]
La funzione range prende due argomenti e ritorna una lista che contiene tutti
gli interi a partire dal primo (incluso) fino al secondo (escluso). Ci sono altre due
forme per range. Con un solo argomento crea una lista a partire da 0:
1
2
range (10)
[0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9]
Se è presente un terzo argomento questo specifica l’intervallo tra valori successivi, chiamato passo. Questo esempio mostra come ottenere una stringa dei numeri
dispari tra 1 e 10:
1
2
range (1 , 10 , 2)
[1 , 3 , 5 , 7 , 9]
Infine esiste una lista speciale che non contiene alcun elemento: è chiamata lista
vuota ed è indicata da [ ].
1.6.2
Classi ed oggetti
Le classi sono tipi composti definiti dall’utente.
Vediamo nello specifico cosa significa e quali migliorie comporta. Creiamo dunque,
il tipo Punto.
Considerando il concetto matematico di punto nelle due dimensioni, il punto è
definito da una coppia di numeri (le coordinate). In notazione matematica le coordinate dei punti sono spesso scritte tra parentesi con una virgola posta a separare
11
Capitolo 1. Python
i due valori. Per esempio (0, 0) rappresenta l’origine e (x, y) il punto che si trova
x unità a destra e y unità in alto rispetto all’origine. Un modo naturale di rappresentare un punto in Python è una coppia di numeri in virgola mobile e la questione
che ci rimane da definire è in che modo raggruppare questa coppia di valori in un
oggetto composto: un sistema veloce anche se poco elegante sarebbe l’uso di una
tupla, anche se possiamo fare di meglio.
Un modo alternativo è quello di definire un nuovo tipo composto chiamato classe.
Una definizione di classe ha questa sintassi:
1
2
class Punto :
pass
Le definizioni di classe possono essere poste in qualsiasi punto di un programma ma solitamente per questioni di leggibilità sono poste all’inizio, subito sotto
le istruzioni import. Le regole di sintassi per la definizione di una classe sono le
stesse degli altri tipi composti: la definizione dell’esempio crea una nuova classe
chiamata Punto. L’istruzione pass non ha effetti: è stata usata per il solo fatto che
la definizione prevede un corpo che deve ancora essere scritto.
Creando la classe Punto abbiamo anche creato un nuovo tipo di dato chiamato con
lo stesso nome. I membri di questo tipo sono detti istanze del tipo o oggetti. La
creazione di una nuova istanza è detta istanziazione: solo al momento dell’istanziazione parte della memoria è riservata per depositare il valore dell’oggetto. Per
creare un oggetto di tipo Punto viene chiamata una funzione chiamata Punto:
1
P1 = Punto ()
Alla variabile P1 è assegnato il riferimento ad un nuovo oggetto Punto. Una
funzione come Punto, che crea nuovi oggetti e riserva quindi della memoria per
depositarne i valori, è detta costruttore. Possiamo aggiungere un nuovo dato ad
un’istanza usando la notazione punto:
12
Capitolo 1. Python
Figura 1.1: Assegnazione attributi alla classe Punto
P1 . x = 3.0
P1 . y = 4.0
1
2
In questo caso stiamo selezionando una voce da un’istanza e queste voci che
fanno parte dell’istanza sono dette attributi. Questo diagramma di stato mostra il
risultato delle assegnazioni:
La variabile P1 si riferisce ad un oggetto Punto che contiene due attributi
ed ogni attributo (una coordinata) si riferisce ad un numero in virgola mobile.
Possiamo leggere il valore di un attributo con la stessa sintassi:
print P1 . y
1
2
4.0
x = P1 . x
print x
3
4
5
3.0
L’espressione P1.x significa "vai all’oggetto puntato da P1 e ottieni il valore del
suo attributo x". In questo caso assegniamo il valore ad una variabile chiamata
x: non c’è conflitto tra la variabile locale x e l’attributo x di P1: lo scopo della
notazione punto è proprio quello di identificare la variabile cui ci si riferisce evitando
le ambiguità.
13
Capitolo 1. Python
1.6.3
Pile
In questo paragrafo verrà esaminata la pila, un tipo di dato astratto molto comune. Una pila è una collezione e cioè una struttura di dati che contiene elementi
multipli.
Un TDA (Tipo Dato Astratto) è definito dalle operazioni che possono essere effettuate su di esso e che sono chiamate interfaccia. L’interfaccia per una pila
consiste di queste operazioni:
__init__: Inizializza un pila vuota.
Push: Aggiunge un elemento alla pila.
Pop: Rimuove e ritorna un elemento dalla pila. L’elemento tornato è sempre l’ultimo inserito.
EVuota: Controlla se la pila è vuota.
Una pila è spesso chiamata struttura di dati LIFO ("last in/first out", ultimo inserito, primo fuori) perché l’ultimo elemento inserito in ordine di tempo è il primo
ad essere rimosso: un esempio è una serie di piatti da cucina sovrapposti, ai quali
aggiungiamo ogni ulteriore piatto appoggiandolo sopra agli altri, ed è proprio dall’alto che ne preleviamo uno quando ci serve.
Le operazioni che Python fornisce per le liste sono simili a quelle definite per la
nostra pila. Il codice utilizzato è chiamato implementazione del TDA Pila. Più in
generale un’implementazione è un insieme di metodi che soddisfano la sintassi e la
semantica dell’interfaccia richiesta.
—–Classe Pila
1
2
3
4
5
6
7
8
9
class Pila :
def __init__ ( self ):
self . Elementi = []
def Push ( self , Elemento ) :
self . Elementi . append ( Elemento )
def Pop ( self ):
return self . Elementi . pop ()
def EVuota ( self ):
return ( self . Elementi == [])
14
Capitolo 1. Python
L’oggetto Pila contiene un attributo chiamato Elementi che è la lista di oggetti
contenuta nella pila.
Il metodo __init__ inizializza Elementi come lista vuota. Push inserisce un
nuovo elemento nella pila aggiungendolo a Elementi. Pop esegue l’operazione inversa, rimuovendo e ritornando l’ultimo elemento inserito nella pila. Per controllare
se la pila è vuota EVuota confronta Elementi con una lista vuota e ritorna vero/falso.
Un’implementazione di questo tipo in cui i metodi sono solo una semplice invocazione di metodi già esistenti viene detta maschera.
1.6.4
Code
Questo paragrafo presenta due tipi di dati astratti (TDA): la Coda e la Coda con
priorità. Nella vita reale un esempio di coda può essere la linea di clienti in attesa
di un servizio di qualche tipo. Nella maggior parte dei casi il primo cliente della
fila è quello che sarà servito per primo, anche se ci possono essere delle eccezioni.
Ad esempio, all’aeroporto ai clienti il cui volo sta per partire può essere concesso
di passare davanti a tutti, indipendentemente dalla loro posizione nella fila o anche
al supermercato un cliente può scambiare per cortesia il suo posto con qualcuno
che deve pagare solo pochi prodotti.
La regola che determina chi sarà il prossimo ad essere servito si chiama politica
di accodamento. Quella più semplice è la FIFO ("First in, first out") dove il
primo che arriva è il primo ad essere servito. La politica di accodamento più
generale è l’accodamento con priorità dove a ciascun cliente è assegnata una priorità
ed il cliente con la massima priorità viene servito per primo indipendentemente
dall’ordine di arrivo.
Diciamo che questa politica di accodamento è la più generale perché la priorità
può essere basata su qualsiasi fattore: l’orario di partenza dell’aereo, la quantità
di prodotti da pagare ad una cassa o anche la gravità dello stato di un paziente al
15
Capitolo 1. Python
pronto soccorso.
I tipi di dati astratti Coda e Coda con priorità condividono lo stesso insieme di
operazioni. La differenza sta soltanto nella loro semantica: una Coda usa la politica
FIFO, mentre la Coda con priorità, come suggerisce il nome stesso, usa la politica
di accodamento con, appunto, priorità.
Una prima implementazione del TDA Coda a cui guarderemo è chiamata coda
linkata perché è composta di oggetti Nodo linkati. Ecco una definizione della
classe:
—–Classe Coda
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Coda :
def __init__ ( self ):
self . Lunghezza = 0
self . Testa = None
def EVuota ( self ):
return ( self . Lunghezza == 0)
def Inserimento ( self , Contenuto ):
NodoAggiunto = Nodo ( Contenuto )
NodoAggiunto . ProssimoNodo = None
if self . Testa == None :
# se la lista e ’ vuota il nodo e ’ il primo
self . Testa = Nodo
else :
# trova l ’ ultimo nodo della lista
Ultimo = self . Testa
while Ultimo . ProssimoNodo : Ultimo = Ultimo . ProssimoNodo
# aggiunge il nuovo nodo
Ultimo . ProssimoNodo = NodoAggiunto
self . Lunghezza = self . Lunghezza + 1
def Rimozione ( self ):
Contenuto = self . Testa . Contenuto
self . Testa = self . Testa . ProssimoNodo
self . Lunghezza = self . Lunghezza - 1
return Contenuto
Il TDA Coda con priorità ha la stessa interfaccia del TDA Coda ma una semantica diversa. L’interfaccia è sempre:
__init__ : Inizializza una nuova coda vuota. Inserimento: Aggiungi un elemento
alla coda. Rimozione: Rimuovi un elemento dalla coda. L’elemento da rimuovere
e ritornare è quello con la priorità più alta. EVuota: Controlla se la coda è vuota.
16
Capitolo 1. Python
La differenza di semantica è che l’elemento da rimuovere non è necessariamente il
primo inserito in coda, ma quello che ha la priorità più alta. Cosa siano le priorità
e come siano implementate sono fatti non specificati dall’implementazione, dato
che questo dipende dal genere di elementi che compongono la coda. Per esempio se
gli elementi nella coda sono delle stringhe potremmo estrarle in ordine alfabetico.
Se sono punteggi del bowling dal più alto al più basso, e viceversa nel caso del golf.
In ogni caso possiamo rimuovere l’elemento con la priorità più alta da una coda
soltanto se i suoi elementi sono confrontabili tra di loro.
Questa è un’implementazione di una coda con priorità che usa una lista Python
come attributo per contenere gli elementi della coda:
—–Classe Coda con priorità
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CodaConPriorita :
def __init__ ( self ):
self . Elementi = []
def EVuota ( self ):
return self . Elementi == []
def Inserimento ( self , Elemento ):
self . Elementi . append ( Elemento )
def Rimozione ( self ):
Indice = 0
for i in range (1 , len ( self . Elementi )):
if self . Elementi [ i ] > self . Elementi [ Indice ]:
Indice = i
Elemento = self . Elementi [ Indice ]
self . Elementi [ Indice : Indice +1] = [ ]
return Elemento
I metodi __init__ , EVuota e Inserimento sono tutte maschere delle operazioni su liste. L’unico metodo "interessante" è Rimozione:
All’inizio di ogni iterazione Indice contiene l’indice dell’elemento con priorità massima. Ad ogni ciclo viene confrontato questo elemento con l’i-esimo elemento della
lista: se il nuovo elemento ha priorità maggiore, il valore di Indice diventa i.
Quando il ciclo for è stato completato Indice è l’indice dell’elemento con priorità
massima. Questo elemento è rimosso dalla lista e ritornato.
17
Capitolo 1. Python
1.7
File
Quando un programma è in esecuzione i suoi dati sono in memoria; nel momento
in cui il programma termina o il computer viene spento tutti i dati in memoria
vengono irrimediabilmente persi. Per conservare i dati devi quindi memorizzarli
in un file, solitamente memorizzato su hard disk, una memoria flash (pen drive o
schede di memoria) o CD-ROM.
Lavorando con un gran numero di file è logico cercare di organizzarli: questo viene
fatto inserendoli in cartelle (dette anche "folder" o "directory"). Ogni file all’interno
di una cartella è identificato da un nome unico.
Leggendo e scrivendo file i programmi possono scambiare informazioni e anche
generare documenti stampabili usando il formato PDF o altri formati simili.
Lavorare con i file è molto simile a leggere un libro: per usarli li devi prima aprire
e quando hai finito li chiudi. Mentre il libro è aperto lo puoi leggere o puoi scrivere
una nota sulle sue pagine, sapendo in ogni momento dove ti trovi al suo interno.
La maggior parte delle volte leggerai il libro in ordine, ma nulla ti vieta di saltare
a determinate pagine facendo uso dell’indice.
Questa metafora può essere applicata ai file. Per aprire un file devi specificarne il
nome e l’uso che intendi farne (lettura o scrittura).
L’apertura del file crea un oggetto file: nell’esempio che segue useremo la variabile
f per riferirci all’oggetto file appena creato.
1
2
3
f = open (" test . dat " ," w ")
print f
< open file ’ test . dat ’ , mode ’w ’ at fe820 >
La funzione open prende due argomenti: il primo è il nome del file ed il secondo
il suo "modo". Il modo "w" significa che stiamo aprendo il file in scrittura ("write").
Nel caso non dovesse esistere un file chiamato test.dat l’apertura in scrittura farà
in modo di crearlo vuoto. Nel caso dovesse già esistere, la vecchia copia verrà
rimpiazzata da quella nuova e definitivamente persa.
18
Capitolo 1. Python
Quando stampiamo l’oggetto file possiamo leggere il nome del file aperto, il modo
e la posizione dell’oggetto in memoria.
Per inserire dati nel file invochiamo il metodo write:
1
2
--- f . write (" Adesso ")
--- f . write (" chiudi il file ")
La chiusura del file avvisa il sistema che abbiamo concluso la scrittura e rende
il file disponibile alla lettura:
1
--- f . close ()
Solo dopo aver chiuso il file possiamo riaprirlo in lettura e leggerne il contenuto.
Questa volta l’argomento di modo è "r":
1
--- f = open (" test . dat " ," r ")
Se cerchiamo di aprire un file che non esiste otteniamo un errore:
1
2
--- f = open (" test . cat " ," r ")
IOError : [ Errno 2] No such file or directory : ’ test . cat ’
Il metodo read legge dati da un file. Senza argomenti legge l’intero contenuto
del file:
1
2
3
--- Testo = f . read ()
--- print Testo
Adesso chiudi il file
"read" accetta anche un argomento che specifica quanti caratteri leggere:
1
2
3
--- f = open (" test . dat " ," r ")
--- print f . read (5)
Adess
Se non ci sono caratteri sufficienti nel file, read ritorna quelli effettivamente
disponibili. Quando abbiamo raggiunto la fine del file read ritorna una stringa
vuota:
19
Capitolo 1. Python
1
2
3
4
--- print f . read (1000006)
o chiudi il file
--- print f . read ()
--La funzione che segue, copia un file leggendo e scrivendo fino a 50 caratteri per
volta. Il primo argomento è il nome del file originale, il secondo quello della copia:
1
2
3
4
5
6
7
8
9
10
11
def CopiaFile ( Originale , Copia ):
f1 = open ( Originale , " r ")
f2 = open ( Copia , " w ")
while 1:
Testo = f1 . read (50)
if Testo == "":
break
f2 . write ( Testo )
f1 . close ()
f2 . close ()
return
L’istruzione break come già accennata nel paragrafo "Ciclo: For e While" interrompe immediatamente il loop saltando alla prima istruzione che lo segue (in
questo caso f1.close()).
Il ciclo while dell’esempio è apparentemente infinito dato che la sua condizione
ha valore 1 ed è quindi sempre vera. L’unico modo per uscire da questo ciclo è
di eseguire un break che viene invocato quando Testo è una stringa vuota e cioè
quando abbiamo raggiunto la fine del file in lettura.
20
Capitolo 2
Programmazione Distribuita
Nell’ambito delle applicazioni informatiche si è soliti distinguere tra:
1. Architetture locali: tutti i componenti sono sulla stessa macchina.
2. Architetture distribuite: le applicazioni e i componenti possono risiedere su
nodi diversi messi in comunicazione da una rete.
I vantaggi della seconda alternativa consistono principalmente nella possibilità di
uso concorrente dei programmi, nella centralizzazione dei dati, nella distribuzione
del carico elaborativo, il tutto al prezzo di una maggiore complessità specialmente
riguardo alla comunicazione fra i vari compenenti.
Le applicazioni distribuite si possono classificare in base al «grado» di distribuzione:
1. Applicazioni client-server:
sono presenti solo due livelli e le operazioni sono svolte quasi interamente sul
servente. Come esempio possiamo citare i classici siti Web statici o dinamici;
gli strumenti per la realizzazione di tale tipo di applicazioni sono i socket di
rete, la cui programmazione è possibile in vari linguaggi, tra cui C, C++,
Java, Python.
21
Capitolo 2. Programmazione Distribuita
Figura 2.1: Modello Logico Client-Server
2. Applicazioni multi-livello:
si ha un numero maggiore di livelli, allo scopo, soprattutto, di alleggerire il
carico elaborativo dei serventi.
Quelle che vengono suddivise infatti sono le funzionalità del lato servente,
lasciando sostanzialmente invariate le caratteristiche della parte cliente che
ha il compito di ospitare l’interfaccia dell’applicazione.
Un esempio di tale tipo di architettura è quello del modello three-tier avente
una struttura suddivisa in tre strati o livelli:
(a) front-end o presentation tier o interfaccia;
(b) middle tier o logica applicativa;
(c) back-end o data tier o gestione dati persistenti.
Questa nomenclatura è tipica delle applicazioni Web; più in generale si può
fare riferimento ad una suddivisione in tre livelli, applicabile a qualsiasi
applicazione software, che è la seguente:
(a) PL (Presentation Layer):
è la parte di visualizzazione dei dati (moduli, «controlli» di input, ecc.)
necessari per l’interfaccia utente;
(b) BLL (Business Logic Layer):
è la parte principale dell’applicazione, che definisce le varie entità e le
loro relazioni indipendentemente dalle modalità di presentazione all’utente e di salvataggio negli archivi;
22
Capitolo 2. Programmazione Distribuita
(c) DAL (Data Access Layer):
contiene tutto il necessario alla gestione dei dati persistenti (sostanzialmente sistemi di gestione di basi di dati).
Il numero di livelli può essere anche maggiore di tre, allo scopo di suddividere
ulteriormente i compiti dei vari strati; in questo caso si parla di applicazioni
multi-tier o n-tier.
2.1
Applicazioni Client-Server
Il termine "sistema client-server" (letteralmente cliente-serviente) indica un’architettura di rete nella quale genericamente un computer client o terminale client si
connette ad un server per la fruizione di un certo servizio, quale ad esempio la
condivisione di una certa risorsa hardware/software con altri client, appoggiandosi
alla sottostante architettura protocollare.
Più semplicemente, i sistemi client/server sono un’evoluzione dei sistemi basati
sulla condivisione semplice delle risorse: la presenza di un server permette ad un
certo numero di client di condividerne le risorse, lasciando che sia il server a gestire
gli accessi alle risorse per evitare conflitti di utilizzazione tipici dei primi sistemi
informatici.
2.1.1
Client
Il software client [3] in genere è di limitata complessità, limitandosi normalmente
ad operare come interfaccia verso il server. In generale nel campo informatico il
termine client indica una componente che accede ai servizi o alle risorse di un’altra
componente, detta server. In questo contesto si può quindi parlare di client riferendosi all’hardware o al software.
Un computer collegato ad un server tramite rete locale o geografica, ed al quale
richiede uno o più servizi, utilizzando uno o più protocolli di rete è un esempio di
23
Capitolo 2. Programmazione Distribuita
client hardware.
Un programma di posta elettronica è un esempio di client software.
Sono sempre di più i software, come il web, l’e-mail, i database, che sono divisi
in una parte client (residente ed in esecuzione sul pc client) ed una parte server
(residente ed in esecuzione sul server). Il termine client indica anche il software
usato sul computer client per accedere alle funzionalità offerte dal server.
Ad esempio, nel web il software client è il web browser, e parla con un server web
attraverso il protocollo HTTP; per l’e-mail il client è detto in gergo mail user
agent o MUA (ad esempio, Outlook, Mozilla Thunderbird, Eudora, ...), e parla
con il server (Mail Transfer Agent o MTA) attraverso i protocolli SMTP e POP
o IMAP; il client per la consultazione o la modifica del database (spesso costituito
da librerie software utilizzate da un’applicazione) parla con il DBMS, che gestisce
il database e risponde alle interrogazioni del client.
2.1.2
Server
l software server, oltre alla gestione logica del sistema, deve implementare tutte le
tecniche di gestione degli accessi, allocazione e rilascio delle risorse, condivisione e
sicurezza dei dati o delle risorse.
Ad esempio un server di posta elettronica è paragonabile ad un qualunque ufficio
postale. Gli utilizzatori per accedere via client alla loro cassetta di posta elettronica devono esser stati autorizzati. In modo analogo un utente deve possedere la
chiave della cassetta sita presso un ufficio postale dalla quale vuole prelevare la
corrispondenza.
2.1.3
Interazione Client-Server
Quando un computer client si connette direttamente ad un sistema di database o
ad una server application standard, questa viene chiamata 2-tier architecture (architettura a 2 livelli).
24
Capitolo 2. Programmazione Distribuita
Recentemente, è più usuale per computer client, chiamati thin client che non incorporano business logic, ma solo elementi di interfaccia, connettersi ad una server
application che implementa una business logic nella quale transitivamente (ossia
successivamente) comunica con il database del server, il quale memorizza i dati
utilizzati dall’applicazione. Tale architettura è chiamata 3-tier architecture (architettura a 3 livelli).
In generale architetture ad n-livelli possono impiegare un certo numero di servizi distinti, comprese relazioni transitive tra application server che implementano
differenti funzioni di business logic, ognuna delle quali può impiegare o meno un
sistema di database condiviso o distinto.
2.2
Architettura Client-Server
Vantaggi dell’architettura client-server
1. Il carico computazionale viene ripartito tra due piattaforme in genere distinte. Ognuna di esse può essere ottimizzata per i compiti a cui è destinata:
gestione di basi di dati, gestione di documenti, calcolo numerico, gestione di
strumentazione, interfaccia utente.
2. L’architettura si presta in modo naturale alla condivisione delle risorse: le
risorse gestite dal server sono accessibili in teoria da qualunque nodo di Internet; se il server è realizzato in modo concorrente, l’accesso può avvenire
da più nodi simultaneamente.
3. Client e server possono:
(a) essere sviluppati su piattaforme (cioè sistemi operativi) diversi; tutti i
moderni sistemi operativi supportano infatti il protocollo TCP/IP;
25
Capitolo 2. Programmazione Distribuita
(b) essere sviluppati con linguaggi di programmazione diversi; tutti i moderni linguaggi di programmazione supportano infatti il protocollo TCP/IP;
(c) essere sviluppati da programmatori diversi, creando così una modalità
naturale di ripartizione del lavoro in un team.
Svantaggi dell’architettura client-server
1. Maggior complessità del sistema.
2. Dipendenza da una connessione di rete efficace.
3. Procedura di sviluppo un po’ più complicata.
2.2.1
Architettura client-server TCP/IP
La connessione TCP/IP stabilisce un collegamento punto-punto tra due applicazioni. Gli estremi di questo collegamento sono contrassegnati da un indirizzo IP,
che identifica la workstation e da un numero di porta, che rende possibile la coesistenza, sulla stessa workstation, di più connessioni, facenti capo ad applicazioni
indipendenti. Le applicazioni che utilizzano il protocollo TCP/IP in un contesto
client-server non solo devono conoscere ciascuna l’indirizzo IP e il numero di porta
dell’altra, ma devono anche condividere il protocollo dell’applicazione, che pertanto deve essere stato preventivamente definito.
Una volta stabilita la connessione e il protocollo cui scambiare dati su di essa, il
sottostante protocollo TCP/IP si incarica di far arrivare questi dati, suddivisi in
pacchetti, da un estremo all’altro del collegamento. In particolare, il protocollo
TCP si occupa di assemblare e disassemblare i pacchetti e di gestire l’handshaking
che garantisce l’affidabilità della connnessione, mentre il protocollo IP si occupa
del trasporto dei singoli pacchetti e della scelta del miglior instradamento degli
stessi lungo la rete. Questo meccanismo è alla base della robustezza del protocollo
26
Capitolo 2. Programmazione Distribuita
TCP/IP nel suo insieme, che a sua volta rappresenta una delle motivazioni dello
sviluppo del protocollo stesso in ambito militare (ARPAnet).
Le varie applicazioni standard esistenti (navigazione Web, trasferimento file, posta
elettronica e molte altre) utilizzano protocolli di applicazione standardizzati (http,
ftp, pop3, imap, smtp etc.). Ogni applicazione client-server specifica deve invece definire ed applicare il proprio protocollo di applicazione proprietario. Questo
può prevedere lo scambio di dati in blocchi di dimensioni fisse (è la soluzione più
semplice).
2.3
Socket
Un socket[5] è oggetto software che permette l’invio e la ricezione di dati, tra host
remoti (tramite una rete) o tra processi locali (Inter-Process Communication).
Data una qualsiasi piattaforma, è probabile che ci siano altre forme di IPC più
veloci, ma per la comunicazione tra piattaforme diverse i socket sono quasi una
scelta obbligata.
Furono inventati a Berkeley come parte dello Unix BSD. Si diffusero assai rapidamente con Internet. Per buone ragioni la combinazione dei socket con INET rende
la comunicazione con macchine di qualunque tipo sparse qua e là per il mondo incredibilmente facile (almeno se comparata con gli altri sistemi). Più precisamente,
il concetto di socket si basa sul modello Input/Output su file di Unix, quindi sulle
operazioni di open, read, write e close; l’utilizzo, infatti, avviene secondo le
stesse modalità, aggiungendo i parametri utili alla comunicazione, quali indirizzi,
numeri di porta e protocolli.
Socket locali e remoti in comunicazione, formano una coppia (pair), composta da
indirizzo e porta di client e server.
Solitamente i sistemi operativi forniscono delle API per permettere alle applicazioni di controllare e utilizzare i socket di rete.
27
Capitolo 2. Programmazione Distribuita
I tipi di protocolli utilizzati dal socket, ne definiscono la famiglia (o dominio).
Possiamo distinguere, ad esempio, due importanti famiglie:
1. AF_INET : comunicazione tra host remoti, tramite Internet;
2. AF_UNIX : comunicazione tra processi locali, su macchine Unix. (Questa
famiglia è anche chiamata Unix Domain Socket).
2.4
Tipi di Socket
All’interno della famiglia possiamo distinguere il tipo di socket, a seconda della
modalità di connessione. Abbiamo:
1. Stream socket: orientati alla connessione (connection-oriented), basati su
protocolli affidabili come TCP o SCTP;
2. Datagram socket: non orientati alla connessione (connectionless), basati
sul protocollo veloce ma inaffidabile UDP;
3. Raw socket (raw IP): il livello di trasporto viene bypassato, e l’header è
accessibile al livello applicativo.
2.4.1
Stream Socket
Vedremo più nel particolare solo questa tipologia di Socket.
Essendo basati su protocolli a livello di trasporto come TCP, garantiscono una
comunicazione affidabile, full-duplex, orientata alla connessione, e con un flusso di
byte di lunghezza variabile.
La comunicazione mediante questo socket, si compone di queste fasi:
1. – Creazione dei socket
Client e server creano i loro rispettivi socket, e il server lo pone in ascolto su
una porta.
28
Capitolo 2. Programmazione Distribuita
Dato che il server può creare più connessioni con client diversi (ma anche con
lo stesso), ha bisogno di una coda per gestire le varie richieste.
2. – Richiesta di connessione
Il client effettua una richiesta di connessione verso il server. Da notare che
possiamo avere due numeri di porta diversi, perchè una potrebbe essere dedicata solo al traffico in uscita, l’altra solo in entrata; questo dipende dalla
configurazione dell’host. In sostanza, non è detto che la porta locale del client
coincida con quella remota del server.
Il server riceve la richiesta e, nel caso in cui sia accettata, viene creata una
nuova connessione.
3. – Comunicazione
Ora client e server comunicano attraverso un canale virtuale, tra il socket del
primo, ed uno nuovo del server, creato appositamente per il flusso dei dati di
questa connessione: data socket. In fede di quanto accennato nella prima
fase, il server crea il data socket perchè il primo serve esclusivamente alla
gestione delle richieste.
È possibile, quindi, che ci siano molti client a comunicare con il server,
ciascuno verso il data socket creato dal server appositamente per loro.
4. – Chiusura della connessione
Essendo il TCP un protocollo orientato alla connessione, quando non si ha più
la necessità di comunicare, il client lo comunica al server, che ne deistanzia
il data socket. La connessione viene così chiusa.
29
Capitolo 3
Programmazione in Python di
un’applicazione Client-Server
Questo capitolo tratterà in modo dettagliato le applicazioni client-server in Python, linguaggio di programmazione analizzato nel capitolo 1.
Utilizzeremo un protocollo di comunicazione TCP/IP tra Client e Server.
Vedremo prima un’applicazione che tratterà la comunicazione semplice tra un client
ed un server con uno scambio di messaggi e successivamente, in maniera più dettagliata l’invio di un file da parte del server, scelto dal client.
30
Capitolo 3. Programmazione in Python di un’applicazione Client-Server
3.1
Primo Esempio
Vediamo un primo esempio in cui il server si mette in ascolta su una porta predefinita ed in seguito ad una connessione TCP/IP con il client, lo stesso client invia
la data e l’ora relativa alla connessione effettuata.
Server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# server . py
import socket
import time
# create a socket object
serversocket = socket . socket (
socket . AF_INET , socket . SOCK_STREAM )
# get local machine name
host = socket . gethostname ()
port = 9999
# bind to the port
serversocket . bind (( host , port ))
# queue up to 5 requests
serversocket . listen (5)
while 1:
# establish a connection
clientsocket , addr = serversocket . accept ()
print (" Connessione con [ addr ] ,[ port ] % s " % str ( addr ))
currentTime = time . ctime ( time . time ()) + "\ r \ n "
clientsocket . send ( currentTime . encode ( ’ ascii ’))
clientsocket . close ()
1. socket.socket(): Crea un nuovo socket utilizzando il dato indirizzo, il tipo
di socket ed il numero di protocollo.
2. socket.bind(address): Associa il socket all’indirizzo.
3. socket.listen(backlog): Ascolta le connessioni fatte al socket. L’argomento("backlog") specifica il numero massimo di connessioni in coda che
saranno almeno 0; il massimo valore dipende dal sistema (di solito è 5), il
minimo valore è sempre 0.
4. socket.accept(): Il valore di ritorno è una coppia (conn, address) dove
conn è un nuovo oggetto socket che serve a mandare e ricevere dati, e address è l’indirizzo legato al socket.
31
Capitolo 3. Programmazione in Python di un’applicazione Client-Server
Una volta accettato, un nuovo socket viene creato ed avrà un proprio indentificativo. Questo nuovo socket è utilizzato unicamente con questo particolare
client.
5. socket.send(bytes[, flags]): Invia dati al socket. Il socket deve essere
connesso al socket. Ritorna il numero di bytes inviati.
6. socket.close(): Indica la chiusura del socket. Tutte le operazioni successive sul socket falliranno.I socket sono automaticamente chiusi quando vengono
rifiutati, ma è sempre raccomandato chiuderli con l’operazione close().
Client.py
1
2
3
4
5
6
7
8
9
10
11
12
13
# client . py
import socket
# create a socket object
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
# get local machine name
host = socket . gethostname ()
port = 9999
# connection to hostname on the port .
s . connect (( host , port ))
# Receive no more than 1024 bytes
tm = s . recv (1024)
s . close ()
print (" Time connection server : % s " % tm . decode ( ’ ascii ’))
Output
1
2
$ python server . py &
Connessione con [ addr ] ,[ port ] ( ’127.0.0.1 ’ , 54597)
3
4
5
$ python client . py
Time connection server is Wed Apr 20 16:32:15 2016
32
Capitolo 3. Programmazione in Python di un’applicazione Client-Server
3.2
Trasferimento di File
Si veda un esempio di applicazione Client-Server con l’utilizzo dei file[6].
—Server.py
1
# server . py
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import socket
port = 60000
s = socket . socket ()
host = socket . gethostname ()
s . bind (( host , port ))
s . listen (15)
print ( ’ Server listening .... ’)
while True :
conn , addr = s . accept ()
print ( ’ Got connection from ’ , addr )
data = conn . recv (1024)
print ( ’ Server received ’ , repr ( data . decode ()))
filename = ’ mytext . txt ’
f = open ( filename , ’ rb ’)
l = f . read (1024)
while ( l ):
conn . send ( l )
print ( ’ Sent ’, repr ( l . decode ()))
l = f . read (1024)
f . close ()
print ( ’ Done sending ’)
conn . send ( ’ - > Thank you for connecting ’. encode ())
conn . close ()
—Output Server
1
2
3
Microsoft Windows [ Versione 6.1.7601]
Copyright ( c ) 2009 Microsoft Corporation .
Tutti i diritti riservati .
4
5
6
7
8
9
10
C :\ Users \ Mickael Di Carluccio >" C :\ server . py "
Server listening ....
Got connection from ( ’192.168.1.11 ’ , 62793)
Server received ’ Hello Server ! ’
Sent ’ ciao a tutti ’
Done sending
33
Capitolo 3. Programmazione in Python di un’applicazione Client-Server
–-Client.py
1
# client . py
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import socket
s = socket . socket ()
host = socket . gethostname ()
port = 60000
s . connect (( host , port ))
s . send ( ’ Hello Server ! ’. encode ())
with open ( ’ received . txt ’ , ’wb ’) as f :
print ( ’ file opened ’)
while True :
print ( ’ receiving data ... ’)
data = s . recv (1024)
if not data :
break
print ( ’ Data = > ’, data . decode ())
# write data to a file
f . write ( data )
f . close ()
print ( ’ Successfully get the file ’)
s . close ()
print ( ’ connection closed ’)
—Output Client
1
2
3
Microsoft Windows [ Versione 6.1.7601]
Copyright ( c ) 2009 Microsoft Corporation .
Tutti i diritti riservati .
4
5
6
7
8
9
10
11
12
13
C :\ Users \ Mickael Di Carluccio >" C :\ client . py "
file opened
receiving data ...
Data = > ciao a tutti
receiving data ...
Data = > -> Thank you for connecting
receiving data ...
Successfully get the file
connection closed
14
15
C :\ Users \ Mickael Di Carluccio >
34
Conclusioni
In questo elaborato abbiamo esaminato il modo con cui è possibile programmare
un’applicazione Client/Server in linguaggio Python.
L’obiettivo prefissato prevedeva la programmazione in linguaggio Python, di un’applicazione Client/Server in grado di aprire un file, copiare il contenuto al suo interno
e di scriverlo in un nuovo file, salvandolo.
Dapprima, però, abbiamo discusso delle potenzialità che questo linguaggio interpretato ci fornisce, descrivendo i suoi vantaggi e svantaggi, di come vengono dichiarate le strutture dati e di come, proprio per il conseguimento dell’obiettivo, di
come vengono trattati i file.
Essendo un’applicazione Client/Server, abbiamo discusso anche di quelle che sono
le applicazioni distribuite, descrivendone struttura e architettura. Ma non solo.
Abbiamo discusso di come è stato possibile, far comunicare un applicativo Client,
con un Server, secondo i vari protocolli di rete, come il TCP/IP e l’UDP.
La tesi tratta argomenti davvero interessanti soprattutto per chi, come me, si è
affacciato per la prima volta al mondo Python, restandone davvero sorpreso e meravigliato. Tra i numerosi vantaggi, quelli che più mi hanno colpito sono: non
prevedono parenti graffe, ma si basa solo sulla corretta indentazione delle righe di
codice, ma anche e soprattutto dal fatto che non esiste un vero e proprio compilatore del Python, infatti, con un semplice editor di testo è possibile scrivere righe
di codice per poi verificarle con un Prompt dei comandi.
Quest’ultima valutazione, considerata da me un vantaggio, fa sì che in qualunque
35
Capitolo 3. Programmazione in Python di un’applicazione Client-Server
momento ed in qualunque luogo, in presenza di un pc, è possibile "buttare giù"
righe di codice. Tra le società più importanti che usano Python abbiamo:
1. Google (molti componenti del motore di ricerca)
2. Industrial Light+Magic (Star Wars Episode II...)
3. Infoseek (search engine)
4. Yahoo! Groups (mailing list manager)
5. Nasa (vari utilizzi, tra cui strumenti per il pre-mission planning dello Space
Shuttle)
6. IBM (test automatici di strumenti internet)
7. Blender 3D (scripting language).
Ciò fa capire la potenza di Python che viene scelto da numerose aziende a discapito
di tanti altri linguaggi di programmazione.
36
Ringraziamenti
Ringrazio per aver reso possibile il conseguimento della mia laurea, innanzitutto i
miei genitori, che mi hanno sempre incitato ed esortato a non mollare quando le
cose non andavano per il verso giusto.
Ringrazio poi, i miei colleghi, Filippo Masi e Francesco Apuzzo per aver condiviso
insieme il percorso di studi che ci ha accompagnati fino al termine degli esami, ringrazio Francesco soprattutto per il tempo dedicatomi, nonostante ne avesse poco
lui stesso.
Ringrazio i miei compagni di vita, Davide D’Ambrosio, Andrea Esposito, Ernesto
Borruto e tutti gli amici della palestra che hanno seguito il mio cammino verso la
laurea.
37
Bibliografia
[1] http://www.python.it/doc/articoli/PyDbAPI2_0.html
[2] http://linuxdidattica.org/docs/altre_scuole/msm_p/txs_01.html#id6
[3] https://it.wikipedia.org/wiki/Sistema_client/server
[4] "Pensare da informatico - Imparare con Python" Allen Downey, Jeffrey Elkner,
Chris Meyers
[5] http://fortyzone.it/socket-cosa-sono/
[6] http://www.bogotobogo.com/python/python_network_programming_server_client.php
38