Asesores Expertos: Programación en MQL5 para tráders: códigos fuente del libro: Parte 7 - página 3

 
Adjunto algunas correcciones de errores y mejoras en las clases websockets.
Archivos adjuntos:
wss.zip  16 kb
 
Stanislav Korotky #:
Adjunto algunas correcciones de errores y mejoras en las clases websockets.
Gracias por vuestros esfuerzos
 
Stanislav Korotky #:
Se han publicado en la base de código más correcciones de errores y mejoras para la lectura del calendario económico y su exportación a CSV. En concreto, se ha corregido el algoritmo de ordenación para el caso de matrices grandes mayoritariamente ordenadas (que suelen recibirse de la API de calendario MQL5), de forma que se eliminan las ralentizaciones y los desbordamientos de pila.

Hay un pequeño brote en la línea 126 de msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' pasando la 'url' completa en lugar de 'path' lleva al error 403 en websocket python. Tuve que hacer ese cambio b4 la EA podría conectarse con éxito a mi servidor websocket python.
Adjunto el registro del servidor de Postman (aprobado) y EA antes de hacer el cambio (fallido)

Pero aquí está el siguiente problema, mi servidor envía automáticamente un ping keepalive en un intervalo configurado, pero la implementación websocket MT5 no parece responder y este comportamiento hace que el servidor siempre deje caer la conexión del cliente inmediatamente después del tiempo de espera de ping.

Por favor, cualquier ayuda con respecto al ping pong keepalive será apreciada.

Archivos adjuntos:
passed.png  88 kb
failed.png  50 kb
 
pauldic #:

Hay un pequeño brote en la línea 126 de msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' pasando la 'url' completa en lugar de 'path' lleva al error 403 en websocket python. Tuve que hacer ese cambio b4 la EA podría conectarse con éxito a mi servidor websocket python.
Adjunto el registro del servidor de Postman (aprobado) y EA antes de hacer el cambio (fallido)

Pero aquí está el siguiente problema, mi servidor envía automáticamente un ping keepalive en un intervalo configurado, pero la implementación websocket MT5 no parece responder y este comportamiento hace que el servidor siempre deje caer la conexión del cliente inmediatamente después del tiempo de espera de ping.

Por favor, cualquier ayuda con respecto al ping pong keepalive será apreciada.

Hola, sí se puede usar path en la llamada al cliente:

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

