Especialistas: Programação no MQL5 para traders: códigos-fonte retirados do livro. Parte 7 - página 3

 
Estou anexando algumas correções de bugs e melhorias nas classes de websockets.
Arquivos anexados:
wss.zip  16 kb
 
Stanislav Korotky #:
Estou anexando algumas correções de erros e melhorias nas classes de websockets.
Obrigado por seus esforços
 
Stanislav Korotky #:
Mais correções de erros e melhorias na leitura do calendário econômico e na exportação para CSV foram publicadas na base de código. Especificamente, o algoritmo de classificação foi corrigido para o caso de matrizes grandes classificadas em sua maior parte (que geralmente são recebidas da API de calendário MQL5), de modo que a lentidão e os estouros de pilha sejam eliminados.

Há um pequeno problema na linha 126 do msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' ao passar o 'url' completo em vez de 'path', ocorre o erro 403 no websocket python. Tive que fazer essa alteração para que o EA pudesse se conectar com êxito ao meu servidor de websocket em python.
Anexeio log do servidor do Postman (aprovado) e do EA antes de fazer a alteração (falhou)

Mas aqui está o próximo problema: meu servidor envia automaticamente um ping keepalive em um intervalo configurado, mas a implementação do websocket do MT5 parece não responder e esse comportamento faz com que o servidor sempre interrompa a conexão do cliente imediatamente após o tempo limite do ping.

Agradecemos qualquer ajuda com relação ao ping pong keepalive.

Arquivos anexados:
passed.png  88 kb
failed.png  50 kb
 
pauldic #:

Há um pequeno botão na linha 126 do msclient.mqh 'return connection.handshake(url, host, origin, custom_headers);' ao passar o 'url' completo em vez de 'path', ocorre o erro 403 no websocket python. Tive que fazer essa alteração para que o EA pudesse se conectar com êxito ao meu servidor de websocket em python.
Anexeio log do servidor do Postman (aprovado) e do EA antes de fazer a alteração (falhou)

Mas aqui está o próximo problema: meu servidor envia automaticamente um ping keepalive em um intervalo configurado, mas a implementação do websocket do MT5 parece não responder e esse comportamento faz com que o servidor sempre interrompa a conexão do cliente imediatamente após o tempo limite do ping.

Agradecemos qualquer ajuda com relação ao ping pong keepalive.

