Differences between revisions 2 and 3
Revision 2 as of 2009-04-23 15:33:06
Size: 2879
Editor: lukisi
Comment:
Revision 3 as of 2009-04-24 18:49:48
Size: 3582
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 9: Line 9:
''Nota'': asyncore.poll usa {{{time.sleep}}} per attendere il timeout specificato anche quando non ci sono oggetti su cui ascoltare. Questo sleep non è un busy wait per il sistema operativo. Però non passa lo scheduler agli altri microthread. Per questo dobbiamo richiamarlo con piccoli timeout intervallando con chiamate a {{{micro_block}}}. Vedi il [[../ModuloXtime|modulo xtime]]. ''Nota'': La funzione {{{asyncore.poll}}} è bloccante. Il parametro passato è il numero di secondi dopo i quali cui si vuole cha la chiamata faccia ritorno.
<<BR>>
Al suo interno la funzione richiama {{{time.sleep}}} oppure {{{select.select}}}, a seconda se ci sono o meno socket da controllare. In entrambi i casi queste funzioni usano chiamate di sistema che non impegnano la CPU, ma nemmeno permettono agli altri microthread di venire schedulati.
<<BR>>
Per questo dobbiamo richiamarla con piccoli timeout intervallando con chiamate a {{{micro_block}}}. Vedi anche la trattazione dell'argomento nella pagina del [[../ModuloXtime|modulo xtime]].
Line 14: Line 18:
<<BR>>
Quando si crea un socket all'interno del programma (con socket.socket(...)) viene effettivamente creato un vero socket (con stdsocket.socket(...)) e anche un microsock.stacklesssocket, che è l'oggetto restituito. Questo memorizza il vero socket e una istanza dell'oggetto microsock.dispatcher, anch'esso a conoscenza del vero socket.
<<BR>>
In seguito tutte le chiamate {{{__getattr__}}} al finto socket (cioè all'oggetto stacklesssocket ritornato) vengono inoltrate al dispatcher (esempi di queste chiamate sono s.listen, s.bind, ...) mentre le chiamate{{{ __setattr__}}} vengono memorizzate localmente. ???
<<BR>>
Nel frattempo, avendo istanziato un dispatcher, che è una derivata di {{{asyncore.dispatcher}}}, abbiamo anche fatto altro:
 * la classe ha i membri connectChannel, acceptChannel e recvChannel.
 * istanziato per recvChannel un Channel con micro_send = True.
 * preparato una stringa buffer e una lista per i send (sendBuffer e sendToBuffers)
 * preparato una stringa buffer e una lista per i recv (readBufferString e readBufferList)
 * preparato una costante maxreceivebuf = 65536
 * memorizzato il vero socket nella mappa di asyncore (questo lo fa {{{asyncore.__init__}}} => {{{asyncore.set_socket}}} => {{{asyncore.add_channel}}})
 * la sua rimozione dalla mappa avviene con {{{stacklesssocket.__del__}}} => {{{asyncore.close}}} => {{{asyncore.del_channel}}}
<<BR>>

== Creazione di un socket ==
Quando si crea un socket all'interno del programma (con {{{socket.socket(...)}}}) avvengono queste operazioni:
 1. viene effettivamente creato un vero socket (con {{{stdsocket.socket(...)}}}).
 1. viene creato anche un {{{microsock.stacklesssocket}}}. Questo nel suo costruttore fa queste operazioni:
   a. memorizza il vero socket.
   a. si crea e memorizza una istanza dell'oggetto {{{microsock.dispatcher}}}, che è una derivata di {{{asyncore.dispatcher}}}.
   a. {{{microsock.dispatcher}}} nel suo costruttore fa queste operazioni:
     I. memorizza il vero socket.
     I. la classe ha i membri {{{connectChannel}}}, {{{acceptChannel}}} e {{{recvChannel}}}.
     I. istanziato per {{{recvChannel}}} un Channel con {{{micro_send = True}}}.
     I. preparato una stringa buffer e una lista per i send ({{{sendBuffer}}} e {{{sendToBuffers}}})
     I. preparato una stringa buffer e una lista per i recv ({{{readBufferString}}} e {{{readBufferList}}})
     I. preparato una costante {{{maxreceivebuf}}} = 65536
     I. memorizzato il vero socket nella mappa di asyncore (questo lo fa {{{asyncore.dispatcher.__init__}}} => {{{asyncore.dispatcher.set_socket}}} => {{{asyncore.dispatcher.add_channel}}})
     I. la sua rimozione dalla mappa avverrà con {{{microsock.stacklesssocket.__del__}}} => {{{microsock.dispatcher.close}}} => {{{asyncore.dispatcher.close}}} => {{{asyncore.dispatcher.del_channel}}}
 1. viene restituito l'oggetto {{{microsock.stacklesssocket}}}.
In seguito tutte le chiamate {{{__getattr__}}} al finto socket (cioè all'oggetto stacklesssocket ritornato) vengono inoltrate al dispatcher (esempi di queste chiamate sono {{{s.listen}}}, {{{s.bind}}}, ...) mentre le chiamate {{{__setattr__}}} vengono memorizzate localmente. ???

== Connessione di una coppia di socket ==

Il modulo microsock

Quando viene creato un socket usando il wrapper ntk.wrap.sock.Sock, viene registrato nella mappa di asyncore e viene avviato un microthread nel modulo microsock.
La mappa di asyncore associa il fd (file descriptor, un numero intero) all'oggetto (che può essere un file, o un socket, ...).
Il microthread avviato esegue la funzione ManageSockets. Questa richiama asyncore.poll(0.05) ciclicamente (intervallando con micro_block per passare lo scheduler agli altri) fin quando esiste un socket (nel caso di netsukuku per sempre) nella mappa di asyncore.

Nota: La funzione asyncore.poll è bloccante. Il parametro passato è il numero di secondi dopo i quali cui si vuole cha la chiamata faccia ritorno.
Al suo interno la funzione richiama time.sleep oppure select.select, a seconda se ci sono o meno socket da controllare. In entrambi i casi queste funzioni usano chiamate di sistema che non impegnano la CPU, ma nemmeno permettono agli altri microthread di venire schedulati.
Per questo dobbiamo richiamarla con piccoli timeout intervallando con chiamate a micro_block. Vedi anche la trattazione dell'argomento nella pagina del modulo xtime.

Il metodo asyncore.poll, tramite il metodo select.select, scopre quali dei socket registrati nella sua mappa ha dei dati da inviare o da ricevere.

Vediamo quali passi segue la creazione e connessione di un socket, prima di analizzare quello che avviene quando si trasmette o riceve qualcosa.

Creazione di un socket

Quando si crea un socket all'interno del programma (con socket.socket(...)) avvengono queste operazioni:

  1. viene effettivamente creato un vero socket (con stdsocket.socket(...)).

  2. viene creato anche un microsock.stacklesssocket. Questo nel suo costruttore fa queste operazioni:

    1. memorizza il vero socket.
    2. si crea e memorizza una istanza dell'oggetto microsock.dispatcher, che è una derivata di asyncore.dispatcher.

    3. microsock.dispatcher nel suo costruttore fa queste operazioni:

      1. memorizza il vero socket.
      2. la classe ha i membri connectChannel, acceptChannel e recvChannel.

      3. istanziato per recvChannel un Channel con micro_send = True.

      4. preparato una stringa buffer e una lista per i send (sendBuffer e sendToBuffers)

      5. preparato una stringa buffer e una lista per i recv (readBufferString e readBufferList)

      6. preparato una costante maxreceivebuf = 65536

      7. memorizzato il vero socket nella mappa di asyncore (questo lo fa asyncore.dispatcher.__init__ => asyncore.dispatcher.set_socket => asyncore.dispatcher.add_channel)

      8. la sua rimozione dalla mappa avverrà con microsock.stacklesssocket.__del__ => microsock.dispatcher.close => asyncore.dispatcher.close => asyncore.dispatcher.del_channel

  3. viene restituito l'oggetto microsock.stacklesssocket.

In seguito tutte le chiamate __getattr__ al finto socket (cioè all'oggetto stacklesssocket ritornato) vengono inoltrate al dispatcher (esempi di queste chiamate sono s.listen, s.bind, ...) mentre le chiamate __setattr__ vengono memorizzate localmente. ???

Connessione di una coppia di socket

Prima che un socket abbia qualcosa da inviare o ricevere, di norma viene connesso richiamando in esso il metodo connect o listen e poi accept.

Netsukuku/ita/ModuloMicrosock (last edited 2009-05-04 15:51:42 by lukisi)