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.
) |