English Русский 中文 Español Deutsch 日本語 Português 한국어 Français Türkçe
Comunicare con MetaTrader 5 utilizzando pipe denominate senza utilizzare DLL

Comunicare con MetaTrader 5 utilizzando pipe denominate senza utilizzare DLL

MetaTrader 5Esempi | 11 gennaio 2022, 15:38
177 0
MetaQuotes
MetaQuotes

Introduzione

Molti sviluppatori affrontano lo stesso problema: come accedere alla sandbox del terminale di trading senza utilizzare DLL non sicure.

Uno dei metodi più semplici e sicuri consiste nell'utilizzare pipe denominate standard che funzionano come normali operazioni sui file. Consentono di organizzare la comunicazione client-server interprocessore tra i programmi. Sebbene sia già stato pubblicato un articolo Una soluzione senza DLL per comunicare tra terminali MetaTrader 5 utilizzando pipe denominate su questo argomento, il che dimostra l'abilitazione dell'accesso alle DLL, utilizzeremo standard e caratteristiche del client terminal.

È possibile trovare ulteriori informazioni sulle pipe denominate nella libreria MSDN, ma passeremo ad esempi pratici in C++ e MQL5. Implementeremo server, client, scambio di dati tra loro e quindi prestazioni di benchmark.


Implementazione del server

Codifichiamo un semplice server in C++. Uno script dal terminale si collegherà a questo server e scambierà dati con esso. Il core del server ha il seguente set di funzioni WinAPI:

Una volta aperta una pipe denominata, restituisce un handle di file che può essere utilizzato per le normali operazioni di lettura/scrittura sui file. Di conseguenza si ottiene un meccanismo molto semplice che non richiede alcuna conoscenza speciale nelle operazioni di rete.

Le pipe con nome hanno una caratteristica distintiva: possono essere sia locali che di rete. Ovvero, è facile implementare un server remoto che accetterà connessioni di rete dai client terminal.

Ecco un semplice esempio di creazione di un server locale come canale full-duplex che funziona in modalità di scambio di byte:

//--- open 
CPipeManager manager;

if(!manager.Create(L"\\\\.\\pipe\\MQL5.Pipe.Server"))
   return(-1);


//+------------------------------------------------------------------+
//| Create named pipe                                                |
//+------------------------------------------------------------------+
bool CPipeManager::Create(LPCWSTR pipename)
  {
//--- check parameters
   if(!pipename || *pipename==0) return(false);
//--- close old
   Close();
//--- create named pipe
   m_handle=CreateNamedPipe(pipename,PIPE_ACCESS_DUPLEX,
                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                            PIPE_UNLIMITED_INSTANCES,256*1024,256*1024,1000,NULL);

   if(m_handle==INVALID_HANDLE_VALUE)
     {
      wprintf(L"Creating pipe '%s' failed\n",pipename);
      return(false);
     }
//--- ok
   wprintf(L"Pipe '%s' created\n",pipename);
   return(true);
  }

Per ottenere una connessione client devi usare la funzione ConnectNamedPipe:

//+------------------------------------------------------------------+
//| Connect client                                                   |
//+------------------------------------------------------------------+
bool CPipeManager::ConnectClient(void)
  {
//--- pipe exists?
   if(m_handle==INVALID_HANDLE_VALUE) return(false);
//--- connected?
   if(!m_connected)
     {
      //--- connect
      if(ConnectNamedPipe(m_handle,NULL)==0)
        {
         //--- client already connected before ConnectNamedPipe?
         if(GetLastError()!=ERROR_PIPE_CONNECTED)
            return(false);
         //--- ok
        }
      m_connected=true;
     }
//---
   return(true);
  }

Lo scambio di dati è organizzato utilizzando 4 semplici funzioni:

  • CPipeManager::Send(void *data,size_t data_size)
  • CPipeManager::Read(void *data,size_t data_size)
  • CPipeManager::SendString(LPCSTR command)
  • CPipeManager::ReadString(LPSTR answer,size_t answer_maxlen)

