Aller au contenu
jjacques68

Question TCPSocket

Recommended Posts

Bonjour à tous, 

 

Pour ceux qui ont commencé à jouer avec la HC3, j'ai une petite question

 

J'ai un QA qui envoie des trames sur une socket TCP vers un serveur.

 

ça marche avec ce code (je vois mes trames arriver sur le serveur) 

 

function QuickApp:onInit()
    self:debug("onInit")
    self:Open_Socket()
end

function QuickApp:Open_Socket()
    self.ip = self:getVariable("IP")
    self.port = tonumber(self:getVariable("Port"))    
    self.sock = net.TCPSocket()
    self.sock:connect(self.ip, self.port)
end

function QuickApp:Send(MaTrame)
    --affiche la trame dans le label    
    self:updateView("LBL_Buffer", "text", tostring(MaTrame)) 
    --envoi la trame
    self.sock:write(MaTrame, {
        success = function()  self:debug("data sent")  end,
        error = function(err)
                    self:debug("error while sending data")
                    self.sock:close()
                    fibaro.sleep(200)
                    self:Open_Socket()
                    fibaro.sleep(200)
                    self:Send(MaTrame)           
                end
        })
end

Donc en gros, 

 

J'ouvre la socket au démarrage du QA, et j'envoi la trame sur appel de la méthode Send(MaTrame)

Actuellement je passe par un bouton dans le QA pour déclencher l'envoi, plus tard ce sera fait par scène, bref... le problème est pas là.

 

Le soucis est que si le serveur TCP redémarre, je n'arrive plus à relancer la connexion depuis le QA !

malgré ces lignes : 

 self.sock:close()
 fibaro.sleep(200)
 self:Open_Socket()
 fibaro.sleep(200)
 self:Send(MaTrame)

où je ferme la précédente socket, je la ré-ouvre, et je renvoi la trame (le tout temporisé) !

 

Et bien au premier essai, après redémarrage du serveur TCP, il me répond que la trame a bien été envoyé. Et biensûr j'ai rien vu arriver sur le serveur.

Au deuxième essai il boucle indéfiniment en me disant "error while sending data".

 

Comment peut-on réinitialiser la socket proprement, et relancer la trame qui n'a pas pu être envoyée !!

 

des idées ????

 

merciiiiiiiiiiiiii !

 

 

Modifié par jjacques68

Partager ce message


Lien à poster
Partager sur d’autres sites

Quelques remarques:

 

1. Il faut placer de préférence l’instanciation du socket dans le onInit ainsi que le nécessaire pour la configuration / paramétrage soit

function QuickApp:onInit()
  self.sock = net.TCPSocket()
  self.ip = self:getVariable("IP")
  self.port = tonumber(self:getVariable("Port"))  
end

2. Ajouter une méthode Close_Socket() dans le QuickApp 

function QuickApp:Close_Socket()
  self.sock:close()
end

3. La méthode Open_Socket ne devrait servir qu'a l'ouverture du socket + trace etc...

function QuickApp:Open_Socket()
  self:debug("open socket")
  self.sock:connect(self.ip, self.port)
end

4. Utiliser de préférence setTimeout (non bloquant) à la place de fibaro.sleep(xxx) car net.TCPSocket est asynchrone... soit remplacer les lignes 

 self.sock:close()
 fibaro.sleep(200)
 self:Open_Socket()
 fibaro.sleep(200)
 self:Send(MaTrame)

PAR

self.Close_Socket() -- socket closed
fibaro.setTimeout(5000, function()
	self:Open_Socket()
	self:Send(MaTrame)
end) -- re-connection attempt (every 5s)

5. Ne pas hésiter à restituer le message d'erreur retourné comme ceci

self:debug("connection error:", message)

Je n'ai rien testé mais cela permettra de mieux identifier par la suite les points bloquants ;)

  • Like 1

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai fais comme tu dis : 

function QuickApp:onInit()
    self.sock = net.TCPSocket()
    self.ip = self:getVariable("IP")
    self.port = tonumber(self:getVariable("Port")) 
    self:Open_Socket()
end

function QuickApp:Open_Socket()
    self.sock:connect(self.ip, self.port)
    self:debug("Socket open")
