DOCUMENTATION EN LIGNE
DE WINDEVWEBDEV ET WINDEV MOBILE

Aide / WLangage / Fonctions WLangage / Communication / Sockets
  • Présentation
  • Fonctionnement
  • Exemple
  • Code de connexion au serveur et d'envoi de messages
  • Code de la procédure Callback de retour
  • Exemple de serveur de WebSockets développé en WINDEV
WINDEV
WindowsLinuxUniversal Windows 10 AppJavaEtats et RequêtesCode Utilisateur (MCU)
WEBDEV
WindowsLinuxPHPWEBDEV - Code Navigateur
WINDEV Mobile
AndroidWidget AndroidiPhone/iPadWidget IOSApple WatchMac CatalystUniversal Windows 10 App
Autres
Procédures stockées
Présentation
Les WebSockets permettent de communiquer depuis une application Web (site Intranet ou Internet exécuté dans un navigateur) vers un serveur Web en utilisant des sockets.
Remarque : Le serveur de WebSockets peut correspondre à une application WINDEV, WEBDEV ou autre. Cette page présente un exemple de serveur de WebSockets réalisé en WLangage avec WINDEV.
Fonctionnement
Pour envoyer un message depuis une application Web en utilisant les WebSockets, il est nécessaire de programmer les traitements suivants côté navigateur :
  • connexion au serveur,
  • envoi de message,
  • déconnexion.
Pour cela, vous disposez des mêmes fonctions WLangage que côté serveur :
SocketConnectePermet de se connecter au serveur de webSockets.
SocketEcritPermet d'envoyer un message au serveur de webSockets.
SocketExistePermet de vérifier si la socket servant à la connexion n'a pas déjà été créée.
SocketFermePermet de fermer la socket une fois les envois de messages terminés.
Exemple

Code de connexion au serveur et d'envoi de messages

SocketConnecte("client", "ws://<AdresseIPServeur>:5001", ProcRetour)
 
// Remarque : en cas de connexion en mode sécurisé, utiliser 'wss' à la place de 'ws'
// SocketConnecte("client", "wss://<AdresseIPServeur>:5001", ProcRetour)
// Attention : en code serveur, il est nécessaire d'utiliser
// la fonction SocketCréeSSL à la place de la fonction SocketCrée
 
Info("fermeture socket...")
 
SocketFerme("client")

Code de la procédure Callback de retour

PROCÉDURE ProcRetour(nEvenement, sMessage)
gsRes est une chaîne = ""
 
SELON nEvenement
CAS SocketOuverture :
SocketEcrit("client", "Texte du message envoyé depuis le navigateur.")
RETOUR
CAS SocketMessage : gsRes += [RC] + "Réception du message : " + sMessage
 
CAS SocketFermeture : gsRes += [RC] + "Fermeture de la socket"
 
CAS SocketErreur : gsRes += [RC] + "Erreur de la socket : " + sMessage
RETOUR
AUTRE CAS
 
