= 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?''