Differences between revisions 4 and 5
Revision 4 as of 2009-04-24 21:02:17
Size: 5898
Editor: lukisi
Comment:
Revision 5 as of 2009-04-25 19:24:23
Size: 6009
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
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. Quando viene creato un socket usando il wrapper {{{ntk.wrap.sock.Sock}}}, viene registrato nella mappa di {{{asyncore}}} (un modulo standard di python stackless) e viene avviato un microthread nel '''modulo microsock'''.
Line 28: Line 28:
     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. istanzia per {{{recvChannel}}} un Channel con {{{micro_send = True}}}.
     I. prepara una stringa buffer e una lista per i send ({{{sendBuffer}}} e {{{sendToBuffers}}})
     I. prepara una stringa buffer e una lista per i recv ({{{readBufferString}}} e {{{readBufferList}}})
     I. prepara una costante {{{maxreceivebuf}}} = 65536
     I. memorizza il vero socket nella mappa di asyncore (questo lo fa {{{asyncore.dispatcher.__init__}}} => {{{asyncore.dispatcher.set_socket}}} => {{{asyncore.dispatcher.add_channel}}})
Line 55: Line 55:

=== con protocollo UDP ===


== Trasmissione dati tra due socket ==

Il modulo microsock

Quando viene creato un socket usando il wrapper ntk.wrap.sock.Sock, viene registrato nella mappa di asyncore (un modulo standard di python stackless) 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. istanzia per recvChannel un Channel con micro_send = True.

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

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

      6. prepara una costante maxreceivebuf = 65536

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

con protocollo TCP

Un socket che funge da server con il protocollo TCP deve per prima cosa richiamare il metodo listen e poi accept.

Quando si richiama il metodo listen di un microsock.dispatcher (ereditato dal asyncore.dispatcher) il dispatcher si memorizza lo stato accepting = True e poi esegue il vero metodo listen sul socket. Il metodo listen non è bloccante.

Quando si richiama il metodo accept di un microsock.dispatcher (overridato rispetto al asyncore.dispatcher) il dispatcher si istanzia un oggetto Channel con micro_send = True e lo memorizza sul membro acceptChannel. Su questo oggetto richiama il metodo recv.
Questo metodo a sua volta richiama il metodo receive di uno stackless.channel. Questo metodo blocca la tasklet, cioè il microthread attivo, e schedula gli altri microthread. Quello bloccato non sarà più ri-schedulato fino a che non ci saranno messaggi su quel canale.
Quando il socket reale riceve una richiesta di connessione, anche se non è stato chiamato in realtà il suo metodo accept, la funzione asyncore_poll (periodicamente richiamata in un microthread apposito, come detto sopra) se ne accorge come se il socket fosse in attesa di lettura. Viene quindi richiamato il metodo handle_accept, da overridare.
Il metodo handle_accept (overridato rispetto al asyncore.dispatcher) verifica che sul channel contenuto sul membro acceptChannel ci sia qualcuno effettivamente in ascolto, guardando l'attributo balance. Poi richiama il metodo asyncore.dispatcher.accept della classe base (non più quello overridato detto prima); questo richiama accept del socket, ma stavolta siamo sicuri che non si bloccherà. Restituirà un socket connesso e l'indirizzo del socket remoto.
Il metodo handle_accept poi incapsula il socket ricevuto in un oggetto stacklesssocket e lo invia al channel contenuto sul membro acceptChannel. Siccome il Channel è con micro_send = True, questo invio viene effettuato in un nuovo microthread. TODO: Questo è davvero necessario? Ci siamo prima accertati che il balance fosse negativo...!
Il microthread prima bloccato viene ora ri-schedulato e il metodo microsock.dispatcher.accept restituisce un stacklesssocket connesso.

con protocollo UDP

Trasmissione dati tra due socket

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