Por otro lado, la sintaxis de petición HTTP soporta URI completas como destino, y he hecho pruebas con los códigos fuente originales contra diferentes servidores (incluyendo el wss://ws.postman-echo.com/raw) y funcionaron normalmente. Así que no estoy seguro si es una violación o la implementación de tu python es demasiado rígida en cierto sentido.

En cuanto a ping/pong, debería funcionar. Echa un vistazo a wsprotocol.mqh:

   // Manejar las tramas de control entrantes: envía Pong en Ping y cierra la conexión tras una petición Close
   void processControlFrame(IWebSocketFrame *frame)
   {
      switch(frame.getType())
      {
      case WS_FRAME_OPCODE::WS_CLOSE_FRAME:
         if(closeRequested) // nuestro cierre fue confirmado
         {
            Print("Server close ack");
         }
         else if(!disconnecting) // cierre iniciado por el servidor
         {
            if(openMessage) // ¡todavía sin finalizar!
            {
               owner.onMessage(openMessage);
               openMessage = NULL;
            }
          
            WebSocketFrame temp(WS_FRAME_OPCODE::WS_CLOSE_FRAME); // envía nuestro 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;
      }
   }

Así que por favor depura estas cosas en tu lado, ya que tienes el servidor enviando pings a tu cliente.

 
BTW, olvidé añadir la nueva versión de MQL5Book/StringUtils.mqh usada en el ws/sources.
Archivos adjuntos:
 
Stanislav Korotky #:
BTW, olvidé añadir la nueva versión de MQL5Book/StringUtils.mqh usada en el ws/sources.
@Stanislav Korotky gracias de nuevo por su ayuda. Con respecto al problema del ping pong con el servidor python websocket, al depurar mas me di cuenta de que el servidor envia la solicitud de ping como texto o datos binarios y la MT5 responde con exito solo cuando se envia un ping basado en texto. Esto me ayudó a localizar y solucionar el problema.

El problema está en la línea 310 dentro del switch/case del marco ws ping, cuando se crea el marco websocket:

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

frame.getData() el servidor parece que sólo reconoce el pong si los datos del ping son de texto/cadena pero falla si se envía un ping binario

así que tengo que usar el frame.getData(uchar &buf[]) sobrecargado para que funcione en ambos casos. No sé si me encontraría con otros casos de uso que (mi cambio) romperá pero de momento todo parece ir bien.


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

Sólo por curiosidad, me pregunto por qué tenemos que comprobar manualmente si hay mensajes a intervalos con el manejador de eventos OnTimer(), mientras que veo algo que parece una función de manejador de eventos OnMessage mensajes entrantes. Aunque la configuración actual funciona como es, pero voy a apreciar si se puede iluminar más sobre por qué este enfoque
 
pauldic #:
@Stanislav Korotky gracias de nuevo por tu ayuda. En cuanto a la cuestión de ping pong con el servidor websocket python, en la depuración adicional me di cuenta de que el servidor envía la solicitud de ping, ya sea como texto o datos binarios y la MT5 responde con éxito sólo cuando un ping basado en texto se ha enviado. Esto me ayudó a localizar y solucionar el problema.

El problema está en la línea 310 dentro del switch/case del marco ws ping, cuando se crea el marco websocket:

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

frame.getData() el servidor parece que sólo reconoce el pong si los datos del ping son de texto/cadena pero falla si se envía un ping binario

así que tengo que usar el frame.getData(uchar &buf[]) sobrecargado para que funcione en ambos casos. No sé si me encontraría con otros casos de uso que (mi cambio) romperá pero de momento todo parece ir bien.


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

Sólo por curiosidad, me pregunto por qué tenemos que comprobar manualmente si hay mensajes a intervalos con el manejador de eventos OnTimer(), mientras que veo algo que parece una función de manejador de eventos OnMessage mensajes entrantes. Aunque la configuración actual funciona como es, pero voy a apreciar si se puede iluminar más sobre por qué este enfoque

Gracias por sus esfuerzos. Voy a considerar la inclusión de sus ediciones en los códigos fuente.

En cuanto a OnTimer y OnMessage, se proporcionan porque es posible que desee implementar una lógica de procesamiento diferente en su aplicación. La lectura de los sockets es bloqueante a nivel de la API MQL5, con un tiempo de espera especificado. Por lo tanto, si usted está interesado sólo en la espera de nuevos mensajes, su procesamiento y la generación de respuestas de forma automática, puede hacerlo en modo de bloqueo (con tiempo de espera infinito en la lectura, y sin OnTimer) - es decir, la aplicación es una especie de congelado a la espera de datos. Pero si usted necesita proporcionar una interfaz de usuario de respuesta para un usuario (que es el caso de la aplicación de chat), a continuación, establecer un pequeño tiempo de espera para los intentos de lectura de corta duración en OnTimer-handler (que pueden producir datos vacíos si se agota el tiempo) y supervisar las acciones del usuario en el medio.

 
Stanislav Korotky #:

Gracias por tus esfuerzos. Consideraré incluir tus ediciones en los códigos fuente.

En cuanto a OnTimer y OnMessage, se proporcionan porque es posible que desee implementar una lógica de procesamiento diferente en su aplicación. La lectura de los sockets es bloqueante a nivel de la API MQL5, con un tiempo de espera especificado. Por lo tanto, si usted está interesado sólo en la espera de nuevos mensajes, su procesamiento y la generación de respuestas de forma automática, puede hacerlo en modo de bloqueo (con tiempo de espera infinito en la lectura, y sin OnTimer) - es decir, la aplicación es una especie de congelado a la espera de datos. Pero si usted necesita proporcionar una interfaz de usuario de respuesta para un usuario (que es el caso de la aplicación de chat), a continuación, establecer un pequeño tiempo de espera para los intentos de lectura de corta duración en OnTimer-handler (que pueden producir datos vacíos si se agota el tiempo) y supervisar las acciones del usuario en el medio.

Gracias de nuevo @StanislavKorotky por la aclaración .
 

Aquí hay otro montón de correcciones de errores menores y mejoras de los códigos fuente wss en un archivo zip.

Entre otras cosas, ahora puede analizar la actualización fallida (por ejemplo, si se requiere autorización y se devuelve un código de estado distinto de 101) y tomar medidas:

if(wss.open(initial_headers))
{
   // éxito, flujo normal
}
else
{
  // algo salió mal - no se actualizó o no se conectó
  if(wss.isConnected())
  {
     Print("Can't upgrade to websockets");
     bool resolved = false;
     string headers[][2]; // response
     if(wss.getHeaders(headers))
     {
       // TODO: analizar el estado y solucionarlo
       resolved = ... // verdadero/falso
     }
     if(!resolved)
     {
       wss.close(); // cerrar para evitar nuevos intentos de lectura/escritura desde un socket no actualizado
     }
     else
     {
       wss.open(updated_headers); // try again
     }
  }
}

También StringUtils.mqh y URL.mqh han recibido importantes correcciones.

Archivos adjuntos:
wss.zip  16 kb
URL.mqh  5 kb
 

Hola @Stanislav Korotky, soy nuevo en el MQL5. Encontré que usted publica un archivo wss.zip para el uso de websocket. ¿Cómo usarlo, ¿hay una demo o algo que pueda aprender. ¡Gracias sinceramente!

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