################################ # Modulo di supporto al TCP/IP # # Versione per PyQt 4.x # # Ultimo aggiorn. 03/03/2013 # ################################ import socket from PyQt4.QtCore import * from PyQt4.QtNetwork import * SCKSTAT_OK = 0 SCKSTAT_EC = 1 SCKSTAT_ER = 2 DOT = '.' EMPTYSTR = '' ENDLINE = "\r\n" LINEFEED = '\n' RETURN = '\r' #################################### # Procedure e funzioni di utilita` # #################################### def rip(s): # # Crea e riporta una nuova stringa eliminando # dalla stringa "s" i RETURN e i LINEFEED finali. # return s.rstrip(ENDLINE) def rtrim(s): # # Crea e riporta una nuova stringa eliminando # dalla stringa "s" gli spazi finali. # return s.rstrip() def ltrim(s): # # Crea e riporta una nuova stringa eliminando # dalla stringa "s" gli spazi iniziali. # return s.lstrip() def trim(s): # # Crea e riporta una nuova stringa eliminando # dalla stringa "s" gli spazi iniziali e finali. # return s.strip() def resolve(s): # # Converte un indirizzo IP in formato # ASCII - sia numerico puntato sia # alfanumerico - in binario rappresentato # sia come intero, sia come bytes (2-tuple). # # # Indirizzo numerico puntato. # try: bas=socket.inet_aton(s) except socket.error: # # Indirizzo alfanumerico. # try: bas=socket.inet_aton(socket.gethostbyname(s)) except socket.error: # # Errore. # bas=b'0' ban=0 for i in range(len(bas)): ban=256*ban+bas[i] return (ban,bas) # End of resolve def ntoa(l): # # Converte un indirizzo IP binario # nel formato ASCII numerico puntato, # che riporta come stringa. # return socket.inet_ntoa(l) def dirsolve(name): # # Converte un hostname in formato alfanumerico # in un indirizzo IP in formato numerico puntato, # che riporta come stringa. # return socket.gethostbyname(name) def bcksolve(ip): # # Converte un indirizzo IP in formato numerico puntato # in un hostname completo, in formato alfanumerico, # che riporta come stringa. # hn=socket.gethostbyaddr(ip) return hn[0] def getBinLocalAddress(): # # Riporta l'Indirizzo Internet locale nei formati # numerico-puntato, binario e bytes (3-tuple). # locip=socket.gethostbyname(socket.gethostname()) binad=resolve(locip) return (locip,binad[0],binad[1]) def getLocalAddress(): # # Riporta l'indirizzo IP della macchina # locale, in formato numerico-puntato. # return socket.gethostbyname(socket.gethostname()) def getLocalName(): # # Riporta l'hostname della macchina locale (senza dominio). # locname=socket.gethostname() p=locname.find(DOT) if p!=-1: return locname[:p] else: return locname def getLocalDomain(): # # Riporta il nome del dominio della macchina locale. # locdomn=bcksolve(socket.gethostbyname(socket.gethostname())) p=locdomn.find(DOT) if p!=-1: return locdomn[p+1:] else: return EMPTYSTR ############################### # Supporto al modo "server" # # (Si appoggia a classi PyQt) # ############################### class agentThread(QThread): def __init__(self,sockid,servfun,parent=None): QThread.__init__(self,parent) # # Costruisce l'oggetto tcpStream # con cui gestire la connessione. # self.net=tcpStream(fileno=sockid) self.net.setblocking(True) # # Memorizza i parametri ricevuti che # devono essere utilizzati in "run". # self.servFun=servfun self.parent=parent def run(self): goon=self.servFun(self.net) self.net.close() # # Si conviene che se il programma # di gestione del processo server # riporta "False", allora occorre # spengere del tutto il server. # NB: il "parent" di questo oggetto # agentThread e' l'oggetto tcpServer # che raccoglie le connessioni! # if not goon: self.parent.emit(SIGNAL("closerequest()")) class tcpServer(QTcpServer): def __init__(self,servfun,parent=None): QTcpServer.__init__(self,parent) # # Memorizzo il riferimento alla funzione # che deve gestire il servizio richiesto. # self.servFun=servfun def incomingConnection(self,sockid): # # Quando il socket in ascolto riceve # una richiesta di connessione, attiva # questa funzione per gestirla; questa # funzione crea e lancia, allo scopo, un # apposito thread a cui passa il riferimento # della funzione da eseguire, oltre che # l'indentificativo della connessione # da utilizzare. # thrd=agentThread(sockid,self.servFun,self) self.connect(thrd,SIGNAL("finished()"),thrd.deleteLater) thrd.start() ##################### # Classe principale # ##################### class tcpStream(socket.socket): def __init__(self,family=socket.AF_INET,type=socket.SOCK_STREAM,proto=0,fileno=None): # # Costruttore (rilancia semplicemente quello della classe genitore). # socket.socket.__init__(self,family,type,proto,fileno) def open(self,serverName,port,timeOut=None): # # Apre uno stream TCP in modo CLIENT, inizializzando # un socket verso la porta TCP "port" del server # "serverName". # Riporta: # 0: connessione stabilita; # 1: errore nella risoluzione del nome del server; # 2: connessione rifiutata dal server. # self.settimeout(timeOut) # # Stabilisce l'host remoto con # cui ci si vuole collegare. # binadd=resolve(serverName)[1] if binadd!=b'0': serverIP=ntoa(binadd) else: return 1 # # Effettua la connessione. # try: self.connect((serverIP,port)) except socket.error: return 2 return 0 # End of open ################################################################## # Procedure e funzioni di inizializzazione, controllo e chiusura # ################################################################## def getRemoteAddress(self): # # Riporta l'indirizzo IP del corrispondente remoto, # in formato numerico-puntato. # return self.getpeername()[0] def getRemoteName(self): # # Riporta l'hostname completo del corrispondente remoto, # in formato alfanumerico. # return bcksolve(self.getpeername()[0]) def stat(self): # # Controlla lo stato del socket riportando: # SCKSTAT_OK se ci sono dati pronti da leggere; # SCKSTAT_EC se la connessione risulta chiusa; # SCKSTAT_ER se NON ci sono dati da leggere. # timeOut=self.gettimeout() # Acquisisce il timeout corrente self.settimeout(0) # Imposta il socket NON bloccante try: # Lettura NON distruttiva ch=self.recv(1,socket.MSG_PEEK).decode() # da bytes a string self.settimeout(timeOut) # Reimposta il timeout originale if ch!=EMPTYSTR: return SCKSTAT_OK else: return SCKSTAT_EC except socket.error as errmsg: errno=int(str(errmsg)[7:12]) if errno!=10035: # No data queued to be read print(str(errmsg)) self.settimeout(timeOut) # Reimposta il timeout originale return SCKSTAT_ER # End of stat def recvready(self): # # Riporta "True" se e solo se ci sono dati # pronti da leggere sulla connessione di rete. # return (self.stat()==SCKSTAT_OK) ######################################## # Procedure e funzioni "C-Stream-Like" # ######################################## def getc(self): # # Per leggere un singolo carattere dalla connessione di rete. # return self.recv(1).decode() # da bytes a string def putc(self,c): # # Per inviare un singolo carattere sulla connessione di rete. # self.send(c.encode()) # da string a bytes def gets(self): # # Per leggere dalla connessione di rete una stringa terminata # con LINEFEED; i caratteri RETURN e/o LINEFEED finali NON # vengono riportati. # ch=EMPTYSTR st=EMPTYSTR while ch!=LINEFEED: ch=self.recv(1).decode() # da bytes a string if ch!=RETURN and ch!=LINEFEED: st=st+ch return st def printf(self,frm): # # Per inviare una stringa sulla connessione di rete. # self.send(frm.encode()) # da string a bytes return SCKSTAT_OK def eof(self): # # Riporta "True" se e solo se il corrispondente # ha chiuso la connessione dalla sua parte. # return (self.stat()==SCKSTAT_EC) # End of class tcpStream