Forum >> Principianti >> Utilizzo Socket

Pagina: Indietro 1 2 3 4 5 Avanti

Pietro Bruno Lancerotto said @ 2024-02-24 13:39:59:
Ti chiedo se sia il caso di postare i file nella loro versione "quasi" definitiva a beneficio di quanti dovessero cimentarsi con un colloquio TCP/IP fra applicazioni
È mia opinione che condividere la conoscenza sia sempre bene, da un lato rende informazioni,magari ottenute con fatica, fruibili agli altri, d'altro lato può spingere a suggerimenti circa eventuali miglioramenti. Comunque, rappresentano un momento di crescita condivisa.
Pietro Bruno Lancerotto said @ 2024-02-24 13:39:59:
E come già chiesto ti chiedo di orientarmi su cosa mettere al posto della Label nel riquadro ricezione
Avevo già letto il Tuo "consigliami", la risposta è scontata : tkinter.text + scrollbars ... per me è facile da realizzare ma il primo pensiero che mi è venuto in mente è stato : e mo' come gli spiego come si fa?




Ho già realizzato un sub-classamento della classe tkinter.Text che si auto-limita ad un numero di linee di testo definito dall'user e che può o meno essere modificabile senza essere disabilitata (nel mio linux una tkinter.Text disabilitata come aspetto fa piangere), una volta completati i test e collegata al codice utilizzato per esemplificare con lo echo-server che ho usato post fa, fornirò qui le istruzioni per inserire la sub-classe nella Tua applicazione e su di un sito più comodo le spiegazioni sul suo funzionamento, ci sono diverse cose da tener presenti in merito alla implementazione.




Sempre in ambito "consigliami":

Le classi sono inevitabili parlando di GUI, cerca di comprenderle, il tutorial è un buon inizio, principalmente se lo condisci con la documentazione, relativamente a tkinter, necessiterai di molte ricerche in rete, comunque la documentazione meglio organizzata a mio parere è quella sulla versione 8.5, un po' vecchiotta ma molto utile ... sono "strumenti" che io uso quotidianamente, poi ci sono tante prove ed olio di gomito ...

Fatti non foste a viver come bruti...
Oggi ho messo sotto torchio il terminale

Diciamo che nell'ambito di utilizzo suo proprio non dovrebbe accadere mai .... però
L'ho lasciato in ascolto sul canale APRS dei radioamatori e li arriva un po' di tutto ed è arrivato questo

`''yl!rr/r7a & ru12a sysop IR3BT*,WIDE2-1 <UI pid=F0 Len=26 >[16:45:18]

`''yl!rr/r7a & ru12a sysop IR3BT*,IK3SVW-11*,WIDE2* <UI pid=F0 Len=26 >[16:45:20]




Che finché il decode ha "tenuto" ha dato questo

1:Fm IN3RIY To 4V0P20 Via IR3BT*,WIDE2-1 <UI pid=F0 Len=26 >[16:45:18]
`''yl!rr/r7a & ru12a sysop
1:Fm IN3RIY To 4V0P20 Via IR3BT*,IK3SVW-11*,WIDE2* <UI pid=F0 Len=26 >[16:45:20]
`''yl!rr/r7a & ru12a sysop




Poi alla ricezione successiva è andato in errore



Exception in thread Thread-1 (listen):
Traceback (most recent call last):
File "C:\Program Files\Python312\Lib\threading.py", line 1073, in _bootstrap_inner
self.run()
File "C:\Program Files\Python312\Lib\threading.py", line 1010, in run
self._target(*self._args, **self._kwargs)
File "D:\DRIVE-BRUNO\PYTHON\PROVE\PKT_GUI_RCS\utility.py", line 86, in listen
msg = ['RECEIVED', data[36:-3].decode()]
^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf8 in position 144: invalid start byte

Argghhh vale 248 decimale / è °

In un primo momento avevo pensavo alla lunghezza ma poi leggendo bene ...
Un famigerato carattere oltre il 127
Secondo il mio punto di vista bisognerebbe prima tagliare il data e poi vedere se nella parte estrapolata ci sia un carattere > di 127 e saltare la decodifica

