Experts: Programmation MQL5 pour les Traders - Codes Source du livre. Partie 7 - page 3

 
Je joins quelques corrections de bogues et améliorations dans les classes websockets.
Dossiers :
wss.zip  16 kb
 
Stanislav Korotky #:
Je joins quelques corrections de bogues et améliorations dans les classes websockets.
Merci pour vos efforts
 
Stanislav Korotky #:
D'autres corrections de bogues et améliorations pour la lecture du calendrier économique et l'exportation au format CSV ont été publiées dans la base de code. En particulier, l'algorithme de tri est corrigé pour le cas des grands tableaux triés (qui sont généralement reçus de l'API de calendrier MQL5), de sorte que les ralentissements et les dépassements de pile sont éliminés.

Il y a un petit bourgeon sur la ligne 126 de msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' en passant le 'url' complet au lieu du 'path', cela conduit à une erreur 403 sur le websocket python. J'ai dû faire ce changement pour que l'EA puisse se connecter avec succès à mon serveur python websocket.
J'aijoint le journal du serveur de Postman (réussi) et de l'EA avant que je fasse le changement (échoué)

Mais voici le problème suivant, mon serveur envoie automatiquement un keepalive ping à un intervalle configuré mais l'implémentation MT5 websocket ne semble pas répondre et ce comportement fait que le serveur abandonne toujours la connexion du client immédiatement après le timeout du ping.

S'il vous plaît, toute aide concernant le keepalive ping pong sera appréciée.

Dossiers :
passed.png  88 kb
failed.png  50 kb
 
pauldic #:

Il y a un petit bouton sur la ligne 126 de msclient.mqh "return connection.handshake(url, host, origin, custom_headers) ;" en passant le "url" complet au lieu du "path", on obtient l'erreur 403 sur le websocket python. J'ai dû faire ce changement pour que l'EA puisse se connecter avec succès à mon serveur python websocket.
J'aijoint le journal du serveur de Postman (réussi) et de l'EA avant que je fasse le changement (échoué)

Mais voici le problème suivant, mon serveur envoie automatiquement un keepalive ping à un intervalle configuré mais l'implémentation MT5 websocket ne semble pas répondre et ce comportement fait que le serveur abandonne toujours la connexion du client immédiatement après le timeout du ping.

S'il vous plaît, toute aide concernant le keepalive ping pong sera appréciée.

Bonjour, oui, vous pouvez utiliser le chemin dans l'appel du client :

template<typename T>
class WebSocketClient: public IWebSocketObserver
{
   ...
   bool open(const string custom_headers = NULL)
   {
      ...
      connection = new T(&this, socket, compression);
      return connection.handshake(path, host, origin, custom_headers);
   }

D'un autre côté, la syntaxe de requête HTTP supporte l'URI complet comme cible, et j'ai fait des tests avec les codes sources originaux contre différents serveurs (y compris le wss://ws.postman-echo.com/raw) et ils ont fonctionné normalement. Je ne sais donc pas s'il s'agit d'une violation ou si l'implémentation de votre python est trop rigide dans un sens.

Quant à ping/pong, cela devrait fonctionner. Jetez un oeil à wsprotocol.mqh :

