11.15 SocketServer -- Un'infrastruttura per i server di rete

Il modulo SocketServer semplifica il compito di scrivere server di rete.

Esistono quattro classi di base per i server: TCPServer, che utilizza il protocollo Internet TCP, che fornisce un flusso continuo di dati tra il client e il server. UDPServer, che utilizza i datagrammi, che sono pacchetti discreti di informazioni che possono arrivare fuori ordine o possono essere persi durante la trasmissione. I meno utilizzati UnixStreamServer e UnixDatagramServer, che sono simili, ma utilizzano i socket domain di Unix; non sono disponibili su piattaforme non Unix. Per maggiori dettagli sulla programmazione di rete, consultare un testo come lo UNIX Network Programming di W.Richard Steven o il Win32 Network Programming di Ralph Davis.

Queste quattro classi elaborano richieste in modalità sincrona; ogni richiesta deve essere completata prima che la richiesta successiva possa iniziare. Questo non è adatto se ogni richiesta richiede molto tempo per essere completata, perché neccessita di parecchio calcolo, o perché restituisce molti dati ed il client è lento ad elaborarli. La soluzione è quella di creare un processo separato o un thread per gestire ogni richiesta; Le classi mix-in ForkingMixIn e ThreadingMixIn possono essere usate per supportare compiti asincroni.

Creare un server richiede diversi passaggi. Per prima cosa, si deve creare una classe handler (Ndt. gestore) di richiesta derivandola da BaseRequestHandler e sovrascrivendo il metodo handle(); questo metodo elaborerà le richieste in ingresso. Secondo, si devono istanziare una o più classi di server, passandogli l'indirizzo del server e le classi degli handler di richiesta. Infine, si deve chiamare il metodo handle_request() o serve_forever() dell'oggetto server per elaborare una o molte richieste.

Quando si eredita da ThreadingMixIn per avere delle connessioni in thread, si deve esplicitamente dichiarare come si vuole il proprio thread per evitare di ottenere una brusca interruzione. La classe ThreadingMixIn definisce un attributo daemon_threads, che indica se il server deve attendere un'interruzione del thread oppure no. Si deve impostare l'opzione esplicitamente se si vuole che i thread siano autonomi; il valore predefinito è False, che significa che Python non uscirà finché tutti i thread creati da ThreadingMixIn non siano terminati.

Le classi server hanno gli stessi metodi ed attributi esterni, indipendentemente dal protocollo che utilizzano.

fileno( )
Restituisce un descrittore file intero per il socket su cui il server è in ascolto. La funzione è abitualmente passata a select.select(), per consentire il monitoraggio di server multipli nello stesso processo.

handle_request( )
Elabora una richiesta singola. Questa funzione chiama i seguenti metodi nell'ordine: get_request(), verify_request() e process_request(). Se il metodo indicato, fornito dall'utente handle() della classe handler solleva un'eccezione, verrà chiamato il metodo handle_error() del server.

serve_forever( )
Gestisce un infinito numero di richieste. Questo semplicemente chiama handle_request() all'interno di un ciclo infinito.

address_family
La famiglia di protocolli a cui il socket del server appartiene. socket.AF_INET e socket.AF_UNIX sono due possibili valori.

RequestHandlerClass
la classe handler fornita dall'utente; un'istanza di questa classe viene creata per ogni richiesta.

server_address
L'indirizzo su cui il server è in ascolto. Il formato degli indirizzi varia in base alla famiglia del protocollo; vedere la documentazione del modulo socket per i dettagli. Per i protocolli internet, questo è una tupla contenente una stringa indicante l'indirizzo ed il numero di porta come intero: per esempio: ('127.0.0.1', 80).

socket
L'oggetto socket su cui il server sarà in ascolto per le richieste in arrivo.

La classe server supporta le seguenti variabili di classe:

allow_reuse_address
Quando il server consente di riutilizzare un indirizzo. Il suo valore predefinito è False e può essere impostato in una sotto classe per cambiare la policy.

request_queue_size
La dimensione della coda di richiesta. Se il server richiede parecchio tempo per elaborare una singola richiesta, ogni richiesta che arriva quando il server è occupato viene inserita in una coda, fino al raggiungimento di request_queue_size. Una volta che la coda è piena, successive richieste dal client riceveranno un errore ``Connection denied'' (Ndt. Connessione negata). Il valore predefinito tipicamente è 5, ma questo può essere sovrascritto con una sotto classe.

socket_type
Il tipo di socket usato dal server; socket.SOCK_STREAM e socket.SOCK_DGRAM sono due possibili valori.

Ci sono diversi metodi della classe server che possono essere sovrascritti da sotto classi delle classi server come TCPServer; questi metodi non sono utili per le utenze esterne all'oggetto server.

finish_request( )
Attualmente elabora la richiesta istanziando RequestHandlerClass e chiamandone il metodo handle().

get_request( )
Deve accettare una richiesta dal socket, e restituire una tupla di due elementi contenente il nuovo oggetto socket da usare per comunicare con il client e l'indirizzo del client.

handle_error( request, client_address)
La funzione viene chiamata se il metodo handle() di RequestHandlerClass solleva un'eccezione. L'azione predefinita è stampare la traceback sullo standard output e continuare a gestire le richieste successive.

process_request( request, client_address)
Chiama finish_request() per creare un'istanza di RequestHandlerClass. Se lo si desidera, questa funzione può creare un nuovo processo o un nuovo thread per gestire la richiesta; le classi ForkingMixIn e ThreadingMixIn fanno questo.

server_activate( )
Chiamato dal costruttore del server per attivare il server. Può essere sovrascritto.

server_bind( )
Chiamato dal costruttore del server per gestire il socket all'indirizzo desiderato. Può essere sovrascritto.

verify_request( request, client_address)
Deve restituire un valore Booleano; se il valore è vero, la richiesta verrà elaborata, e se è falsa, la richiesta verrà negata. La funzione può essere sovrascritta per implementare il controllo degli accessi al server. L'implementazione predefinita restituisce sempre vero.

La classe handler di richiesta deve definire un nuovo metodo handle() e può sovrascrivere uno dei seguenti metodi. Una nuova istanza viene creata per ogni richiesta.

finish( )
Chiamato dopo il metodo handle() per eseguire ogni azione di pulizia richiesta. L'implementazione predefinita non fa nulla. Se setup() o handle() sollevano un'eccezione, questa funzione non verrà chiamata.

handle( )
Questa funzione deve fare tutto il lavoro richiesto per servire una richiesta. Molti attributi dell'istanza sono disponibili; la richiesta è disponibile come self.request; l'indirizzo del client è self.client_address; e l'istanza del server come self.server, in caso avesse bisogno di accedere ad informazioni del server.

Il tipo di self.request è differente per i datagrammi o flussi di servizi. Per servizi di flussi, self.request è un oggetto socket; per i servizi di datagrammi, self.request è una stringa. Comunque, questo può essere nascosto usando le classi handler di richiesta mix-in StreamRequestHandler o DatagramRequestHandler, che sovrascrivono i metodi setup() e finish(), e forniscono gli attributi self.rfile e self.wfile. self.rfile e self.wfile possono essere letti o scritti, rispettivamente, per acquisire i dati richiesti o restituirli al client.

setup( )
Chiamato prima del metodo handle() per eseguire ogni richiesta della sequenza di inizializzazione. L'implementazione predefinita non fa nulla.
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.