Expert Advisors: MQL5 Programming for Traders – Quellcodes aus dem Buch. Teil 7 - Seite 3

 
Ich füge einige Bugfixes und Verbesserungen in den Websockets-Klassen bei.
Dateien:
wss.zip  16 kb
 
Stanislav Korotky #:
Ich füge einige Bugfixes und Verbesserungen in den Websockets-Klassen bei.
Vielen Dank für Ihre Bemühungen
 
Stanislav Korotky #:
Weitere Bugfixes und Verbesserungen für das Lesen des Wirtschaftskalenders und den Export nach CSV wurden in der Codebase veröffentlicht. Insbesondere wurde der Sortieralgorithmus für den Fall von meist sortierten großen Arrays (die normalerweise von der MQL5-Kalender-API empfangen werden) korrigiert, so dass Verlangsamungen und Stack-Overflows beseitigt werden.

Es gibt einen kleinen Fehler in der msclient.mqh Zeile 126 'return connection.handshake(url, host, origin, custom_headers);', der bei der Übergabe der vollständigen 'url' anstelle von 'path' zu einem Fehler 403 auf dem Python Websocket führt. Ich musste diese Änderung vornehmen, bevor sich der EA erfolgreich mit meinem Python-Websocket-Server verbinden konnte.
Ichhabe das Server-Protokoll von Postman (bestanden) und EA, bevor ich die Änderung (fehlgeschlagen)

Aber hier ist das nächste Problem, mein Server sendet automatisch ein Keepalive-Ping in einem konfigurierten Intervall, aber die MT5-Websocket-Implementierung scheint nicht zu reagieren und dieses Verhalten Ursache der Server immer die Client-Verbindung sofort nach dem Ping-Timeout zu beenden.

Bitte jede Hilfe in Bezug auf die Keepalive-Ping-Pong wird geschätzt werden

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

Es gibt einen kleinen Fehler in der msclient.mqh Zeile 126 'return connection.handshake(url, host, origin, custom_headers);', bei dem die vollständige 'url' anstelle von 'path' zu einem Fehler 403 auf dem Python-Websocket führt. Ich musste diese Änderung vornehmen, bevor sich der EA erfolgreich mit meinem Python-Websocket-Server verbinden konnte.
Ichhabe das Server-Protokoll von Postman (bestanden) und EA, bevor ich die Änderung (fehlgeschlagen)

Aber hier ist das nächste Problem, mein Server sendet automatisch ein Keepalive-Ping in einem konfigurierten Intervall, aber die MT5-Websocket-Implementierung scheint nicht zu reagieren und dieses Verhalten Ursache der Server immer die Client-Verbindung sofort nach dem Ping-Timeout zu beenden.

Bitte jede Hilfe in Bezug auf die Keepalive-Ping-Pong wird geschätzt werden

Hallo, ja, Sie können path im Client-Aufruf verwenden:

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);
   }

