11.23 asyncore -- Gestione di socket asincroni

Questo modulo fornisce l'infrastruttura di base per la scrittura di servizi client e server con socket asincroni.

Ci sono solo due sistemi per avere un programma che su di un singolo processore faccia ``più di una cosa nello stesso tempo''. La programmazione multithread è il più semplice ed il più popolare metodo per fare ciò, ma esiste anche un'altra tecnica, che consente di avere praticamente tutti i vantaggi del multithreading, senza utilizzare thread multipli. È applicabile praticamente solo se il vostro programma richiede un I/O massiccio. Se il programma richiede solo calcolo, la schedulazione di thread preemptive è probabilmente la via migliore. I server di rete, comunque, raramente fanno un uso pesante del processore.

Se il vostro sistema operativo supporta nella propria libreria di I/O la chiamata di sistema select() (e tutto quanto ne consegue), la si può utilizzare per gestire canali multipli di comunicazione immediati; svolgendo altri lavori mentre il vostro I/O sta procedendo in ``background''. Curiosamente, questa strategia può sembrare strana e complessa, specialmente all'inizio, in realtà è sotto molti aspetti facile da capire e da controllare, contrariamente alla programmazione multithread. Il modulo asyncore risolve molti dei problemi difficili, svolgendo il compito di costruire server e client di rete ad alte e sofisticate prestazioni in un attimo. Per applicazioni e protocolli di ``conversazione'' il modulo di supporto asynchat è insostituibile.

L'idea di base dietro ad entrambi i moduli è di creare uno o più canali di rete, istanze delle classi asyncore.dispatcher e asynchat.async_chat. La creazione dei canali li aggiunge ad una mappa globale, usata dalla funzione loop() nel caso non si fornisca una propria mappa.

Una volta che il canale o i canali è/sono creato/i, la chiamata alla funzione loop() attiva il servizio del canale, che continua finché l'ultimo canale (includendo tutti quelli aggiunti alla mappa durante i servizi asincroni) non viene chiuso.

loop( [timeout[, use_poll[, map]]])
Inserisce un ciclo di controllo che termina solo dopo che tutti i canali sono stati chiusi. Tutti gli argomenti sono facoltativi. L'argomento timeout imposta il parametro del tempo limite per le chiamate select() o poll() appropriate, misurato in secondi; il valore predefinito è 30 secondi. Il parametro use_poll, se vero, indica che poll() deve essere usato come preferenza rispetto a select() (il valore predefinito è False). Il parametro map è un dizionario i cui elementi sono i canali da controllare. Alla chiusura dei canali, questi vengono rimossi da map. Se map viene omessa, viene utilizzata una mappa globale (il metodo di classe predefinito __init__() aggiorna questa mappa - ci si deve assicurare di estendere __init__() piuttosto che sovrascriverlo se si vuole mantenere questo comportamento).

I canali (istanze di asyncore.dispatcher, asynchat.async_chat e relative sotto classi) possono essere liberamente inserite nella mappa.

class dispatcher( )
La classe dispatcher è un involucro leggero intorno all'oggetto socket di basso livello. Per renderlo più efficace, possiede una serie di metodi per gestire gli eventi che vengono chiamati dal ciclo asincrono. Altrimenti, può essere trattato come un normale oggetto socket non bloccante.

Due attributi di classe che possono essere modificati, per aumentare le prestazioni oppure per conservare la memoria.

ac_in_buffer_size
La dimensione del buffer di input asincrono (il suo valore predefinito è 4096).

ac_out_buffer_size
La dimensione del buffer di output asincrono (il suo valore predefinito è 4096).

Lo scopo degli eventi di basso livello in momenti precisi o in particolari stati della connessione dicono al ciclo asincrono che determinate azioni di alto livello devono prendere il via. Per esempio, se si è chiesto ad un socket di connettersi a un altro host, si sa che la connessione è stata stabilita quando il socket diviene scrivibile per la prima volta (a questo punto si è in grado di scrivere con la certezza di avere successo). Gli eventi di alto livello coinvolti sono:

Evento  Descrizione 
handle_connect() Coinvolto dal primo evento di scrittura
handle_close() Coinvolto da un evento di lettura senza che ci siano dati disponibili
handle_accept() Coinvolto da un evento di lettura su di un socket in ascolto

