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.
| [timeout[, use_poll[, map]]]) |
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.
| ) |
Due attributi di classe che possono essere modificati, per aumentare le prestazioni oppure per conservare la memoria.
4096).
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:
| ) |
| ) |
def handle_write(self):
sent = self.send(self.buffer)
self.buffer = self.buffer[sent:]
| ) |
| ) |
| ) |
| ) |
| ) |
| ) |
True, indicando che,
per definizione, tutti i canali verranno interessati all'evento di
lettura.
| ) |
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.
| family, type) |
| address) |
| data) |
| buffer_size) |
| backlog) |
1; il numero massimo applicabile è dipendente dal
sistema (solitamente 5).
| address) |
| ) |
(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.
| ) |