end

function QuickApp:Close_Socket()    
    self.sock.close()
    self:debug("Socket close")
end

function QuickApp:Send(MaTrame)
    --affiche la trame dans le label    
    self:updateView("LBL_Buffer", "text", tostring(MaTrame)) 
    --envoi la trame
    self.sock:write(MaTrame, {
        success = function() 
                     self:debug("data sent")
                    end,
        error = function(err)
                     self:debug("error : "..err)
                     self:Close_Socket()
                     fibaro.setTimeout(5000, function()
                                                self:Open_Socket()
                                                self:Send(MaTRame)
                                             end)
                end
        })
end

Le comportement est vraiment étrange : 

 

- tout est toujours ok au démarrage du QA.

- mais après redémarrage du serveur TCP, pareil la 1ère trame envoyée me répond "data sent", mais je vois toujours rien !

- à la 2ème trame, j'ai l'erreur "Broken Pipe" (logique je comprends...)

- et le setTimeout semble prendre après très très longtemps (plus que les 5 secondes) : j'ai le "Socket Open" qui arrive après 10 s !!

- et la trame n'a pas été renvoyée

 

étrange tout ça :4:

Partager ce message


Lien à poster
Partager sur d’autres sites

Hum le socket doit mal gérer la déconnection.
Tu as essayer sans faire le close ? Juste rejouer le send toutes les 5 secondes jusqu’à livraison.
Si le QA plante il faudrait penser à faire le sens dans un pcall pour protéger le qa du plantage.

Partager ce message


Lien à poster
Partager sur d’autres sites

alors j'ai essayé sans le close, même résultat.

 

mais le QA ne plante pas, (du moins j'ai pas le message du crash comme j'ai déjà eu)

 

J'ai essayé de faire un Open à chaque envoi (un peu comme sur la HC2), mais ça marche pas non plus.

 

On dirait qu'on a perd de la réactivité avec ces sockets.

 

Pourtant tant que tu redémarres pas le serveur TCP, ça marche nickel !!

Partager ce message


Lien à poster
Partager sur d’autres sites

Étrange... Si je trouve le temps en soirée

Partager ce message


Lien à poster
Partager sur d’autres sites

@jjacques68, ne changerais-tu pas ton pseudo en SocketMen ? :2:

  • Like 1

Partager ce message


Lien à poster
Partager sur d’autres sites

Hello, non malheureusement je n’ai pas trouvé le temps mais je n’oublie pas

Partager ce message


Lien à poster
Partager sur d’autres sites
Le 09/03/2020 à 19:50, jjacques68 a dit :

@Krikroff, je vais abuser... désolé... mais tu as pu regarder cette histoire de socket ?? :) 

 

Alors...

 

L'idée serait de déclarer une variable dans ton init

self.socketClosed = true

Puis implémenter 2 méthodes: connect / closeSocket (ou les noms que tu souhaites bien évidement ;) )

 

socket1.thumb.PNG.3ffa4ae400e43351b82597a86995c2b2.PNG

 

Et enfin d'utiliser une méthode principale pour ton send, afin de faire toutes les vérifications d'usage avant le socket:write

 

exemple:

 

function pioneer__vsx__device:send(cmd, expected, callback)
    if (self.socketClosed == true) then
        self:connect()
    end
    -- This equipments is using the 1st command's <CR> as a trigger to wake up the main CPU
    self.socket:write("\r", {
        success = function()
            self:debug("wake up the main CPU")
            -- wait 100msec
            fibaro.sleep(100)
            self.socket:write("\r"..cmd.."\r", {
                success = function() -- the function that will be triggered when the data is correctly sent
                    self:debug("data sent")
                    if (type(expected) == 'string') then
                        self:waitForResponse(expected, callback) -- launching a data readout "loop" waitng for expected data               
                    else
                        -- no results expected
                    end
                end,
                error = function(err) -- the function that will be triggered in the event of an error in data transmission
                    self:debug("error while sending data")
                    self:closeSocket()
                end
            })
        end,
        error = function(err) -- the function that will be triggered in the event of an error in data transmission
            self:debug("error while sending data")
            self:closeSocket()
            fibaro.setTimeout(2000, function() self:send(cmd, expected, callback) end)
        end
    }) 