Durante un processo asincrono, ogni metodo readable() e writable() dei canali mappati viene usato per determinare se il socket del canale deve essere aggiunto alla lista dei canali selezionati (select()) o interrogati (poll()) per eventi di lettura e scrittura.

Comunque, l'insieme degli eventi dei canali è maggiore degli eventi di base del socket. L'intero insieme dei metodi che possono essere sovrascritti da nuove classi sono:

handle_read( )
Chiamato quando il ciclo asincrono registra una chiamata a read() sul socket del canale.

handle_write( )
Chiamato quando il ciclo asincrono rileva che un socket scrivibile può essere scritto. Spesso questo metodo implementa il necessario buffer per migliorare le prestazioni. Per esempio:

def handle_write(self):
    sent = self.send(self.buffer)
    self.buffer = self.buffer[sent:]

handle_expt( )
Chiamato quando si è oltre il limite (OOB (NdT: out of band - fuori banda)) dei dati per una connessione su socket. Questo non dovrebbe mai accadere, ma l'OOB viene ancora supportato anche se usato raramente.

handle_connect( )
Chiamato quando il socket dell'opener attivo crea una connessione. Può inviare, per esempio, un banner di ``benvenuto'', o iniziare una negoziazione di protocollo con il punto remoto.

handle_close( )
Chiamato quando il socket viene chiuso.

handle_error( )
Chiamato quando viene sollevata un'eccezione non altrimenti gestita. La versione predefinita stampa un traceback condensato.

handle_accept( )
Chiamato sul canale in ascolto (opener passivo) quando una connessione può essere stabilita con un nuovo destinatario che ha invocato la chiamata connect() per il suo punto locale.

readable( )
Chiamato ogni volta che durante il ciclo asincrono si deve determinare se un socket di un canale debba essere aggiunto alla lista su cui gli eventi di lettura possono verificarsi. Il metodo predefinito restituisce semplicemente True, indicando che, per definizione, tutti i canali verranno interessati all'evento di lettura.

writable( )
Chiamato ogni volta che durante il ciclo asincrono si deve determinare se il socket del canale debba essere aggiunto alla lista in cui avvengono gli eventi in scrittura. Il metodo predefinito restituisce semplicemente True, indicando per definizione, che tutti i canali verranno interessati dagli eventi di scrittura.

In aggiunta, ogni canale delega o estende molti dei metodi dei socket. Molti di questi sono praticamente identici ai loro corrispondenti nei socket.

create_socket( family, type)
Questo è identico alla creazione di un normale socket e usa le stesse opzioni per la sua creazione. Ci si riferisca alla documentazione del modulo socket per le informazioni su come creare socket.

connect( address)
Come con un normale oggetto socket, address è una tupla il cui primo elemento è l'host a cui connettersi ed il secondo il numero della porta.

send( data)
Invia data al punto remoto del socket.

recv( buffer_size)
Legge tanti byte fino a buffer_size dal punto remoto del socket. Una stringa vuota indicata che il canale è stato chiuso dall'altra parte.

listen( backlog)
Ascolta le connessioni fatte sul socket. L'argomento backlog specifica il numero massimo di connessioni in coda e deve essere almeno 1; il numero massimo applicabile è dipendente dal sistema (solitamente 5).

bind( address)
Associa il socket a address. Il socket non deve già essere associato. (Il formato di address (NdT: indirizzo) dipende dalla famiglia dell'indirizzo stesso -- vedere più avanti.)

accept( )
Accetta una connessione. Il socket deve essere associato ad un'indirizzo ed essere in ascolto per le connessioni. Il valore restituito è una coppia (connessione, indirizzo) dove connessione è un nuovo oggetto socket utilizzabile per inviare e ricevere dati sulla connessione e indirizzo è l'indirizzo associato al socket all'altra estremità della connessione.

close( )
Chiude il socket. Tutte le successive operazioni sull'oggetto socket falliranno. Il punto remoto non riceverà più dati (dopo che i dati in coda verranno scaricati). I socket vengono automaticamente chiusi quando sono riciclati attraverso il meccanismo della garbage-collected.



Subsections
Vedete Circa questo documento... per informazioni su modifiche e suggerimenti.