Consentono di inviare/ricevere dati come dati binari o stringhe di testo ANSI in modalità compatibile MQL5. Inoltre, poiché CFilePipe in MQL5 apre un file in modalità ANSI per impostazione predefinita, le stringhe vengono convertite automaticamente in Unicode alla ricezione e all'invio. Se il tuo programma MQL5 apre un file in modalità Unicode (FILE_UNICODE), allora può scambiare stringhe Unicode (con firma iniziale BOM).


Implementazione del client

Scriveremo il nostro client in MQL5. Sarà in grado di eseguire normali operazioni sui file utilizzando la classe CFilePipe dalla libreria standard. Questa classe è quasi identica a CFileBin, ma contiene un'importante verifica della disponibilità dei dati in un file virtuale prima di leggere questi dati.

//+------------------------------------------------------------------+
//| Wait for incoming data                                           |
//+------------------------------------------------------------------+
bool CFilePipe::WaitForRead(const ulong size)
  {
//--- check handle and stop flag
   while(m_handle!=INVALID_HANDLE && !IsStopped())
     {
      //--- enough data?
      if(FileSize(m_handle)>=size)
         return(true);
      //--- wait a little
      Sleep(1);
     }
//--- failure
   return(false);
  }

//+------------------------------------------------------------------+
//| Read an array of variables of double type                        |
//+------------------------------------------------------------------+
uint CFilePipe::ReadDoubleArray(double &array[],const int start_item,const int items_count)
  {
//--- calculate size
   uint size=ArraySize(array);
   if(items_count!=WHOLE_ARRAY) size=items_count;
//--- check for data
   if(WaitForRead(size*sizeof(double)))
      return FileReadArray(m_handle,array,start_item,items_count);
//--- failure
   return(0);
  }

Le pipe denominate presentano differenze significative nell'implementazione delle modalità locale e di rete. Senza tale verifica, le operazioni in modalità rete restituiranno sempre un errore di lettura durante l'invio di grandi quantità di dati (oltre 64 KB).

Connettiamoci al server con due controlli: o al computer remoto denominato 'RemoteServerName' o al computer locale.

