Sistemi Esperti: MQL5 Programming for Traders – Source Codes from the Book. Parte 7 - pagina 3

 
Allego alcune correzioni di bug e miglioramenti nelle classi websockets.
File:
wss.zip  16 kb
 
Stanislav Korotky #:
Allego alcuni bugfix e miglioramenti nelle classi websockets.
Grazie per i vostri sforzi
 
Stanislav Korotky #:
Nel codice sono state pubblicate altre correzioni e miglioramenti per la lettura del calendario economico e l'esportazione in CSV. In particolare, è stato corretto l'algoritmo di ordinamento per il caso di array di grandi dimensioni per lo più ordinati (che di solito vengono ricevuti dall'API calendario di MQL5), in modo da eliminare rallentamenti e stack-overflow.

C'è una piccola gemma alla riga 126 di msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' passando l'intero 'url' invece di 'path' si ottiene l'errore 403 su python websocket. Ho dovuto apportare questa modifica prima che l'EA potesse connettersi con successo al mio server websocket python.
Hoallegato il log del server da Postman (superato) e dall'EA prima che apportassi la modifica (fallita)

Ma ecco il problema successivo, il mio server invia automaticamente un ping keepalive a un intervallo configurato, ma l'implementazione del websocket MT5 non sembra rispondere e questo comportamento fa sì che il server abbandoni sempre la connessione del client subito dopo il timeout del ping.

Per favore, qualsiasi aiuto per quanto riguarda il ping pong keepalive sarà apprezzato.

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

C'è un piccolo germoglio nella riga 126 di msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' passando l'intero 'url' invece di 'path' si ottiene l'errore 403 su python websocket. Ho dovuto apportare questa modifica prima che l'EA potesse connettersi con successo al mio server websocket python.
Hoallegato il log del server da Postman (superato) e dall'EA prima che apportassi la modifica (fallita)

Ma ecco il problema successivo, il mio server invia automaticamente un ping keepalive a un intervallo configurato, ma l'implementazione del websocket MT5 non sembra rispondere e questo comportamento fa sì che il server abbandoni sempre la connessione del client subito dopo il timeout del ping.

Per favore, qualsiasi aiuto per quanto riguarda il ping pong keepalive sarà apprezzato.

Ciao, sì, puoi usare il percorso nella chiamata del 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'altra parte, la sintassi della richiesta HTTP supporta l'URI completo come target, e ho fatto dei test con i codici sorgente originali su diversi server (incluso wss://ws.postman-echo.com/raw) e hanno funzionato normalmente. Quindi non sono sicuro che si tratti di una violazione o che l'implementazione di python sia troppo rigida.

Per quanto riguarda ping/pong, dovrebbe funzionare. Date un'occhiata a wsprotocol.mqh:

   // Gestisce i frame di controllo in arrivo: invia Pong su Ping e chiude la connessione dopo una richiesta di chiusura.
   void processControlFrame(IWebSocketFrame *frame)
   {
      switch(frame.getType())
      {
      case WS_FRAME_OPCODE::WS_CLOSE_FRAME:
         if(closeRequested) // la nostra chiusura è stata confermata
         {
            Print("Server close ack");
         }
         else if(!disconnecting) // chiusura avviata dal server
         {
            if(openMessage) // ancora non finalizzato(!)
            {
               owner.onMessage(openMessage);
               openMessage = NULL;
            }
          
            WebSocketFrame temp(WS_FRAME_OPCODE::WS_CLOSE_FRAME); // Inviare il nostro 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;
      }
   }

Quindi, per favore, eseguite il debug di queste cose sul vostro lato, dato che il server invia i ping al vostro client.

 
BTW, ho dimenticato di aggiungere la nuova versione di MQL5Book/StringUtils.mqh usata in ws/sources.
File:
 
Stanislav Korotky #:
BTW, ho dimenticato di aggiungere la nuova versione di MQL5Book/StringUtils.mqh utilizzata in ws/sources.
@Stanislav Korotky grazie ancora per il tuo aiuto. Per quanto riguarda il problema del ping pong con il server websocket python, dopo un ulteriore debug ho notato che il server invia la richiesta di ping sia come testo che come dati binari e l'MT5 risponde correttamente solo quando viene inviato un ping basato sul testo. Questo mi ha aiutato a rintracciare e risolvere il problema.

Il problema si trova alla riga 310 all'interno dello switch/case del frame ws ping, quando viene creato il frame websocket:

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

frame.getData() il server sembra riconoscere il pong solo se i dati del ping sono basati su testo/stringa ma fallisce se è stato inviato un ping binario

quindi ho dovuto usare il sovraccarico frame.getData(uchar &buf[]) per farlo funzionare in entrambi i casi. Non so se incontrerò altri casi d'uso che (la mia modifica) romperà, ma per ora sembra tutto a posto.


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

Per curiosità, mi chiedo perché dobbiamo controllare manualmente i messaggi a intervalli con il gestore di eventi OnTimer(), mentre vedo qualcosa che sembra una funzione di gestore di eventi OnMessage per i messaggi in arrivo. Anche se la configurazione attuale funziona così com'è, apprezzerei se poteste illuminarmi di più sul perché di questo approccio.
 
pauldic #:
@Stanislav Korotky grazie ancora per il tuo aiuto. Per quanto riguarda il problema del ping pong con il server websocket python, dopo un ulteriore debug ho notato che il server invia la richiesta di ping sia come testo che come dati binari e l'MT5 risponde correttamente solo quando viene inviato un ping basato sul testo. Questo mi ha aiutato a rintracciare e risolvere il problema.

Il problema si trova alla riga 310 all'interno dello switch/case del frame ws ping, quando viene creato il frame websocket:

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

frame.getData() il server sembra riconoscere il pong solo se i dati del ping sono basati su testo/stringa ma fallisce se è stato inviato un ping binario

quindi ho dovuto usare il sovraccarico frame.getData(uchar &buf[]) per farlo funzionare in entrambi i casi. Non so se incontrerò altri casi d'uso che (la mia modifica) romperà, ma per ora sembra tutto a posto.


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

Per curiosità, mi chiedo perché dobbiamo controllare manualmente i messaggi a intervalli con il gestore di eventi OnTimer(), mentre vedo qualcosa che sembra una funzione di gestore di eventi OnMessage per i messaggi in arrivo. Anche se la configurazione attuale funziona così com'è, apprezzerei se poteste illuminarmi di più sul perché di questo approccio
.

Grazie per il tuo impegno. Prenderò in considerazione la possibilità di includere le tue modifiche nel codice sorgente.

Per quanto riguarda OnTimer e OnMessage, sono forniti perché si potrebbe voler implementare una logica di elaborazione diversa nella propria applicazione. Le letture dai socket sono bloccanti a livello di API MQL5, con un timeout specificato. Pertanto, se si è interessati solo ad attendere nuovi messaggi, alla loro elaborazione e alla generazione automatica di risposte, è possibile farlo in modalità bloccante (con timeout infinito in lettura e senza OnTimer) - cioè l'applicazione è una sorta di blocco in attesa dei dati. Ma se è necessario fornire un'interfaccia utente reattiva (come nel caso dell'applicazione di chat), si può impostare un piccolo timeout per i tentativi di lettura brevi in OnTimer-handler (possono produrre dati vuoti se il timeout è scaduto) e monitorare le azioni dell'utente nel mezzo.

 
Stanislav Korotky #:

Grazie per i vostri sforzi. Prenderò in considerazione la possibilità di includere le vostre modifiche nel codice sorgente.

Per quanto riguarda OnTimer e OnMessage, sono forniti perché si potrebbe voler implementare una logica di elaborazione diversa nella propria applicazione. Le letture dai socket sono bloccanti a livello di API MQL5, con un timeout specificato. Pertanto, se si è interessati solo ad attendere nuovi messaggi, alla loro elaborazione e alla generazione automatica di risposte, è possibile farlo in modalità bloccante (con timeout infinito in lettura e senza OnTimer) - cioè l'applicazione è una sorta di blocco in attesa dei dati. Ma se è necessario fornire un'interfaccia utente reattiva (come nel caso dell'applicazione di chat), si può impostare un piccolo timeout per i tentativi di lettura brevi in OnTimer-handler (possono produrre dati vuoti se il timeout è scaduto) e monitorare le azioni dell'utente nel mezzo.

Grazie ancora @StanislavKorotky per il chiarimento .
 

Ecco un altro gruppo di correzioni di bug minori e miglioramenti dei codici sorgente di wss in un archivio zip.

Tra le altre cose, è ora possibile analizzare gli aggiornamenti non riusciti (ad esempio, se è richiesta l'autorizzazione e viene restituito un codice di stato diverso da 101) e prendere provvedimenti:

if(wss.open(initial_headers))
{
   // successo, flusso normale
}
else
{
  // qualcosa è andato storto - non è stato aggiornato o non è stato collegato
  if(wss.isConnected())
  {
     Print("Can't upgrade to websockets");
     bool resolved = false;
     string headers[][2]; // response
     if(wss.getHeaders(headers))
     {
       // TODO: analizzare lo stato e risolverlo
       resolved = ... // vero/falso
     }
     if(!resolved)
     {
       wss.close(); // chiudere per impedire ulteriori tentativi di lettura/scrittura dal socket non aggiornato
     }
     else
     {
       wss.open(updated_headers); // try again
     }
  }
}

Anche StringUtils.mqh e URL.mqh hanno ricevuto importanti correzioni.

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

Ciao @Stanislav Korotky, sono nuovo di MQL5. Ho scoperto che hai pubblicato un file wss.zip per l'uso di websocket. Come si usa, c'è una demo o qualcosa che posso imparare. Grazie di cuore!

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