Differences between revisions 5 and 6
Revision 5 as of 2009-06-05 09:30:55
Size: 9560
Editor: lukisi
Comment:
Revision 6 as of 2009-07-16 08:28:44
Size: 9560
Editor: lukisi
Comment:
Deletions are marked like this. Additions are marked like this.
Line 5: Line 5:
 * il TCPClient invia il messaggio con {{{send}}};  * il TCPClient invia il messaggio con {{{sendall}}};
Line 7: Line 7:
 * l'esecuzione del metodo remotable produce una risposta; il TCPServer invia questa risposta con {{{sendall}}};  * l'esecuzione del metodo remotable produce una risposta; il TCPServer invia questa risposta con {{{send}}};

Il modulo microsock

Trasmissione dati tra due socket

Protocollo TCP

La trasmissione di dati tra due socket connessi con il protocollo TCP avviene in Netsukuku con l'uso dei metodi send, sendall e recv. Per la precisione quando viene chiamato un metodo remoto (vedi Chiamate a metodi remoti):

  • il TCPClient invia il messaggio con sendall;

  • il TCPServer riceve il messaggio con recv e esegue il metodo remotable;

  • l'esecuzione del metodo remotable produce una risposta; il TCPServer invia questa risposta con send;

  • il TCPClient riceve la risposta con recv.

Quando si richiama il metodo send di un microsock.dispatcher questo memorizza il messaggio da inviare in coda al buffer nel suo membro sendBuffer e fa subito ritorno al chiamante, segnalando di aver trasmesso tutti i bytes richiesti.
La presenza di dati in questo buffer fa sì che il metodo writable restituisca True. Questo a sua volta, come detto sopra, fa sì che asyncore.poll, periodicamente e solo quando la trasmissione non risulterebbe bloccante, richiami il metodo handle_write_event che a sua volta richiama handle_write overridato nella classe microsock.dispatcher.
Il metodo microsock.dispatcher.handle_write richiama il metodo asyncore.dispatcher.send sulla classe base, non più quello overridato. Questo effetua l'invio di un certo numero di bytes senza bloccare il processo e restituisce il numero di bytes trasmessi. Il metodo microsock.dispatcher.handle_write di seguito toglie dal buffer i bytes già inviati e fa ritorno.

Abbiamo detto che il metodo microsock.dispatcher.send fa subito ritorno segnalando la trasmissione di tutti i bytes richiesti. In realtà la trasmissione avviene in seguito.
Se si vuole essere sicuri che la trasmissione sia già avvenuta si chiama il metodo microsock.dispatcher.sendall. Questo metodo fa ritorno solo quando il buffer nel suo membro sendBuffer è completamente svuotato.
Per attendere questo evento effettua un loop in cui intervalla il test (poco oneroso) alla schedulazione degli altri microthread con micro_block e ad un time.sleep molto breve (vedi spiegazione in modulo xtime).

Per quanto riguarda la ricezione di dati, il metodo readable di un microsock.dispatcher si comporta in modo diverso a seconda che il socket sia di tipo UDP o TCP.
Per i socket di tipo UDP esso, ereditato tal quale dalla asyncore.dispatcher, restituisce sempre True. Invece per i socket di tipo TCP e connessi, esso restituisce True solo se qualche microthread è in attesa di ricevere dati (attributo _receiving valorizzato nel metodo recv).
Quando il metodo readable restituisce True (e inoltre ci sono dei dati disponibili per la lettura), asyncore.poll richiama il metodo handle_read_event che a sua volta richiama handle_read overridato nella classe microsock.dispatcher.
Il metodo microsock.dispatcher.handle_read richiama il metodo recv di asyncore.dispatcher, non quello overridato da microsock.dispatcher. Questo richiama il metodo recv sul socket reale che leggerà un massimo di maxreceivebuf bytes senza bloccarsi. Questi dati vengono inviati come messaggio sul Channel memorizzato sul membro recvChannel.
Siccome il Channel è stato istanziato con micro_send = True questa trasmissione sullo stackless.channel avviene in realtà su un altro microthread, lasciando così immediatamente libero di continuare il microthread che richiama periodicamente la asyncore.poll.
In questo modo si ottiene che i dati sono in attesa, senza bloccare gli altri microthread, di essere letti dallo pseudo socket.