FIN
Info(gsRes)
Exemple de serveur de WebSockets développé en WINDEV
Voici un exemple de code pour réaliser un serveur de WebSockets avec WINDEV. Ce code doit être adapté selon votre configuration et vos besoins (Adresse IP du serveur, ports à ouvrir, ...).
Avant de modifier cet exemple, il est nécessaire de connaître la technique et la théorie des WebSockets. Cet exemple n'a pas pour but d'expliquer le fonctionnement théorique des WebSockets. Il montre juste comment utiliser des WebSockets.
Pour créer un serveur de WebSockets en WINDEV :
  1. Créez un projet WINDEV.
  2. Créez une fenêtre vierge
  3. Créer une procédure locale pServeurSocketEcoute par exemple pour écouter les messages envoyés par le client depuis son site Web. Le code de cette procédure locale est le suivant :
    // Code de la procédure locale : Ecoute des messages envoyés par le client socket.
     
    PROCÉDURE pServeurSocketEcoute()
     
    ENTETE_WEBSOCKET_CLIENT est une chaîne = "Sec-WebSocket-Key: "
    ENTETE_WEBSOCKET_PROTOCOL est une chaîne = "Sec-WebSocket-Protocol: "
     
    // Création du serveur de sockets pour effectuer l'écoute des messages arrivants
    SI SocketCrée("Serveur", 5001) = Faux ALORS
    Erreur("Erreur de création " + ErreurInfo(errMessage))
    SINON
    // Process (ou thread) d'écoute des messages
    // Ce traitement est exécuté en tâche de fond
    // On écoute en permanence car on ne sait pas quand un message arrive
    ThreadExécute("Thread1", threadNormal, ProcédureAttente)
    FIN
     
    //-----------------------------------------------------------
    // Procédure appelée dans le thread d'écoute
    PROCÉDURE INTERNE ProcédureAttente()
     
    // Boucle sans fin afin d'attendre une connexion d'un client.
    // Dès qu'un client se connecte pour envoyer un message,
    // un thread est lancé afin de gérer les messages arrivants.
    // La procédure ProcédureGestion gère les messages arrivants.
    BOUCLE
    Multitâche(0)
    SI SocketAttendConnexion("Serveur") ALORS
    Canal est une chaîne
    Canal = SocketAccepte("Serveur")
    SocketChangeModeTransmission(Canal, SocketSansMarqueurFin)
    ThreadExécute(Canal, threadNormal, ProcédureGestion, Canal)
    FIN
    FIN
     
    //--------------------------------------------------------------
    // Code de la procédure de gestion des messages arrivants
    PROCÉDURE INTERNE ProcédureGestion(Canal)
     
    sRequête est une chaîne ANSI
    tabProtocol est un tableau de chaînes
    sRes est une chaîne
    sProtocol est une chaîne = "JSON"
     
    // Lecture de la socket jusqu'à avoir la séquence de terminaison d'un message
    // Dans notre cas, 2 RC qui se suivent
    TANTQUE ChaîneOccurrence(sRequête, RC + RC) = 0
    sRequête += SocketLit(Canal)
    FIN
     
    // Traitement de la requête qui arrive
    // La variable sRequête contient le message à traiter
    // On analyse la chaîne
    sClé est une chaîne ANSI
    POUR TOUTE CHAÎNE sLigne DE sRequête SÉPARÉE PAR RC
    Trace(sLigne)
    SI sLigne [=  ENTETE_WEBSOCKET_CLIENT ALORS
    sClé = sLigne[[Taille(ENTETE_WEBSOCKET_CLIENT)+1 À]]
    SINON SI sLigne [= ENTETE_WEBSOCKET_PROTOCOL
    // Récupération des protocoles
    ChaîneVersTableau(ExtraitChaîne(sLigne, 2, ":"), tabProtocol, ",")
    FIN
    FIN
     
     
    // Préparation de la réponse à envoyer au client
    sRes = [
    HTTP/1.1 101 Web Socket Protocol Handshake
    Upgrade: websocket
    Connection: Upgrade
    WebSocket-Origin: <AdresseIPServeur>
    WebSocket-Location: ws://<AdresseIPServeur>:5001
    Sec-WebSocket-Accept: %1
    ]
    SI tabProtocol..Occurrence <> 0 ALORS
    sRes += [RC] + "Sec-WebSocket-Protocol: %2"
    sProtocol = Majuscule(SansEspace(tabProtocol[1]))
    sRes = ChaîneConstruit(sRes, Crypte(HashChaîne(HA_SHA_160,
    sClé + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"), "",
    compresseAucun, encodeBASE64), sProtocol + RC + RC)
     
    SINON
    sRes = ChaîneConstruit(sRes, Crypte(HashChaîne(HA_SHA_160,
    sClé + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"), "",
    compresseAucun, encodeBASE64) + RC + RC)
    FIN
     
    // Envoi la réponse au client
    SocketEcrit(Canal, sRes)
     
    BOUCLE
    bufFrame est un Buffer = SocketLit(Canal)
    SI 0 = Taille(bufFrame) ALORS
    Trace("Break")
    BREAK
    FIN
     
    nLongueurFrame est un entier = Asc(bufFrame[[2]]) & 127
    bufMasque est un Buffer
    bufDonne est un Buffer
    SI 126 = nLongueurFrame ALORS
    nLongueurFrame = HexaVersEntier(
    BufferVersHexa(bufFrame[[3 SUR 2]])) //BufferVersEntier(bufFrame, 3, 2)
    bufMasque = bufFrame[[5 À 8]]
    bufDonne = bufFrame[[9 À]]
    SINON SI 127 = nLongueurFrame ALORS
    nLongueurFrame = HexaVersEntier(BufferVersHexa(bufFrame[[3 SUR 8]]))
    bufMasque = bufFrame[[11 À 14]]
    bufDonne = bufFrame[[15 À]]
    SINON
    bufMasque = bufFrame[[3 À 6]]
    bufDonne = bufFrame[[7 À]]
    FIN
    sTexte est une chaîne ANSI
    POUR i = 1 _À_ Taille(bufDonne)
    sTexte += Caract(OUExclusifBinaire(
    Asc(bufDonne[[i]]), Asc(bufMasque[[(i - 1) modulo 4 + 1]])))
    FIN
    Trace(UTF8VersAnsi(sTexte))
     
    // Formatage de la réponse
    SELON sProtocol
    CAS "XML"
    sRes = [
    <XML status="ok">
    %1
    </XML>
    ]
    CAS "JSON"
    sRes = [
    { "status" : "ok",
    "reponse": "%1" }
    ]
    AUTRE CAS
    sRes = ChaîneVersUTF8("Format non supporté !")
    FIN
    sRes = ChaîneConstruit(sRes, sTexte)
     
    nLongueurRéponse est un entier = Taille(sRes)
    bufRéponse est un Buffer
    bufRéponse[[1]] = Caract(OUBinaire(0x80, ETBinaire(0x1, 0xF)))
    SI nLongueurRéponse <= 125 ALORS
    bufRéponse[[2]] = Caract(nLongueurRéponse)
    SINON SI nLongueurRéponse <= 65536 ALORS
    bufRéponse[[2]] = Caract(126)
    // Manque l'écriture de la longueur
    bufRéponse += HexaVersBuffer(Droite(EntierVersHexa(nLongueurRéponse), 4))
    SINON
    bufRéponse[[2]] = Caract(127)
    // Manque l'écriture de la longueur
    bufRéponse += HexaVersBuffer(EntierVersHexa(nLongueurRéponse))
    FIN
    SocketEcrit(Canal, bufRéponse + sRes)
    FIN
     
    FIN
    FIN
Version minimum requise
  • Version 22
Commentaires
Assignation de la clé d'encryption au niveau du client
Bonjour,

Bravo l'exemple du serveur marche du premier coup.

Le serveur utilise une clé "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" pour communiquer avec le client. Si l'on modifie la clé la connexion ne fonctionne plus. Où/comment faut il mettre la clé dans le code du client pour rétablir la connexion.

Bien cordialement,
Marc
Marc
04 oct. 2019
PROCEDURE Envoi message par Serveur webSocket au Clients web
PROCEDURE wsEnvoiMsgACahqueCanalAvecProtocolWebSocket(sMsg,canal)

///eCode est un OpCode
bufDonnées est un Buffer //= [
// "{ "status" : "ok","reponse": "Votre msg est : text" }"
//]
//gbufDonne
vMsgServeur est un Variant
vMsgServeur.status = "ok"
vMsgServeur.reponse = sMsg
bufDonnées = VariantVersJSON(vMsgServeur)

bDernièreTrame est un booléen = Vrai

bufPayload est un Buffer

// La connexion doit être valide
//SI PAS m_bOuvert ALORS RETOUR

//
nFinBit est un octet = (bDernièreTrame ? 0x80 SINON 0x00)
bufPayload += Caract( ( nFinBit | 1) )

// Taille encodable sur 1 octet
SI Taille(bufDonnées) < 126 ALORS

nTaille est un octet = Taille(bufDonnées)
bufPayload += Caract( ( nTaille) )

// Taille encodable sur 2 octets
SINON SI Taille(bufDonnées) <= 0xFFFF

bufPayload += Caract( (126) )

nTaille est un entier sur 2 sans signe = Taille(bufDonnées)
bufPayload += HexaVersBuffer(EntierVersHexa(nTaille, 4)[[6 SUR 4]])

// Taille encodable
moez-wm
27 sep. 2019

Dernière modification : 25/05/2022

Signaler une erreur ou faire une suggestion | Aide en ligne locale