end

Du coup, en cas de déconnexion / reco. de ton serveur et au prochain send depuis le HC3, si ça plante le script ferme le socket, puis ré-ouvre et réexecutre la dernière commande.

Il faudrait certainement encore "durcir" tout cela mais ok dans mes simulations.

 

Amuse-toi bien :)

 

 

  • Like 1

Partager ce message


Lien à poster
Partager sur d’autres sites

Merci pour ton aide, alors de ce que je comprends... :) 

 

ok pour la variable d’état du statut de la socket, avec les méthodes open et close...

 

Pour la méthode send : 

- tu testes la variable d’état avant tout (avec reconnexion si nécessaire)

- Tu fais un premier envoi pour tester la connexion avec le “/r “ (ou autre chose).

      -> Si celui échoue, tu fermes, (méthode CloseSocket et tu relances la méthode Send au complet après le Timeout - l’état de la variable fera relancer la connexion)

      -> Si celui réussi, tu envoies la data.

            -> Si celui réussi, ok super, on attend une éventuelle réponse (“expected=string“)

            -> Si celui-ci échoue, tu fermes, c’est tout ? tu ne renvoie pas la trame ?

 

Il faudrait pas ajouter un setTimeout avec au préalable CloseSocket dans le deuxième envoi ? exactement comme dans le premier ?

 

Tu utilises le Callback ?

Je vois pas trop son intérêt pour le moment...

Modifié par jjacques68

Partager ce message


Lien à poster
Partager sur d’autres sites

@Krikroff : ça marche très bien ! je crois que j'ai fait mon très gros gros boulet de base !

 

J'ai oublié de mettre le "\n" à la fin des trames... erreur de débutant... donc j'avais cette impression que la socket ne se reconnectait pas à la première trame renvoyée !!

 

mais en fait si !!! Simplement je ne la voyais pas sur le serveur !

 

bref en tout cas la reconnexion fonctionne très bien !!

 

Mille merci pour le temps consacré !

 

 

  • Like 1

Partager ce message


Lien à poster
Partager sur d’autres sites

et j'ai modifié le type du QA en binarySwitch, comme ça j'ai une jolie icone quand la socket crash et une autre quand c'est OK.

 

Par contre, en cas de perte du serveur TCP, il tourne en boucle pour tenter de se reconnecter... c'est le but ! mais j'espère que c'est pas trop gênant... côté ressouces...

Partager ce message


Lien à poster
Partager sur d’autres sites

C’est top bien joué.

Côté ressources, cela occupe un thread le temps du setTimeout mais pas bloquant ;)

Pourquoi n’utilises tu pas plutôt un type Binary sensor ? Sauf si tu as besoin de faire une action genre On/Off sur ton QA

Partager ce message


Lien à poster
Partager sur d’autres sites

ah oui pas bête..., pas besoin de faire de tunOn ou turnOff... avais pas pensé au Sensor ;) 

Partager ce message


Lien à poster
Partager sur d’autres sites

mouai j’ai encore des difficultés de connexion au serveur ...

je vais regardé ça demain.

 

bon une fois que c’est ouvert c’est très stable.

Partager ce message


Lien à poster
Partager sur d’autres sites

ouai nan j'arrive pas réouvrir la socket ...

 

j'ai cette erreur lors de la tentative de réouverture : 

Citation

error opening socket : Operation canceled

 

pourtant, côté serveur je vois bien la demande arriver...

 

et le serveur l'accepte bien

 

 

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai l'impression que les boucles dans le Open_Socket() et dans le Send() se prennent les pied dans le tapis...

Partager ce message


Lien à poster
Partager sur d’autres sites

pffffffffffff...

 

je trouve pas ce qui cloche !

 

Ce qui est sûr

 

- La connexion du QA est immédiate si le serveur est déjà lancé.

- Si le serveur est redémarré, le QA n'arrive pas à se reconnecter malgré le code qu'on mis en place.

 