Andererseits unterstützt die HTTP-Anforderungssyntax vollständige URIs als Ziel, und ich habe Tests mit den ursprünglichen Quellcodes mit verschiedenen Servern durchgeführt (einschließlich wss://ws.postman-echo.com/raw) und sie funktionierten normal. Ich bin mir also nicht sicher, ob es sich um einen Verstoß handelt oder ob Ihre Python-Implementierung in gewisser Weise zu starr ist.

Was ping/pong betrifft, so sollte es funktionieren. Werfen Sie einen Blick auf wsprotocol.mqh:

   // Behandlung eingehender Kontrollrahmen: sendet Pong auf Ping und schließt die Verbindung nach einer Close-Anfrage
   void processControlFrame(IWebSocketFrame *frame)
   {
      switch(frame.getType())
      {
      case WS_FRAME_OPCODE::WS_CLOSE_FRAME:
         if(closeRequested) // unser Abschluss wurde bestätigt
         {
            Print("Server close ack");
         }
         else if(!disconnecting) // vom Server initiiertes Schließen
         {
            if(openMessage) // noch nicht abgeschlossen(!)
            {
               owner.onMessage(openMessage);
               openMessage = NULL;
            }
          
            WebSocketFrame temp(WS_FRAME_OPCODE::WS_CLOSE_FRAME); // unseren Ack senden
            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;
      }
   }

Bitte debuggen Sie dieses Zeug auf Ihrer Seite, da der Server Pings an Ihren Client sendet.

 
BTW, ich habe vergessen, neue Version von MQL5Book/StringUtils.mqh in der ws/sources verwendet hinzufügen.
Dateien:
 
Stanislav Korotky #:
BTW, ich habe vergessen, neue Version von MQL5Book/StringUtils.mqh in der ws/sources verwendet hinzufügen.
@Stanislav Korotky danke nochmals für Ihre Hilfe. Bezüglich des Ping-Pong-Problems mit dem Python-Websocket-Server habe ich bei der weiteren Fehlersuche festgestellt, dass der Server die Ping-Anfrage entweder als Text oder als Binärdaten sendet und der MT5 nur dann erfolgreich antwortet, wenn ein textbasierter Ping gesendet wird. Dies half mir, das Problem aufzuspüren und zu beheben.

Das Problem befindet sich in Zeile 310 innerhalb des ws ping frame switch/case, wenn der Websocket-Frame erstellt wird:

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

frame.getData() scheint der Server den Pong nur zu bestätigen, wenn die Ping-Daten text-/stringbasiert sind, schlägt aber fehl, wenn ein binärer Ping gesendet wurde

daher muss ich die überladene frame.getData(uchar &buf[]) verwenden , damit es in beiden Fällen funktioniert. Ich weiß nicht, ob ich andere Anwendungsfälle treffen würde, die (meine Änderung) brechen wird, aber so weit so gut scheint alles in Ordnung für jetzt.


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

Nur aus Neugier frage ich mich, warum wir mit dem OnTimer()-Ereignishandler manuell nach Nachrichten in Intervallen suchen müssen, während ich etwas sehe, das wie eine Ereignishandlerfunktion OnMessage für eingehende Nachrichten aussieht. Obwohl die aktuelle Einrichtung funktioniert, wie es ist, aber ich werde zu schätzen wissen, wenn Sie mehr auf, warum dieser Ansatz aufklären können
 
pauldic #:
@Stanislav Korotky danke nochmals für Ihre Hilfe. Bezüglich des Ping-Pong-Problems mit dem Python-Websocket-Server habe ich bei der weiteren Fehlersuche festgestellt, dass der Server die Ping-Anfrage entweder als Text oder als Binärdaten sendet und der MT5 nur dann erfolgreich antwortet, wenn ein textbasierter Ping gesendet wird. Dies half mir, das Problem aufzuspüren und zu beheben.

Das Problem befindet sich in Zeile 310 innerhalb des ws ping frame switch/case, wenn der Websocket-Frame erstellt wird:

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

frame.getData() scheint der Server den Pong nur zu bestätigen, wenn die Ping-Daten text-/stringbasiert sind, schlägt aber fehl, wenn ein binärer Ping gesendet wurde

daher muss ich die überladene frame.getData(uchar &buf[]) verwenden , damit es in beiden Fällen funktioniert. Ich weiß nicht, ob ich andere Anwendungsfälle treffen würde, die (meine Änderung) brechen wird, aber so weit so gut scheint alles in Ordnung für jetzt.


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

Nur aus Neugier frage ich mich, warum wir mit dem OnTimer()-Ereignishandler manuell nach Nachrichten in Intervallen suchen müssen, während ich etwas sehe, das wie eine Ereignishandlerfunktion OnMessage für eingehende Nachrichten aussieht. Obwohl die aktuelle Einrichtung funktioniert, wie es ist, aber ich würde es schätzen, wenn Sie mehr darüber aufklären können, warum dieser Ansatz

Ich danke Ihnen für Ihre Bemühungen. Ich werde in Betracht ziehen, Ihre Änderungen in den Quellcode aufzunehmen.

Was OnTimer und OnMessage betrifft, so werden sie zur Verfügung gestellt, weil Sie vielleicht eine andere Verarbeitungslogik in Ihrer Anwendung implementieren möchten. Das Lesen aus den Sockets ist auf MQL5-API-Ebene blockierend, mit angegebenem Timeout. Wenn Sie also nur daran interessiert sind, auf neue Nachrichten zu warten, diese zu verarbeiten und automatisch Antworten zu generieren, können Sie dies im blockierenden Modus tun (mit unendlichem Timeout beim Lesen und ohne OnTimer) - das heißt, die Anwendung ist sozusagen eingefroren, während sie auf Daten wartet. Aber wenn Sie eine reaktionsfähige Benutzeroberfläche für einen Benutzer bereitstellen müssen (was bei der Chat-App der Fall ist), dann setzen Sie eine kleine Zeitüberschreitung für kurze Leseversuche im OnTimer-Handler (sie können leere Daten produzieren, wenn die Zeit abgelaufen ist) und überwachen die Benutzeraktionen dazwischen.

 
Stanislav Korotky #:

Ich danke Ihnen für Ihre Bemühungen. Ich werde in Erwägung ziehen, Ihre Bearbeitungen in die Quellcodes aufzunehmen.

OnTimer und OnMessage werden zur Verfügung gestellt, weil Sie vielleicht eine andere Verarbeitungslogik in Ihrer Anwendung implementieren möchten. Das Lesen aus den Sockets ist auf MQL5-API-Ebene blockierend, mit angegebenem Timeout. Wenn Sie also nur daran interessiert sind, auf neue Nachrichten zu warten, diese zu verarbeiten und automatisch Antworten zu generieren, können Sie dies im blockierenden Modus tun (mit unendlichem Timeout beim Lesen und ohne OnTimer) - das heißt, die Anwendung ist sozusagen eingefroren, während sie auf Daten wartet. Aber wenn Sie eine reaktionsfähige Benutzeroberfläche für einen Benutzer bereitstellen müssen (was bei der Chat-App der Fall ist), dann setzen Sie eine kleine Zeitüberschreitung für kurze Leseversuche im OnTimer-Handler (sie können leere Daten produzieren, wenn die Zeit abgelaufen ist) und überwachen die Benutzeraktionen dazwischen.

Vielen Dank nochmals an @StanislavKorotky für die Klarstellung
 

Hier ist eine weitere Reihe kleinerer Bugfixes und Verbesserungen der wss-Quellcodes in einem Zip-Archiv.

Unter anderem kann man nun erfolglose Upgrades analysieren (z.B. wenn eine Autorisierung erforderlich ist und ein anderer Statuscode als 101 zurückgegeben wird) und Maßnahmen ergreifen:

if(wss.open(initial_headers))
{
   // Erfolg, normaler Ablauf
}
else
{
  // Etwas ist schief gelaufen - nicht aufgerüstet oder nicht angeschlossen
  if(wss.isConnected())
  {
     Print("Can't upgrade to websockets");
     bool resolved = false;
     string headers[][2]; // response
     if(wss.getHeaders(headers))
     {
       // TODO: Analyse des Status und Lösung des Problems
       resolved = ... // wahr/falsch
     }
     if(!resolved)
     {
       wss.close(); // schließen, um weitere Lese-/Schreibversuche von einem nicht aktualisierten Socket zu verhindern
     }
     else
     {
       wss.open(updated_headers); // try again
     }
  }
}

Auch StringUtils.mqh und URL.mqh haben wichtige Korrekturen erhalten.

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

Hallo @Stanislav Korotky, ich bin neu in der MQL5. Finden Sie, dass Sie eine wss.zip-Datei für Websocket verwenden zu posten. Wie man es verwendet, gibt es eine Demo oder etwas, das ich lernen kann. Ich danke Ihnen herzlich!

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