Il metodo recv overridato nella classe microsock.dispatcher valorizza l'attributo _receiving detto sopra; di seguito sta in attesa di ricevere i dati dal Channel memorizzato sul membro recvChannel. Tutti i bytes che vi trova li mette nel buffer readBufferString. Poi ne restituisce al massimo quanti richiesti dal chiamante.
Per la precisione, prima di tutto il metodo guarda se nel buffer readBufferString ci sono già un numero di bytes da soddisfare del tutto il chiamante. Altrimenti si possono verificare 2 casi:

  • Ci sono nel Channel alcuni messaggi pronti per essere letti. In questo caso il metodo legge tutto quello che può dal Channel e accumula nel buffer. TODO: In realtà questo è il comportamento che si intenderebbe tenere, mi pare di capire dai commenti sul codice; ma non è implementato correttamente: ci vorrebbe un while invece di un if. Verificare e correggere.

  • Ci sono già alcuni bytes nel buffer (anche se non tanti quanti il massimo richiesto) e non ci sono altri messaggi pronti per essere letti nel Channel. In questo caso il metodo si prepara a restituire immediatamente quanto finora disponibile.
  • Ci sono 0 bytes nel buffer e non ci sono altri messaggi pronti per essere letti nel Channel. In questo caso il metodo deve bloccarsi in attesa di qualche byte. Lo fa mettendosi in ascolto sul Channel, quindi blocca il microthread ma non il resto dell'applicazione.

Alla fine, da quello che ha a disposizione sul buffer, rimuove e restituisce al chiamante al massimo n bytes, quanti richiesti.

Protocollo UDP

La trasmissione di dati tra due socket connessi con il protocollo UDP in modalità broadcast avviene in Netsukuku con l'uso dei metodi sendto e recvfrom. Per la precisione quando viene chiamato un metodo remoto (vedi Chiamate a metodi remoti):

  • il BcastClient invia il messaggio in broadcast a tutta la LAN connessa alla sua scheda di rete (ogni BcastClient è associato ad una interfaccia di rete) con sendto;

  • il UDPServer riceve il messaggio con recvfrom e esegue il metodo remotable;

  • non sono previste risposte ai metodi remoti chiamati in broadcast.

Il metodo sendto di un microsock.dispatcher riceve come parametri il messaggio da inviare e l'indirizzo a cui inviarlo.
La prima volta che un messaggio viene inviato ad un certo destinatario (che può essere l'indirizzo speciale "<broadcast>"), il metodo crea un Channel col costruttore micro_send = True associato a quel destinatario.
Poi memorizza messaggio, indirizzo destinatario, Channel e "numero di microthread in attesa sul Channel" nel suo membro sendToBuffers e si mette in attesa sul Channel, bloccando il microthread corrente ma non l'intero programma.
La valorizzazione del membro sendToBuffers, come avveniva per sendBuffer nella modalità TCP, fa in modo che, ogni volta che la trasmissione sul socket reale può avvenire senza bloccare, venga richiamato il metodo handle_write overridato nella classe microsock.dispatcher.
Questo metodo vede che deve inviare un certo numero di bytes ad un certo destinatario. Usa il metodo sendto sul socket reale. In base a quanti bytes è riuscito ad inviare aggiorna il numero di bytes restanti. Questi saranno presi in carico la prossima volta che il metodo handle_write viene richiamato.
Quando tutto il messaggio memorizzato in sendToBuffers per un certo destinatario è stato trasmesso viene inviato il numero totale di bytes al Channel associato in precedenza a questo destinatario. Quindi il microthread chiamante viene sbloccato ed è come se avesse inviato in un sol colpo tutto il messaggio.
Se altri microthread, usando il medesimo BcastClient comune a tutto il programma, tentassero di inviare messaggi allo stesso destinatario, questi verrebbero accodati perché il metodo sendto del microsock.dispatcher, nelle chiamate successive per lo stesso destinatario, usa la tupla memorizzata in precedenza nel suo membro sendToBuffers.

Dall'altra parte, quando un socket UDP (quello creato dal MicroUDPServer) ha dei dati da leggere, il metodo handle_read richiamato dal microthread che esegue ManageSockets effettua la lettura con il metodo recvfrom sul socket reale e trasmette i bytes letti al Channel memorizzato nel membro recvChannel.
Siccome il Channel è stato istanziato con micro_send = True questa trasmissione sullo stackless.channel avviene in realtà su un altro microthread, lasciando così immediatamente libero di continuare il microthread che richiama periodicamente la asyncore.poll.
In questo modo si ottiene che i dati sono in attesa, senza bloccare gli altri microthread, di essere letti dallo pseudo socket.

Il metodo recvfrom overridato nella classe microsock.dispatcher non fa altro che ricevere i dati dal Channel memorizzato sul membro recvChannel e restituirli al chiamante.
Siccome per il socket creato con un BcastClient questo metodo viene richiamato fin dall'inizio con il parametro bytecount a 8192, questo è il valore che sempre conterrà il suo membro maxreceivebuf utilizzato dal metodo handle_read per richiamare il metodo recvfrom sul socket reale.
Questo non implica che le letture fatte siano sempre complete. Del resto il protocollo UDP non garantisce tale completezza.

Netsukuku/ita/ModuloMicrosockTrasmissione (last edited 2009-07-16 08:28:44 by lukisi)