Pour le refaire partir, je suis obligé de modifier n'importe quoi dans le QA afin de de pouvoir l'enregistrer et donc de le redémarrer.

 

En gros : le serveur en premier, le QA en deuxième. L'inverse est impossible. Faut redémarrer le QA.

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Là je suis charrette mais si tu veux tu peux je peux dans la soirée jeter un œil

Partager ce message


Lien à poster
Partager sur d’autres sites

si tu as le temps je veux bien, ça fait 5 heures que je fais toutes sortes d'essais... :( 

 

Ce qui est dingue, c'est que j'utilise le bout de code suivant (dan sun autre QA) pour un envoi unique et à la demande, et j'ai aucun soucis !!

 

le truc, c'est dans ce cas là j'ouvre la socket à chaque envoi, chose que je peux pas faire dans l'autre QA, car il y aune grosse quantité de données envoyées à la suite... puis c'est pas normal...

 

function QuickApp:TurnOffPc(Ip)
    self.sockOff:connect(tostring(Ip), tonumber(self:getVariable("Port")),{
        
        success = function()
            self:debug("Socket OFF - opened")            
            fibaro.sleep(100)
            
            self.sockOff:write("shutdown;YWRtaW5zaHV0ZG93bg==", {
                success = function() self:debug("Socket OFF - Data sended") end,
                error = function(err)
                            self:debug("Socket OFF - error sending data : "..err)
                            self:Close_SocketOff()
                            fibaro.setTimeout(3000, function() self:TurnOffPc(Ip) end)
                        end
            })
        end,

        error = function(err)
            self:debug("Socket OFF - error opening : ",err)
            self:Close_SocketOff()
            fibaro.setTimeout(3000, function() self:TurnOffPc(Ip) end)          
        end
    })
end

 

 

Partager ce message


Lien à poster
Partager sur d’autres sites

Une remarque sur ton QuickApp:TurnOffPc(Ip), tu devrais juste vérifier que le socket et fermé et seulement si faire un connect et pas à chaque fois...

 

Quelle est l'utilité du fibaro.sleep(100) ?

 

Sinon possible d'avoir accès au code qui te pose problème ?

Partager ce message


Lien à poster
Partager sur d’autres sites

c'est celui sur lequel on avait déjà bossé :

 

function QuickApp:onInit()
    self.sock = net.TCPSocket({timeout = 2000})
    self.ip = self:getVariable("IP")
    self.port = tonumber(self:getVariable("Port")) 
    self:Open_Socket()
end

----------------------------------------------------------------
function QuickApp:Open_Socket()
    self.sock:connect(self.ip, self.port,{
        success = function()
            self:debug("Socket opened")
            self.socketClosed = false
            self:updateProperty("value", true)
            self:Send("Hello")
        end,
        error = function(err)
            self:debug("error opening socket : ",err)
            self.sock:close()
            self.socketClosed = true
            self:updateProperty("value", false)
        end
    })
end

----------------------------------------------------------------
function QuickApp:Close_Socket()   
    self.sock:close()
    self.socketClosed = true
    self:debug("Socket closed")
    self:updateProperty("value", false)
end

---------------------------------------------------------------
function QuickApp:Send(MaTrame)

    --reconnect if closed
    if self.socketClosed == true then 
        self:Open_Socket()
        fibaro.sleep(3000)
    end    

    --affiche la trame dans le label    
    self:updateView("LBL_Buffer", "text", tostring(MaTrame)) 

    --envoi la première trame d'essai
    self.sock:write("\n", {
    success = function()

            self:debug("First send = OK")

            --envoi la data
            self.sock:write(MaTrame.."\n", {
            success = function() 
                self:debug("Data send = OK") 
            end,

            error = function(err)
                self:debug("error sending data : "..err)
                self:Close_Socket()
                fibaro.sleep(500)
                self:Send(MaTrame)
            end
            })
    end,                

    error = function(err)
        self:debug("error sending first : "..err)
        self:Close_Socket()
        fibaro.sleep(500)
        self:Send(MaTrame)
    end
    })
end

 

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai l'impression que l'instruction close() ne libère pas les ressources correctement...

Partager ce message


Lien à poster
Partager sur d’autres sites

×