void OnStart()
  {
//--- wait for pipe server
   while(!IsStopped())
     {
      if(ExtPipe.Open("\\\\RemoteServerName\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break;
      if(ExtPipe.Open("\\\\.\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break;
      Sleep(250);
     }
   Print("Client: pipe opened");


Data Exchange

Dopo che la connessione è andata a buon fine, inviamo una stringa di testo con le informazioni di identificazione al server. La stringa Unicode verrà automaticamente convertita in ANSI, poiché il file viene aperto in modalità ANSI.

//--- send welcome message
   if(!ExtPipe.WriteString(__FILE__+" on MQL5 build "+IntegerToString(__MQ5BUILD__)))
     {
      Print("Client: sending welcome message failed");
      return;
     }

In risposta, il server invierà la sua stringa "Hello from pipe server" e l'intero 1234567890. Il client invierà ancora una volta la stringa "Test string" e l'intero 1234567890.

//--- read data from server
   string        str;
   int           value=0;

   if(!ExtPipe.ReadString(str))
     {
      Print("Client: reading string failed");
      return;
     }
   Print("Server: ",str," received");

   if(!ExtPipe.ReadInteger(value))
     {
      Print("Client: reading integer failed");
      return;
     }
   Print("Server: ",value," received");
//--- send data to server
   if(!ExtPipe.WriteString("Test string"))
     {
      Print("Client: sending string failed");
      return;
     }

   if(!ExtPipe.WriteInteger(value))
     {
      Print("Client: sending integer failed");
      return;
     }

OK, abbiamo finito con il semplice scambio di dati. Ora è il momento del benchmark delle prestazioni.


Benchmark delle prestazioni

Come test, invieremo 1 gigabyte di dati come array dei numeri di tipo double in blocchi di 8 megabyte dal server al client, quindi verificheremo la correttezza dei blocchi e misureremo la velocità di trasferimento.

Ecco questo codice nel server C++:

//--- benchmark
   double  volume=0.0;
   double *buffer=new double[1024*1024];   // 8 Mb

   wprintf(L"Server: start benchmark\n");
   if(buffer)
     {
      //--- fill the buffer
      for(size_t j=0;j<1024*1024;j++)
         buffer[j]=j;
      //--- send 8 Mb * 128 = 1024 Mb to client
      DWORD   ticks=GetTickCount();

      for(size_t i=0;i<128;i++)
        {
         //--- setup guard signatures
         buffer[0]=i;
         buffer[1024*1024-1]=i+1024*1024-1;
         //--- 
         if(!manager.Send(buffer,sizeof(double)*1024*1024))
           {
            wprintf(L"Server: benchmark failed, %d\n",GetLastError());
            break;
           }
         volume+=sizeof(double)*1024*1024;
         wprintf(L".");
        }
      wprintf(L"\n");
      //--- read confirmation
      if(!manager.Read(&value,sizeof(value)) || value!=12345)
         wprintf(L"Server: benchmark confirmation failed\n");
      //--- show statistics
      ticks=GetTickCount()-ticks;
      if(ticks>0)
         wprintf(L"Server: %.0lf Mb sent at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks);
      //---
      delete[] buffer;
     }

e nel client MQL5:

//--- benchmark
   double buffer[];
   double volume=0.0;

   if(ArrayResize(buffer,1024*1024,0)==1024*1024)
     {
      uint  ticks=GetTickCount();
      //--- read 8 Mb * 128 = 1024 Mb from server
      for(int i=0;i<128;i++)
        {
         uint items=ExtPipe.ReadDoubleArray(buffer);
         if(items!=1024*1024)
           {
            Print("Client: benchmark failed after ",volume/1024," Kb, ",items," items received");
            break;
           }
         //--- check the data
         if(buffer[0]!=i || buffer[1024*1024-1]!=i+1024*1024-1)
           {
            Print("Client: benchmark invalid content");
            break;
           }
         //---
         volume+=sizeof(double)*1024*1024;
        }
      //--- send confirmation
      value=12345;
      if(!ExtPipe.WriteInteger(value))
         Print("Client: benchmark confirmation failed ");
      //--- show statistics
      ticks=GetTickCount()-ticks;
      if(ticks>0)
         printf("Client: %.0lf Mb received at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks);
      //---
      ArrayFree(buffer);
     }

Da notare che il primo e l'ultimo elemento dei blocchi trasferiti vengono controllati per assicurarsi che non ci siano stati errori durante il trasferimento. Inoltre, quando il trasferimento è completo, il client invia un segnale di conferma al server sull'avvenuta ricezione dei dati. Se non utilizzerai le conferme finali, incontrerai facilmente una perdita di dati se una delle parti chiude la connessione troppo presto.

Esegui localmente il server PipeServer.exe e allega lo script PipeClient.mq5 a qualsiasi grafico:

PipeServer.exe PipeClient.mq5
MQL5 Pipe Server
Copyright 2012, MetaQuotes Software Corp.
Pipe '\\.\pipe\MQL5.Pipe.Server' created
Client: waiting for connection...
Client: connected as 'PipeClient.mq5 on MQL5 build 705'
Server: send string
Server: send integer
Server: read string
Server: 'Test string' received
Server: read integer
Server: 1234567890 received
Server: start benchmark
......................................................
........
Server: 1024 Mb sent at 2921 Mb per second
PipeClient (EURUSD,H1)  Client: pipe opened
PipeClient (EURUSD,H1)  Server: Hello from pipe server received
PipeClient (EURUSD,H1)  Server: 1234567890 received
PipeClient (EURUSD,H1)  Client: 1024 Mb received at 2921 Mb per second


Per lo scambio locale, la velocità di trasferimento è davvero sorprendente: quasi 3 gigabyte al secondo. Ciò significa che le pipe denominate possono essere utilizzate per trasferire quasi qualsiasi quantità di dati nei programmi MQL5.

Ora esaminiamo le prestazioni di trasferimento dei dati in una normale LAN da 1 gigabit:

PipeServer.exe PipeClient.mq5
MQL5 Pipe Server
Copyright 2012, MetaQuotes Software Corp.
Pipe '\\.\pipe\MQL5.Pipe.Server' created
Client: waiting for connection...
Client: connected as 'PipeClient.mq5 on MQL5 build 705'
Server: send string
Server: send integer
Server: read string
Server: 'Test string' received
Server: read integer
Server: 1234567890 received
Server: start benchmark
......................................................
........
Server: 1024 Mb sent at 63 Mb per second
PipeClient (EURUSD,H1)  Client: pipe opened
PipeClient (EURUSD,H1)  Server: Hello from pipe server received
PipeClient (EURUSD,H1)  Server: 1234567890 received
PipeClient (EURUSD,H1)  Client: 1024 Mb received at 63 Mb per second


Nella rete locale è stato trasferito 1 gigabyte di dati alla velocità di 63 megabyte al secondo, il che è molto buono. In effetti è il 63% della larghezza di banda massima della rete gigabit.


Conclusione

Il sistema di protezione della piattaforma di trading MetaTrader 5 non consente ai programmi MQL5 di essere eseguiti al di fuori della loro sandbox, proteggendo i trader dalle minacce quando utilizzano Expert Advisor non attendibili. Utilizzando le pipe denominate puoi creare facilmente integrazioni con software di terze parti e gestire gli EA dall'esterno. In sicurezza.

Tradotto dal russo da MetaQuotes Ltd.
Articolo originale: https://www.mql5.com/ru/articles/503

File allegati |
pipeclient.mq5 (3.15 KB)
pipeserver.zip (43.59 KB)
Come abbonarsi ai segnali di trading Come abbonarsi ai segnali di trading
Il servizio Segnali introduce il social trading con MetaTrader 4 e MetaTrader 5. Il servizio è integrato nella piattaforma di trading e consente a chiunque di copiare facilmente le operazioni di trader professionisti. Seleziona una delle migliaia di fornitori di segnali, iscriviti in pochi clic e le operazioni del fornitore verranno copiate sul tuo account.
Come preparare le quotazioni MetaTrader 5 per altre applicazioni Come preparare le quotazioni MetaTrader 5 per altre applicazioni
L'articolo descrive gli esempi di creazione di directory, copia di dati, archiviazione, utilizzo dei simboli nel Market Watch o nell'elenco comune, nonché esempi di errori di gestione, ecc. Tutti questi elementi possono eventualmente essere raccolti in un unico script per archiviare i dati in un formato definito dall'utente.
Come scrivere una buona descrizione per un prodotto del Market Come scrivere una buona descrizione per un prodotto del Market
Il Market MQL5 ha molti prodotti in vendita, ma alcune delle loro descrizioni lasciano molto a desiderare. Molti testi hanno ovviamente bisogno di miglioramenti, poiché i trader comuni non sono in grado di comprenderli. Questo articolo ti aiuterà a mettere il tuo prodotto sotto una luce migliore. Utilizza i nostri consigli per scrivere una descrizione accattivante che mostrerà facilmente ai tuoi clienti cosa stai vendendo esattamente.
Come acquistare un robot di trading dal Market MetaTrader e installarlo? Come acquistare un robot di trading dal Market MetaTrader e installarlo?
Un prodotto del Market MetaTrader può essere acquistato sul sito MQL5.com o direttamente dalle piattaforme di trading MetaTrader 4 e MetaTrader 5. Scegli il prodotto che desideri e che si adatta al tuo stile di trading, pagalo utilizzando il tuo metodo di pagamento preferito e attiva il prodotto.