   // Traite les trames de contrôle entrantes : envoie Pong sur Ping et ferme la connexion après une demande de fermeture.
   void processControlFrame(IWebSocketFrame *frame)
   {
      switch(frame.getType())
      {
      case WS_FRAME_OPCODE::WS_CLOSE_FRAME:
         if(closeRequested) // notre fermeture a été confirmée
         {
            Print("Server close ack");
         }
         else if(!disconnecting) // fermeture initiée par le serveur
         {
            if(openMessage) // toujours pas finalisé( !)
            {
               owner.onMessage(openMessage);
               openMessage = NULL;
            }
          
            WebSocketFrame temp(WS_FRAME_OPCODE::WS_CLOSE_FRAME); // envoie notre ack
            sendFrame(&temp);
         }
         close();
         break;
      case WS_FRAME_OPCODE::WS_PING_FRAME:
         {
            IWebSocketFrame *temp = WebSocketFrame::create(WS_FRAME_OPCODE::WS_PONG_FRAME, frame.getData());
            sendFrame(temp);
            delete temp;
         }
         break;
      }
   }

Donc s'il vous plaît, déboguez ce truc de votre côté, puisque vous avez le serveur qui envoie des pings à votre client.

 
BTW, j'ai oublié d'ajouter la nouvelle version de MQL5Book/StringUtils.mqh utilisée dans le ws/sources.
Dossiers :
 
Stanislav Korotky #:
BTW, j'ai oublié d'ajouter la nouvelle version de MQL5Book/StringUtils.mqh utilisée dans le ws/sources.
@Stanislav Korotky merci encore pour votre aide. En ce qui concerne le problème de ping pong avec le serveur websocket python, j'ai remarqué que le serveur envoie la requête ping sous forme de texte ou de données binaires et que le MT5 ne répond avec succès que lorsqu'un ping textuel est envoyé. Cela m'a aidé à trouver et à résoudre le problème.

Le problème se situe à la ligne 310 dans le switch/case de la frame ws ping, lorsque la frame websocket est créée :

IWebSocketFrame *temp = WebSocketFrame::create(WS_FRAME_OPCODE::WS_PONG_FRAME, frame.getData()) ;

frame.getData() le serveur ne semble reconnaître le pong que si les données du ping sont basées sur du texte/des chaînes mais échoue si un ping binaire a été envoyé

j'ai donc dû utiliser la surcharge frame.getData(uchar &buf[]) pour que cela fonctionne dans les deux cas. Je ne sais pas si je rencontrerais d'autres cas d'utilisation que (ma modification) briserait, mais jusqu'à présent tout semble aller pour le moment.


uchar data[]
; frame.getData(data)
; IWebSocketFrame *temp = WebSocketFrame::create(WS_FRAME_OPCODE: :WS_PONG_FRAME, data) ;

Par curiosité, je me demande pourquoi nous devons vérifier manuellement les messages à intervalles réguliers avec le gestionnaire d'événements OnTimer(), alors que je vois quelque chose qui ressemble à une fonction de gestion d'événements OnMessage pour les messages entrants. Bien que la configuration actuelle fonctionne telle quelle, j'apprécierais que vous puissiez m'éclairer davantage sur la raison de cette approche.
 
pauldic #:
@Stanislav Korotky merci encore pour votre aide. En ce qui concerne le problème de ping pong avec le serveur python websocket, j'ai remarqué que le serveur envoie la requête ping sous forme de texte ou de données binaires et que le MT5 ne répond avec succès que lorsqu'un ping sous forme de texte est envoyé. Cela m'a aidé à trouver et à résoudre le problème.

Le problème se situe à la ligne 310 dans le switch/case de la frame ws ping, lorsque la frame websocket est créée :

IWebSocketFrame *temp = WebSocketFrame::create(WS_FRAME_OPCODE::WS_PONG_FRAME, frame.getData()) ;

frame.getData() le serveur ne semble reconnaître le pong que si les données du ping sont basées sur du texte/des chaînes mais échoue si un ping binaire a été envoyé

j'ai donc dû utiliser la surcharge frame.getData(uchar &buf[]) pour que cela fonctionne dans les deux cas. Je ne sais pas si je rencontrerais d'autres cas d'utilisation que (mon changement) briserait, mais jusqu'à présent tout semble bien pour l'instant.


uchar data[]
; frame.getData(data)
; IWebSocketFrame *temp = WebSocketFrame::create(WS_FRAME_OPCODE: :WS_PONG_FRAME, data) ;

Par curiosité, je me demande pourquoi nous devons vérifier manuellement les messages à intervalles réguliers avec le gestionnaire d'événements OnTimer(), alors que je vois quelque chose qui ressemble à une fonction de gestion d'événements OnMessage pour les messages entrants. Bien que la configuration actuelle fonctionne telle quelle, j'apprécierais que vous m'éclairiez davantage sur la raison de cette approche.

Je vous remercie pour vos efforts. Je vais envisager d'inclure vos modifications dans les codes sources.

En ce qui concerne OnTimer et OnMessage, ils sont fournis parce que vous pouvez vouloir mettre en œuvre une logique de traitement différente dans votre application. La lecture des sockets est bloquante au niveau de l'API MQL5, avec un délai spécifié. Par conséquent, si vous souhaitez uniquement attendre de nouveaux messages, les traiter et générer des réponses automatiquement, vous pouvez le faire en mode bloquant (avec un délai infini pour la lecture et sans OnTimer) - c'est-à-dire que l'application est en quelque sorte gelée pendant l'attente des données. Mais si vous devez fournir une interface utilisateur réactive (ce qui est le cas de l'application de chat), vous pouvez fixer un petit délai pour les tentatives de lecture courtes dans OnTimer-handler (elles peuvent produire des données vides si le délai est dépassé) et surveiller les actions de l'utilisateur entre les deux.

 
Stanislav Korotky #:

Je vous remercie pour vos efforts. J'envisagerai d'inclure vos modifications dans les codes sources.

En ce qui concerne OnTimer et OnMessage, ils sont fournis parce que vous pouvez vouloir mettre en œuvre une logique de traitement différente dans votre application. La lecture des sockets est bloquante au niveau de l'API MQL5, avec un délai spécifié. Par conséquent, si vous souhaitez uniquement attendre de nouveaux messages, les traiter et générer des réponses automatiquement, vous pouvez le faire en mode bloquant (avec un délai infini pour la lecture et sans OnTimer) - c'est-à-dire que l'application est en quelque sorte gelée pendant l'attente des données. Mais si vous devez fournir une interface utilisateur réactive (ce qui est le cas de l'application de chat), vous pouvez fixer un petit délai pour les tentatives de lecture courtes dans OnTimer-handler (elles peuvent produire des données vides si elles sont interrompues) et surveiller les actions de l'utilisateur entre-temps.

Merci encore à @StanislavKorotky pour ses éclaircissements .
 

Voici une nouvelle série de corrections de bugs mineurs et d'améliorations des codes sources de wss dans une archive zip.

Entre autres choses, vous pouvez maintenant analyser les échecs de mise à niveau (par exemple, si une autorisation est requise et qu'un code de statut autre que 101 est renvoyé) et prendre des mesures :

if(wss.open(initial_headers))
{
   // succès, flux normal
}
else
{
  // quelque chose s'est mal passé - pas de mise à jour ou pas de connexion
  if(wss.isConnected())
  {
     Print("Can't upgrade to websockets");
     bool resolved = false;
     string headers[][2]; // response
     if(wss.getHeaders(headers))
     {
       // TODO : analyser le statut et le résoudre
       resolved = ... // vrai/faux
     }
     if(!resolved)
     {
       wss.close(); // fermer pour empêcher d'autres tentatives de lecture/écriture à partir d'un socket non mis à jour
     }
     else
     {
       wss.open(updated_headers); // try again
     }
  }
}

StringUtils.mqh et URL.mqh ont également reçu d'importantes corrections.

Dossiers :
wss.zip  16 kb
URL.mqh  5 kb
 

Bonjour @Stanislav Korotky, je suis nouveau sur MQL5. J'ai trouvé que vous aviez mis en ligne un fichier wss.zip pour l'utilisation de websocket. Comment l'utiliser, y a t-il une démo ou quelque chose que je peux apprendre. Je vous remercie sincèrement !

Stanislav Korotky
Stanislav Korotky
  • 2025.04.07
  • www.mql5.com
Trader's profile