Olá, sim, você pode usar o caminho na chamada do 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 outro lado, a sintaxe da solicitação HTTP suporta URI completo como destino, e fiz testes com os códigos-fonte originais em diferentes servidores (incluindo o wss://ws.postman-echo.com/raw) e eles funcionaram normalmente. Portanto, não tenho certeza se isso é uma violação ou se a implementação do python é muito rígida, de certa forma.

Quanto ao ping/pong, ele deve funcionar. Dê uma olhada no wsprotocol.mqh:

   // Manipular quadros de controle de entrada: envia Pong em Ping e fecha a conexão após uma solicitação Close
   void processControlFrame(IWebSocketFrame *frame)
   {
      switch(frame.getType())
      {
      case WS_FRAME_OPCODE::WS_CLOSE_FRAME:
         if(closeRequested) // nosso fechamento foi confirmado
         {
            Print("Server close ack");
         }
         else if(!disconnecting) // fechamento iniciado pelo servidor
         {
            if(openMessage) // ainda não finalizado(!)
            {
               owner.onMessage(openMessage);
               openMessage = NULL;
            }
          
            WebSocketFrame temp(WS_FRAME_OPCODE::WS_CLOSE_FRAME); // enviar nosso 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;
      }
   }

Portanto, depure essas coisas do seu lado, já que o servidor está enviando pings para o cliente.

 
Aliás, esqueci de adicionar a nova versão do MQL5Book/StringUtils.mqh usado no ws/sources.
Arquivos anexados:
 
Stanislav Korotky #:
Aliás, esqueci de adicionar a nova versão do MQL5Book/StringUtils.mqh usado no ws/sources.
@Stanislav Korotky, obrigado novamente por sua ajuda. Com relação ao problema do ping pong com o servidor websocket python, após uma depuração mais aprofundada, notei que o servidor envia a solicitação de ping como texto ou dados binários e o MT5 responde com êxito somente quando um ping baseado em texto é enviado. Isso me ajudou a rastrear e corrigir o problema.

O problema está na linha 310 do switch/case do quadro de ping do ws, quando o quadro do websocket é criado:

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

frame.getData() o servidor parece reconhecer o pong somente se os dados do ping forem baseados em texto/string, mas falha se um ping binário tiver sido enviado

portanto, tenho que usar o frame.getData(uchar &buf[]) sobrecarregado para fazê-lo funcionar em ambos os casos. Não sei se encontraria outros casos de uso que (minha alteração) quebraria, mas até agora tudo parece estar bem.


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

Só por curiosidade, estou me perguntando por que temos de verificar manualmente se há mensagens em intervalos com o manipulador de eventos OnTimer(), enquanto vejo algo que parece ser uma função de manipulador de eventos OnMessage de mensagens recebidas. Embora a configuração atual funcione como está, eu agradeceria se você pudesse esclarecer melhor o porquê dessa abordagem
 
pauldic #:
@Stanislav Korotky, obrigado novamente por sua ajuda. Com relação ao problema de ping pong com o servidor websocket python, após uma depuração mais aprofundada, percebi que o servidor envia a solicitação de ping como texto ou dados binários e o MT5 responde com êxito somente quando um ping baseado em texto é enviado. Isso me ajudou a rastrear e corrigir o problema.

O problema está na linha 310 do switch/case do quadro de ping do ws, quando o quadro do websocket é criado:

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

frame.getData() o servidor parece reconhecer o pong somente se os dados do ping forem baseados em texto/string, mas falha se um ping binário tiver sido enviado

portanto, tenho que usar o frame.getData(uchar &buf[]) sobrecarregado para fazê-lo funcionar em ambos os casos. Não sei se encontraria outros casos de uso que (minha alteração) quebraria, mas até agora tudo parece estar bem.


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

Só por curiosidade, estou me perguntando por que temos de verificar manualmente se há mensagens em intervalos com o manipulador de eventos OnTimer(), enquanto vejo algo que parece ser uma função de manipulador de eventos OnMessage de mensagens recebidas. Embora a configuração atual funcione como está, gostaria que você me esclarecesse melhor o porquê dessa abordagem

Obrigado por seu esforço. Considerarei a possibilidade de incluir suas edições nos códigos-fonte.

Quanto a OnTimer e OnMessage, eles são fornecidos porque você pode querer implementar uma lógica de processamento diferente em seu aplicativo. A leitura dos soquetes está bloqueando no nível da API MQL5, com tempo limite especificado. Portanto, se você estiver interessado apenas em aguardar novas mensagens, seu processamento e a geração automática de respostas, poderá fazê-lo no modo de bloqueio (com tempo limite infinito na leitura e sem OnTimer) - ou seja, o aplicativo fica como que congelado enquanto aguarda os dados. Mas se precisar fornecer uma interface de usuário responsiva para um usuário (que é o caso do aplicativo de bate-papo), defina um pequeno tempo limite para tentativas curtas de leitura no manipulador OnTimer (elas podem produzir dados vazios se o tempo limite for ultrapassado) e monitore as ações do usuário nesse intervalo.

 
Stanislav Korotky #:

Obrigado por seu esforço. Considerarei a possibilidade de incluir suas edições nos códigos-fonte.

Quanto a OnTimer e OnMessage, eles são fornecidos porque você pode querer implementar uma lógica de processamento diferente em seu aplicativo. A leitura dos soquetes está bloqueando no nível da API MQL5, com tempo limite especificado. Portanto, se você estiver interessado apenas em aguardar novas mensagens, seu processamento e a geração automática de respostas, poderá fazê-lo no modo de bloqueio (com tempo limite infinito na leitura e sem OnTimer) - ou seja, o aplicativo fica como que congelado enquanto aguarda os dados. Mas se precisar fornecer uma interface de usuário responsiva para um usuário (que é o caso do aplicativo de bate-papo), defina um pequeno tempo limite para tentativas curtas de leitura no manipulador OnTimer (elas podem produzir dados vazios se o tempo limite for ultrapassado) e monitore as ações do usuário nesse intervalo.

Mais uma vez, obrigado @StanislavKorotky pelo esclarecimento
 

Aqui está outro conjunto de pequenas correções de erros e melhorias nos códigos-fonte do wss em um arquivo zip.

Entre outras coisas, agora você pode analisar atualizações malsucedidas (por exemplo, se a autorização for necessária e o código de status diferente de 101 for retornado) e tomar medidas:

if(wss.open(initial_headers))
{
   // sucesso, fluxo normal
}
else
{
  // algo deu errado - não foi atualizado ou não está conectado
  if(wss.isConnected())
  {
     Print("Can't upgrade to websockets");
     bool resolved = false;
     string headers[][2]; // response
     if(wss.getHeaders(headers))
     {
       // TODO: analisar o status e resolvê-lo
       resolved = ... // verdadeiro/falso
     }
     if(!resolved)
     {
       wss.close(); // fechar para evitar novas tentativas de leitura/gravação de um soquete não atualizado
     }
     else
     {
       wss.open(updated_headers); // try again
     }
  }
}

Além disso, o StringUtils.mqh e o URL.mqh também receberam correções importantes.

Arquivos anexados:
wss.zip  16 kb
URL.mqh  5 kb
 

Olá @Stanislav Korotky, sou novo no MQL5. Descobri que você publicou um arquivo wss.zip para uso de websocket. Como usá-lo, há uma demonstração ou algo que eu possa aprender. Muito obrigado!

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