= Il modulo microsock =
== Connessione di una coppia di socket ==
=== con protocollo TCP ===
Se vogliamo implementare un server con il protocollo TCP, dobbiamo creare un socket di tipo {{{SOCK_STREAM}}}, associargli indirizzo e porta, 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 [[../ClasseChannel|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.
Se vogliamo implementare un client con il protocollo TCP, dobbiamo creare un socket di tipo {{{SOCK_STREAM}}} e poi richiamare il metodo connect.
Quando si richiama il metodo '''{{{connect}}}''' di un {{{microsock.dispatcher}}} questo per prima cosa richiama il metodo base, cioè il metodo {{{asyncore.dispatcher.connect}}}.
<
>
Il metodo {{{asyncore.dispatcher.connect}}} utilizza il metodo {{{socket.connect_ex}}} fornito dalla libreria di Python. Questo avvia un tentativo di connessione ma la chiamata non si blocca. Invece restituisce un numero (errno) con il quale indica se il tentativo è andato a buon fine o se è in corso o se c'è stato un altro errore. <
>
Se la connessione si completa, il metodo {{{asyncore.dispatcher.connect}}} richiama {{{handle_connect_event}}}, questo valorizza a True il suo membro {{{connected}}} e poi richiama il metodo {{{handle_connect}}}, da overridare. Il metodo {{{microsock.dispatcher.connect}}} in questo caso non ha bisogno di fare altro.
<
>
Se, invece, la connessione non si completa, il metodo {{{asyncore.dispatcher.connect}}} ritorna. Il metodo {{{microsock.dispatcher.connect}}} se ne accorge dal fatto che il membro {{{connected}}} è ancora a False. Quindi si prepara nel suo membro {{{connectChannel}}} un Channel con {{{prefer_sender = True}}} (vedi la [[../ClasseChannel|classe Channel]]) e si mette in ascolto su quel canale. In questo modo il microthread si blocca in attesa della connessione, senza bloccare gli altri microthread.
<
>
Quando il sistema completa la connessione sul socket reale, 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_connect}}}.
<
>
Il metodo {{{handle_connect}}} (overridato rispetto al {{{asyncore.dispatcher}}}) invia un messaggio al Channel memorizzato nel membro {{{connectChannel}}} così da ri-schedulare il microthread prima bloccato.
=== con protocollo UDP ===
Se vogliamo implementare un server con il protocollo UDP, dobbiamo creare un socket di tipo {{{SOCK_DGRAM}}}, associargli un indirizzo (nome host e porta UDP) e una interfaccia di rete.
<
>
Dopo di questo non ci sono altre operazioni da fare, quindi nessuna bloccante, prima di passare alla trasmissione/ricezione dati.
Se vogliamo implementare un client con il protocollo UDP, dobbiamo creare un socket di tipo {{{SOCK_DGRAM}}}, associare il socket ad una interfaccia di rete con {{{sk_bindtodevice}}} (supportato solo su linux) e poi richiamare il metodo {{{connect}}} dove al posto dell'indirizzo dell'host specifichiamo la stringa parola chiave {{{}}}.
<
>
In questo modo otteniamo un socket pronto a comunicare in broadcast su tutta la LAN acceduta tramite quella interfaccia di rete. Per questo il programma Netsukuku si crea un socket UDP per ogni scheda di rete da gestire e li connette ed usa tutti per inviare messaggi ai suoi vicini (vedi la classe {{{BcastClient}}} nel [[../ModuloRPC|modulo RPC]]).
<
>
Abbiamo già visto come il metodo {{{connect}}} di {{{microsock.dispatcher}}} finisce con il richiamare per prima cosa il metodo {{{socket.connect_ex}}} fornito dalla libreria di Python. Questo, quando si usa come indirizzo la parola chiave {{{}}} connette il socket e fa subito ritorno.
<
>
'''''TODO''': Questa è una mia deduzione. La documentazione di Python non è così esplicita. E' corretta?''