Server TCP completo in python

Abbiamo visto nell’articolo precedente come far comunicare fra di loro due programmi scritti in python. Avevamo rilevato alcuni problemi che andavano risolti ed in questo articolo proviamo a risolverli.

Il primo problema è quello del controllo degli errori. Non possiamo permettere che un errore di comunicazione blocchi l’esecuzione del programma, è meglio gestirlo da programma e se la connessione non riesce o cade, basta riprovarci.

Gestione degli errori

Il python mette a disposizione il costrutto TRY (prova in italiano) che permette di intercettare gli errori e definire come comportarsi se qualcosa va male. La sintassi è la seguente:

try:
    x = 10 / 0
except:
    print("C'é stato un errore")

Se provate ad eseguire direttamente x = 10 / 0 riceverete l’errore division by zero, utilizzando il costrutto try vengono invece eseguite le istruzioni che seguono except, in questo caso il print. Il concetto è semplice, se l’esecuzione delle istruzioni dopo il try non genera errori il programma ignora le istruzioni che seguono except, se invece si verifica un errore l’interprete invece di bloccare l’esecuzione salta alle istruzioni che seguono except.

Se except viene scritta senza parametri, per qualsiasi errore verranno eseguite le istruzioni che la seguono. Volendo si possono intercettare i vari tipi di errori ed comportarsi in maniera diversa a seconda dell’errore. Si possono mettere più except per ogni try specificando per ognuno il tipo di errore che si vuole gestire con le istruzioni che lo seguono.

import sys
try:
    x = int(input("Inserisci un numero: "))
    x = 12 / 0
except ValueError:
    print("Hai inserito un carattere non numerico")
except:
    print("Errore imprevisto:", sys.exc_info()[0])
print("fine programma")

La libreria sys che importiamo a inizio script ci serve per avere le informazioni sull’errore, nell’ultima riga dello script. Se fate partire il programma e inserite una lettera al posto di un numero lo script salta all’except valueerror, che si accende quando operiamo con un tipo di dato diverso da quello atteso, comunicando che è stato inserito un carattere non numerico.

Se inseriamo un numero il programma prosegue con una divisione per zero, che non essendo gestito salta alla exept senza valori che sfruttando la funzione info ci comunica il tipo di errore che si è verificato.

In entrambi i casi il programma visualizza “fine programma”, dimostrando che l’esecuzione è comunque continuata.

I threads

Un thread è una parte del programma che viene eseguito in contemporanea con il programma principale. Avevamo il problema di accettare più connessioni da più client contemporanemente e lo possiamo risolvere con questa tecnica.

Utilizzeremo un thread per ogni connessione in modo che il programma principale possa continuare a fare quello che deve e contemporanemente ogni thread lanciato si occupa di una connessione con un client. Questo il nuovo codice

import socket
from threading import Thread
class ThreadedServer(Thread):
    def run(self):
        print('Avvio server', '\n')
        self.sock = socket.socket()
        self.sock.bind(('0.0.0.0', 60001))
        self.sock.listen(5)
        print('SERVER in ascolto', '\n')
        while True:
            client, address = self.sock.accept()
            client.settimeout(60)
            print('nuova connessione: ')
            Thread(target=self.listenToClient,args=(client, address)).start()
            client.send(b'benvenuto')

    def listenToClient(self, client, address):
        while True:
            try:
                data = client.recv(1024)
                if data:
                    print('dati ricevuti:',data)
                    client.send(b'risposta + '+ data)
            except:
                print('disconnesso')
                client.close()
                return False
ThreadedServer().start()
print ('altre istruzioni');
#contatore = 0
#while contatore <= 3000:
#    print(contatore)
#    contatore = contatore + 1

Un po’ più complesso di quello precedente, vediamo come funziona. Intanto stiamo importanto oltre alla libreria socket che ci serve per comunicare in TCP, anche la libreria thread, che ci serve per creare i thread. Subito dopo import vedete la classe threadserver, sono le istruzioni del nostro server. Come potete notare la classe ha due funzioni, run e listentoclient. La classe run è quella che viene eseguita all’avvio del thread e provvede a creare il socket per la connessione ed attendere che un client si connette, appena questo avviene il client viene gestito dalla funzione listentoclient e il thread ritorna in attesa di una connessione. La classe listentoclient (ascolta il client) manda un messaggio di benvenuto al client, riceve i suoi dati e li restituisce con una stringa in più.

Il programma effettivo parte dallo start del thread. Il ciclo commentato che segue l’ho messo per verificare che mentre il programma faceva altro, cioè visualizzava un numero che si incrementa, le connessioni avvenivano regolarmente e le visualizzazione dell’avvenuta connessione si alternavano ai numeri visualizzati.

Il client é lo stesso utilizzato nell’articolo precedente ma con il controllo degli errori.

import socket
HOST = '127.0.0.1'  
PORT = 60001        
try:
    s = socket.socket()
    s.connect((HOST, PORT))
    dati = s.recv(1024)
    print('messaggio di benvenuto:', repr(dati))
    s.sendall(b'vai su salentoplus.it')
    dati = s.recv(1024)
    print('risposta ricevuta:', repr(dati))
    s.close()
except:
    print("problema di connessione, riprova")

Nei prossimi articoli ho intenzione di provare la comunicazione, sempre in python, fra computer e microcontrollore e in un secondo momento, se avrò ancora voglia e pazienza, fra un microcontrollore e android.

Lascia un commento