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

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:

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