Trovare UN carattere è una cosa ma in questo caso bisogna "ciclare" tutti i caratteri e se uno è sballato saltare il decode
Questa la mia teoria ma metterla in pratica .... ;(


Mi è capitato di muovere il disco di una "bollinatrice" con 36 caratteri ( 1 ogni 10° ) posizionandola dopo aver interpretato il codice ASCII del testo, lettera per lettera.

Ma in quel caso il linguaggio era un altro e non era Python ;( ;(

Si puo' fare ??



--- Ultima modifica di Pietro Bruno Lancerotto in data 2024-02-24 20:19:24 ---
--
Bruno L.
Sto facendo qualche prova per togliere i caratteri da 128 a 255

string = "AAAÇüé°BBB"
characters = "Ç°üé"
for x in range(len(characters)):
string = string.replace(charactersx, "") # ma perchè toglie le parentesi quadre ai lati di x
print(string)

In ogni caso io ho byte :(


vedo cosa succede




Ho modificato così

msg = ['RECEIVED', data[36:-3].decode(encoding="UTF-8", errors="ignore")]




--- Ultima modifica di Pietro Bruno Lancerotto in data 2024-02-25 10:21:58 ---
--
Bruno L.
Pietro Bruno Lancerotto said @ 2024-02-24 20:05:53:
...
Poi alla ricezione successiva è andato in errore
Exception in thread Thread-1 (listen):
...
File "D:\DRIVE-BRUNO\PYTHON\PROVE\PKT_GUI_RCS\utility.py", line 86, in listen
msg = ['RECEIVED', data[36:-3].decode()]
...
Argghhh vale 248 decimale / è °

Prova a fare questo tentativo:
# implenta questa funzione nel modulo utility
def read_message(data: bytes) -> str:
    return ''.join([chr(int.from_bytes(x, 'little')) for x in data[36:-3]])

#trasforma questa istruzione
 msg = ['RECEIVED', data[36:-3].decode()]
#così
  msg = ['RECEIVED', read_message(data)]

la funzione "dovrebbe" trasformare ogni singolo byte in un intero e poi nel carattere corrispondente, quindi unire tutto in stringa e restituire la stringa formata.
Non la ho testata (appena sveglio e con poco tempo) ma penso dovrebbe funzionare, possibile variante sarebbe utilizzare "big" invece di "little", dipende dalla benedetta codifica con cui vengono trasmessi i dati, di certo molto oscura.

Fai sapere, ciao


EDIT : No, lascia perdere, non funziona

il problema dovrebbe nascere dalla codifica di trasmissione, di certo non utf-8, vai ad ad indovinare che codifica viene utilizzata (forse la cp1252?) ... meglio la Tua soluzione di ignorare gli errori.

Una variante potrebbe essere utilizzare esclusivamente il chr() ma darebbe caratteri impropri per gli utf-8 da due bytes ... forse bisognerebbe guardarsi la documentazione l'header scartato (i 36 bit) per vedere se vi è qualcosa riguardo alla codifica del messaggio.





--- Ultima modifica di nuzzopippo in data 2024-02-25 10:50:44 ---

--- Ultima modifica di nuzzopippo in data 2024-02-25 11:38:06 ---
Fatti non foste a viver come bruti...
EDIT : No, lascia perdere, non funziona
meglio la Tua soluzione di ignorare gli errori.

Con la soluzione di ignorare gli errori sta andando da una decina di ore senza perdere un colpo; quasi 3000 righe di log.


Io sto provando connesso agli APRS che mandano un casino di caratteri strani

Mi domando come il SM faccia passare certi pacchetti.




Quando sarà usato per lo scopo per cui è nato ci saranno solo caratteri corretti e comunque le postazioni sono qualche decina di Km dalla prima all'ultima

Il segnale è sempre molto forte per cui non c'è pericolo di invii farlocchi.




Adesso dovrò ridimensionare un po' perchè è troppo grande.Nella parte TX vengono inviate 2 3 righe alla volta per cui và accorciata




Grazie al tuo supporto sono arrivato in fondo

--
Bruno L.
beh ... c'è ancora quel "consigliami" in ballo ;)

Copia questo codice in "utility.py"

import tkinter as tk

class BufferedText(tk.Text):
    '''Una Text-box inibibile alla scrittura senza disabilitazione
       e che espone un numero stabilito di righe di testo.'''
    key_none = ['Alt_L', 'Alt_R', 'Caps_Lock', 'Control_L', 'Control_R', 'Down', 'End',
                'Escape', 'Execute', 'F1', 'F2', 'Fi', 'F12', 'Home', 'Insert', 'Left',
                'Linefeed', 'KP_Add', 'KP_Begin', 'KP_Divide', 'KP_Down', 'KP_End',
                'KP_Home', 'KP_Insert', 'KP_Left', 'KP_Next', 'KP_Prior','KP_Right',
                'KP_Up', 'Next', 'Num_Lock', 'Pause', 'Print', 'Prior', 'Right',
                'Scroll_Lock', 'Shift_L', 'Shift_R', 'Tab', 'Up']
    def __init__(self, parent: callable, active: bool=False, rowsize: int=0, *args, **kwargs) -> None:
        super().__init__(parent, *args, **kwargs)
        self.parent = parent
        self._active = active
        self._rowsize =  rowsize if rowsize >=0 else 0
        self._bg = self['background']
        self.bind('<KeyPress>', self._on_key)
        self.bind('<FocusIn>', self._on_focus)
        self.bind('<FocusOut>', self._out_focus)

    def _on_key(self, evt: callable) -> None:
        if  evt.keysym in ['Return', 'KP_Enter'] and self._rowsize:  # valuta il caso si aggiunga una riga
            if not self._active: return 'break'
            self._evaluate_rows()
        elif not self._active and not evt.keysym in self.key_none:
            return 'break'
    
    def _evaluate_rows(self):
        if not self._rowsize: return
        rows = int(self.index('end').split('.')[0]) - 2
        if self._active and self.parent.focus_get() == self:
            rows += 1
        if rows > self._rowsize:  # raggiunto il limite di righe stabilito
            for i in range(rows - self._rowsize):
                self.delete('1.0', '1.end + 1 char')
                self.update()
            
    def _on_focus(self, evt: callable) -> None:
        color = '#ffffc0' if self._active else '#bfe5f1'
        self.configure(bg=color)
    
    def _out_focus(self, evt: callable) -> None:
        self.configure(bg=self._bg)
    
    def is_active(self) -> bool:
        return self._active
    
    def activate(self) -> None:
        self._active = True
    
    def disable(self) -> None:
        self._active = False

    @property
    def buffer(self) -> int:
        return self._rowsize

    @buffer.setter
    def buffer(self, buff: int) -> None:
        self._rowsize = buff if buff >=0 else self._rowsize
        self._evaluate_rows()

    def add_text(self, text: str) -> None:
        self.insert('end', text)
        self._evaluate_rows()
        self.see('end')

avrai disponibile un Text tkinter che potrai rendere a sola lettura senza disattivarlo e cui potrai porre un limite alle righe da visualizzare, per farlo edita il file "pkt_gui_rcs.py" inserendo questo import

from utility import BufferedText
Poi modifica il punto con self.TESTO_RX in questo modo

        self.TESTO_RX = BufferedText(self.FrameRX, rowsize=200, height=5, wrap='none')
        #self.TESTO_RX.place(relx=0.005, rely=0.1, height=350, width=915)
        self.TESTO_RX.grid(row=1, column=0, padx=(5,1), pady=(5,1), sticky='nsew')
        
        '''self.TESTO_RX.configure(activebackground="#f9f9f9")
        self.TESTO_RX.configure(anchor='w')
        self.TESTO_RX.configure(background="#ffffec")
        self.TESTO_RX.configure(compound='left')
        self.TESTO_RX.configure(disabledforeground="#a3a3a3")
        self.TESTO_RX.configure(font="-family {Courier} -size 10")
        self.TESTO_RX.configure(foreground="black")
        self.TESTO_RX.configure(highlightbackground="#d9d9d9")
        self.TESTO_RX.configure(highlightcolor="black")
        self.TESTO_RX.configure(justify='left')'''
        vscroll = tk.Scrollbar(self.FrameRX, orient=tk.VERTICAL,
                               command=self.TESTO_RX.yview)
        vscroll.grid(row=1, column=1, padx=(1,5), pady=(5,1), sticky='ns')
        self.TESTO_RX.configure(yscrollcommand=vscroll.set)
        hscroll = tk.Scrollbar(self.FrameRX, orient=tk.HORIZONTAL,
                               command=self.TESTO_RX.xview)
        hscroll.grid(row=2, column=0, padx=(5,1), pady=(1,5), sticky='ew')
        self.TESTO_RX.configure(xscrollcommand=hscroll.set)
        #self.TESTO_RX.configure(textvariable=self.TestoRicevuto)
        #self.TestoRicevuto.set('''''')
        self.FrameRX.grid_columnconfigure(0, weight=1)
        self.FrameRX.grid_rowconfigure(1, weight=1)

quindi modifica il metodo "obs" così

    def obs(self, message: list) -> None:
        op = message[0]
        txt = ''
        if op != 'ERROR':
            txt = message[1]
            self.TESTO_RX.add_text(txt + '\n')
        else:
            txt = 'AVVENUTO ERRORE : ' + message1
        #old = self.TestoRicevuto.get()
        #txt = old + '\n'+ txt
        #self.TestoRicevuto.set(txt)

Dovresti trovarTi una text-box che fa quello che mi hai chiesto.

Ho cercato di spiegarne il funzionamento in questo post, su di un sito di cui sono l'howner, c'è anche il codice di una finestra di test e potrebbe mostrarTi altri tipi di approccio.

Se mi dirai che ne pensi sarà gradito.




Ciao




P.S. : Per il Tuo virus non ne so niente, non uso windows dal secolo scorso ;)

Fatti non foste a viver come bruti...
Ho cercato di spiegarne il funzionamento in questo post, su di un sito di cui sono l'howner, c'è anche il codice di una finestra di test e potrebbe mostrarTi altri tipi di approccio.
Se mi dirai che ne pensi sarà gradito.
Cosa pensare ??

Eccezzionale !!!




Ho fatto qualche adattamento

Abbassato il bordo superiore perché la finestra andava sotto la Label "RICEZIONE"

Poi leggendo l'ottimo post di cui sopra, ho messo il wrap = word

Di conseguenza ho commentato le tre righe della scrolbar orizzontale

Almeno quello che arriva si vede per intero senza doversi spostare in orizzontale

E poi messo 100 nel numero di righe





Poi, cosa che non c'entra nulla con Python, ho eliminato molti più caratteri

L'intestazione con il FROM TO LEN a livello pratico delle nostre necessità non riveste alcuna importanza

Poi leggendo le API consigliate dall'amico Andrej ho modificato il FROM con il Testo + chr(0) *10 e "tagliando" poi tutto a 10 caratteri

In realtà il FROM ha i caratteri del nominativo ma potrebbe avere un -1 o altro in aggiunta per indicare se sei fisso, in movimento o altro

Se il nominativo fosse 5 caratteri verrebbero aggiunti NUL nei "buchi" vuoti. Non sono 6 + 4 NULL ma bensi 10 con i NULL nelle posizioni mancanti


Teniamo presente che il COSO è per i radioamatori




Tornando a noi volevo chiederTi qualcosa sui ritorni a capo

Ne file di LOG che io salvo ci sono per cui in quel text nel obs ci sono




self.TESTO_RX.add_text(txt + '\n')

in coda ho aggiunto



pkt_gui_rcs_support.Salvataggio(txt + "\n")





E' possibile che quella finestra Rispetti i ritorni a capo ??

Ovviamente sarà come scrivi nell'articolo che le righe impostate a 100 saranno in realtà di più




Mi defisci collega, troppo onore. Mi reputo uno smanettone ignorante.




Ciao e grazie ancora







--
Bruno L.
Ho cercato di spiegarne il funzionamento in questo post, su di un sito di cui sono l'howner, c'è anche il codice di una finestra di test e potrebbe mostrarTi altri tipi di approccio.
Se mi dirai che ne pensi sarà gradito.
Cosa pensare ??

Eccezzionale !!!




Ho fatto qualche adattamento

Abbassato il bordo superiore perché la finestra andava sotto la Label "RICEZIONE"

Poi leggendo l'ottimo post di cui sopra, ho messo il wrap = word

Di conseguenza ho commentato le tre righe della scrolbar orizzontale

Almeno quello che arriva si vede per intero senza doversi spostare in orizzontale

E poi messo 100 nel numero di righe





Poi, cosa che non c'entra nulla con Python, ho eliminato molti più caratteri

L'intestazione con il FROM TO LEN a livello pratico delle nostre necessità non riveste alcuna importanza

Poi
leggendo le API consigliate dall'amico Andrej ho modificato il FROM con
il Testo + chr(0) *10 e "tagliando" poi tutto a 10 caratteri

In
realtà il FROM ha i caratteri del nominativo ma potrebbe avere un -1 o
altro in aggiunta per indicare se sei fisso, in movimento o altro

Se
il nominativo fosse 5 caratteri verrebbero aggiunti NUL nei "buchi"
vuoti. Non sono 6 + 4 NULL ma bensi 10 con i NULL nelle posizioni
mancanti


Teniamo presente che il COSO è per i radioamatori




Tornando a noi volevo chiederTi qualcosa sui ritorni a capo

Ne file di LOG che io salvo ci sono per cui in quel text nel obs ci sono


self.TESTO_RX.add_text(txt + '\n')

in coda ho aggiunto


pkt_gui_rcs_support.Salvataggio(txt + "\n")





E' possibile che quella finestra Rispetti i ritorni a capo ??

Ovviamente sarà come scrivi nell'articolo che le righe impostate a 100 saranno in realtà di più




Mi defisci collega, troppo onore. Mi reputo uno smanettone ignorante.



Ciao e grazie ancora
--
Bruno L.
Ho cercato di spiegarne il funzionamento in questo post, su di un sito di cui sono l'howner, c'è anche il codice di una finestra di test e potrebbe mostrarTi altri tipi di approccio.
Se mi dirai che ne pensi sarà gradito.
Cosa pensare ??

Eccezzionale !!!

Ho fatto qualche adattamento

Abbassato il bordo superiore perché la finestra andava sotto la Label "RICEZIONE"

Poi leggendo l'ottimo post di cui sopra, ho messo il wrap = word

Di conseguenza ho commentato le tre righe della scrolbar orizzontale

Almeno quello che arriva si vede per intero senza doversi spostare in orizzontale

E poi messo 100 nel numero di righe





Poi, cosa che non c'entra nulla con Python, ho eliminato molti più caratteri

L'intestazione con il FROM TO LEN a livello pratico delle nostre necessità non riveste alcuna importanza

Poi
leggendo le API consigliate dall'amico Andrej ho modificato il FROM con
il Testo + chr(0) *10 e "tagliando" poi tutto a 10 caratteri

In
realtà il FROM ha i caratteri del nominativo ma potrebbe avere un -1 o
altro in aggiunta per indicare se sei fisso, in movimento o altro

Se
il nominativo fosse 5 caratteri verrebbero aggiunti NUL nei "buchi"
vuoti. Non sono 6 + 4 NULL ma bensi 10 con i NULL nelle posizioni
mancanti


Teniamo presente che il COSO è per i radioamatori




Tornando a noi volevo chiederTi qualcosa sui ritorni a capo

Ne file di LOG che io salvo ci sono per cui in quel text nel obs ci sono


self.TESTO_RX.add_text(txt + '\n')

in coda ho aggiunto


pkt_gui_rcs_support.Salvataggio(txt + "\n")





E' possibile che quella finestra Rispetti i ritorni a capo ??

Ovviamente sarà come scrivi nell'articolo che le righe impostate a 100 saranno in realtà di più




Mi defisci collega, troppo onore. Mi reputo uno smanettone ignorante.



Ciao e grazie ancora
--
Bruno L.
Allegati
Pietro Bruno Lancerotto said @ 2024-02-27 18:24:51:
Ho fatto qualche adattamento ...
In effetti e quello che si deve fare : capire le indicazioni, approfondire ed adattare il tutto ai propri scopi.
Pietro Bruno Lancerotto said @ 2024-02-27 18:24:51:
Poi, cosa che non c'entra nulla con Python, ho eliminato molti più caratteri
L'intestazione con il FROM TO LEN a livello pratico delle nostre necessità non riveste alcuna importanza

Si, ho provato le nuove impostazioni con il server che ho mandato ptima ... naturalmente, funzionano male con esso dato che trattano un protocollo di comunicazione che non vi è contemplato.

... C'è quel "nostre" che mi preoccupa un po' ... hai presente, vero, che quelle che Ti do solo solo indicazioni e che dovresti approfondire le tematiche rivenienti?

Vi sono problematiche che temo potresti non aver presenti, che non danno molto fastidio a Te che codifichi ma se passi il Tuo lavoro ad altri è bene non ci siano ... Te ne indicherò un paio in seguito

Pietro Bruno Lancerotto said @ 2024-02-27 18:24:51:
...
E' possibile che quella finestra Rispetti i ritorni a capo ??

La finestra rispetterà i ritorni a capo solo (e soltanto) se riceverà una stringa codificata quale utf-8, i singoli caratteri"\" e "n", passati come tali non verranno interpretati come new-line ... in pratica, dovresti decodificare l'insieme di bytes da passare alla finestra come 'utf-8'

Pietro Bruno Lancerotto said @ 2024-02-27 18:24:51:
Mi defisci collega, troppo onore. Mi reputo uno smanettone ignorante.

Non credere io sia un "sapiente" sono a mia volta un (vecchio) smanettone ignorante che cerca di imparare qualcosa, magari su python ho avuto più tempo di Te ma rimani un collega :)




Veniamo ora ai punti di cui Ti volevo avvertire :

1 - in utility.Delivery vi sono diverse istruzioni "raise RuntimeError ...." esse stanno li perché dovrebbero essere intercettate e gestite, altrimenti emetteranno un traceback in una eventuale shell di avvio, ovviamente quando lo ho manipolato non ho "gonfiato" il codice con la gestione degli errori, pensaci ... al minimo trasforma i "raise" in "return"




2 - più "pesante", il recv dei socket è bloccante, come è impostato al momento Chatterbox alla chiusura della applicazione rimane appeso un thread in attesa di comunicazione (è un daemon) ... non se ne accorge nessuno ma non è una bella cosa ... e qui si incappa in complessità da far piangere (socket + thread + strutturazione codice) ... soluzione di "emergenza" :

A - in Chatterbox :

- funzione "connect" definire un timeout per il socket immediatamente dopo la connessione, così:

            self.sok.connect((self.svr, self.port))
            self.sok.settimeout(0.1)

la "temporizzazione" è di un decimo di secondo

- gestire l'errore di timeout nel target del thread, modificando il metodo "listen" come segue:

    def listen(self) -> None:
        while self.still:
            try:
                data = self.sok.recv(2048)
                if data:
                    # qui andrebbe una queue per collezionare i dati giunti
                    # ma complicherebbe le cose, mi limito a comunicare
                    msg = ['RECEIVED', data[86:-3].decode(encoding="UTF-8", errors="ignore")]
                    #print(msg)
                    self.pub.send_message('LISTENERS', msg)
            except socket.timeout:
                continue
        self.sok.close()
        msg = ['OUTCOME', f'Connessione con {self.svr} chiusa']
        self.pub.send_message('OBSERVER', msg)
        self.pub.unsubscribe('WORKER', self.obs)

B - modificare PAGINA in modo che dia tempo alle operazioni del socket, ritengo sia bene spezzettare "_on_close" in modo da avere più "ordine" nella eliminazione delle registrazione e tempo, in questo modo:

    def _on_close(self) -> None:
        msg = ['CLOSING', 'CLOSING']
        self.pub.send_message('WORKER', msg)
        self.top.after(500, self.__close)

    def __close(self) -> None:
        self.pub.unsubscribe('OBSERVER', self.obs)
        self.pub.unsubscribe('LISTENERS', self.obs)
        self.top.destroy()

credo debba essere sufficiente.




Comunque, thread e socket sono bestioline complicate ed approfondite, così come la strutturazione del codice ... hai ricevuto la mia email di risposta? Se ritieni posso inviarti una maccheronica traduzione (fatta da me e non pubblicabile) di un tutorial sui thread che ho trovato "buono" come spunto per approfondire l'argomento.




Ciao

Fatti non foste a viver come bruti...


Pagina: Indietro 1 2 3 4 5 Avanti



Esegui il login per scrivere una risposta.