Download MetaTrader 5

Expert Advisor MetaTrader 4 para Intercâmbio de Informações com o Mundo Exterior

28 setembro 2015, 16:12
Sergey Sartakov
0
487

Introdução

Aqui é uma ferramenta de software que fornece Expert Advisors MetaTrader 4 com a possibilidade da criação de servidor e clientes ao mesmo tempo. Os clientes podem estabelecer conexões tanto para seus próprios servidores como para quaisquer outros tipos de servidores que fornecem conexões de protocolo peer-to-peer. A ferramenta de software oferecido consiste de dois componentes:

  • NetEventsProc.exe - É um processo do Windows que funciona em modo de fundo (sem console) e execuçõess (paradas), quando necessários pelo segundo componente, NetEventsProcDLL.dll. Pela solicitação de aplicativos, ele pode criar para servidor e clientes ao mesmo tempo: tanto para seus próprios servidores como para quaisquer outros tipos de servidores que fornecem conexões de protocolo peer-to-peer. Por exemplo, você pode criar clientes que trocam informações com websites. Claro, se você pode suportar o protocolo HTTP.

  • NetEventsProcDLL.dll - É a interface entre o aplicativo que necessita dos serviços do processo NetEventsProc.exe e o processo NetEventsProc.exe em si. Graças à interface de DLL, quaisquer programas escritos em qualquer linguagem de programação pode usar essa ferramenta de software para a troca de informações nos dois sentidos. O Expert Advisor MetaTrader 4 é apenas um exemplo do possível uso desta ferramenta de software.

Este artigo está estruturado da seguinte forma:

  • Iniciação Rápida: o uso prático da ferramenta de software oferecida é demonstrada primeiramente de forma simples, em seguida com exemplos mais complexos.

  • Especificação da Interface DLL: descrição detalhada das funções DLL e serviços fornecidos pelo processo NetEventsProc.exe ao abordar estas funções a partir de aplicativos.

  • Implementaçao do Projeto: Os detalhes desta implementação do projeto são esclarecidas quando possível.

O arquivo anexado NetServerClient.zip contém dois projetos Microsoft Visual Studio 2010 Ultimate: NetEventsProc - para construir NetEventsProc.exe; e NetEventsProcDLL - para construir NetEventsProcDLL.dll. Os códigos-fonte são comentados em detalhes. Você pode olhar para os detalhes da implementação e personalizar os projetos com suas necessidades específicas, se quiser.

NetEventsProc.exe implementa o servidor e os clientes usando soquetes assíncronos. Para comutar soquetes ao modo assíncrono, um dos possíveis métodos de operar no modo assíncrono é usado: soquetes de ligação aos eventos de rede WSAEventSelect (h_Socket, h_Event, FD_ALL_EVENTS).

Este projeto baseia-se no trabalho fundamental de um grande mestre, Elmue.


1. Iniciação Rápida

1.1. O arquivo exe.zip

Extrair este arquivo. Você vai encontrar os seguintes componentes:

  • NetEventsProcDLL.dll - Coloque na pasta "C:\Windows\System32\".

  • NetEventsProc.exe - crie a pasta "C:\NetEventsProc\" e coloque este componente nesta pasta. NetEventsProcDLL.dll irá procurar o módulo NetEventsProc.exe exatamente nesta pasta!

Os seguintes componentes deste arquivo proporcionam mecanismos para importar funções DLL a partir do arquivo NetEventsProcDLL.dll para aplicativos:

  • ImportNetEventsProcDLL.mqh - Protótipos das funções importadas para o Expert Advisor MetaTrader 4 a partir do NetEventsProcDLL.dll. Coloque esse arquivo na pasta de dados do terminal - "MetaTrader 4\experts\include\".

  • cNetEventsProcDLL.h - definição da classe C++ que contém todos os protótipos das funções DLL a partir do NetEventsProcDLL.dll. Incluindo essa classe no programa C++ permite importar todas as funções DLL a partir do NetEventsProcDLL.dll. Para os programas escritos em outras linguagens de programação, você deve ou importar cada função separadamente ou reescrever a definição desta classe, respectivamente.

  • NetEventsProcDLL.lib - módulo incluídos em programas que importam funções DLL a partir do NetEventsProcDLL.dll no modo de ligação dinâmica em tempo de carga (compilar com: /EHsc/link NetEventsProcDLL.lib).

Isto, na verdade, conclui o processo de configuração. Agora você pode escrever o Expert Advisors MetaTrader 4 e aplicativos em qualquer linguagem de programação usando funções DLL para a criação de servidor e clientes.

A fim de não divagar, damos os códigos-fonte dos arquivos ImportNetEventsProcDLL.mqh e cNetEventsProcDLL.h aqui mesmo. O arquivo de cabeçalho ImportNetEventsProcDLL.mqh contém protótipos das funções DLL importadas do programa NetEventsProcDLL.dll e duas funções de serviços adicionais:

string GetErrMsg(int s32_Error);
string FormatIP(int IP);

A função GetErrMsg converte para texto os códigos de retorno da funções DLL. A função FormatIP converte a representação binária do endereço IP para o formato de texto padrão como "93.127.110.161". Você deve colocar o arquivo ImportNetEventsProcDLL.mqh na pasta de dados terminal - "MetaTrader 4\experts\include\".

Aqui está o código fonte do ImportNetEventsProcDLL.mqh (somente uma parte do arquivo é fornecida, correspondendo diretamente à definição dos protótipos de funções DLL importadas):

// ImportNetEventsProcDLL.mqh

#import "NetEventsProcDLL.dll"
// Apenas para Clientes:
int ConnectTo(string  ps8_ServerIP,             // entrada- string ps8_ServerIP = "0123456789123456"
              int     s32_Port,                 // entrada 
              int&    h_Client[]);              // saída- int h_Client[1]
                 
int ConnectClose(int h_Client);                 // entrada
//
// Apenas para o servidor:
int ServerOpen(int  s32_Port);                  // entrada

int GetAllConnections(int& ph_Client[],         // saída - int ph_Client[62]
                      int& ps32_ClientIP[],     // saída - int ps32_ClientIP[62]
                      int& ps32_ClientCount[]); // saída - int ps32_ClientCount[1]
                       
int DisconnectClient(int h_Client);             // entrada

int ServerClose();
//
// Para ambos: clientes e servidor
int SendToInt   (int  h_Client,             // entrada
                 int& ps32_SendBuf[],       // entrada
                 int  s32_SendBufLen);      // entrada - tamanho do array SendBuf[] no elemento int 
                 
int SendToDouble(int     h_Client,          // entrada
                 double& pd_SendBuf[],      // entrada
                 int     s32_SendBufLen);   // entrada - o tamanho do array SendBuf[] no elemento double 
                 
int SendToString(int    h_Client,           // entrada
                 string ps8_SendBuf,        // entrada
                 int    s32_SendBufLen);    // entrada - tamanho da string SendBuf no elemento char
                 

int ReadFromInt   (int h_Client,            // entrada
                   int& ps32_ReadBuf[],     // entrada 
                   int  s32_ReadBufLen,     // entrada  - o tamanho do array ReadBuf[] no elemento int
                   int& ps32_ReadLen[]);    // saida- int ps32_ReadLen[1] - conta que realmente lê os dados no elemento int
                  
int ReadFromDouble(int     h_Client,        // entrada
                   double& pd_ReadBuf[],    // entrada
                   int     s32_ReadBufLen,  // entrada  - o tamanho do array ReadBuf[] no elemento double
                   int&    ps32_ReadLen[]); // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento double
                   
int ReadFromString(int     h_Client,        // entrada
                   string  ps8_ReadBuf,     // entrada
                   int     s32_ReadBufLen,  // entrada  - tamanho da string ReadBuf no elemento char
                   int&    ps32_ReadLen[]); // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento char
//                   
#import
//***************************************************************************************
...
...
...
// Obter uma leitura humana para mensagem de erro a um código de erro API
string GetErrMsg(int s32_Error)
{
   ...
   .. 
}

// Converte DWORD IP para string IP
string FormatIP(int IP)
{
   ...
   ...
   ...
}       

O arquivo cNetEventsProcDLL.h tem definição de classe C++ com todas as funções DLL importadas do arquivo NetEventsProcDLL.dll. Aqui está o código fonte desse arquivo:

//+---------------------------------------------------------------------------+
//|                                            cNetEventsProcDLL.h            |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |

//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+

// cNetEventsProcDLL.h

#pragma once

#define EXPFUNC __declspec(dllexport)
        
class cNetEventsProcDLL
{
public:
    static BOOL MessageDLL_PROCESS_ATTACH(void);
    static BOOL MessageDLL_PROCESS_DETACH(void);
//*******************************************************************************************************************
    static EXPFUNC int __stdcall ConnectTo(char* ps8_ServerIP, // entrada - ps8_ServerIP = "0123456789123456"
                                           int   s32_Port,     //entrada 
                                           int*  ph_Client);   //saída - int ph_Client[1]

    static EXPFUNC int __stdcall ConnectClose(int h_Client);   //entrada

    static EXPFUNC int __stdcall ServerOpen(int s32_Port);     //entrada

    static EXPFUNC int __stdcall GetAllConnections(int* ph_Client,         // saída - int ph_Client[62]
                                                   int* ps32_ClientIP,     // saída - int ps32_ClientIP[62]
                                                   int* ps32_ClientCount); // saída - int ps32_ClientCount[1]
    
    static EXPFUNC int __stdcall DisconnectClient(SOCKET h_Client);        // entrada 

    static EXPFUNC int __stdcall ServerClose();

    static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,             // entrada
                                           int*   ps32_SendBuf,         // entrada
                                           int    s32_SendBufLen);      // entrada - o tamanho de matriz SendBuf[] no elemento int

    static EXPFUNC int __stdcall SendToDouble(SOCKET  h_Client,         // entrada
                                              double* pd_SendBuf,       // entrada
                                              int     s32_SendBufLen);  // entrada - o tamanho de array SendBuf[] no elemento double

    static EXPFUNC int __stdcall SendToString(SOCKET h_Client,          // entrada
                                              char*  ps8_SendBuf,       // entrada
                                              int    s32_SendBufLen);   // o tamanho da string SendBuf no elemento char

    static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,           // entrada
                                             int*   ps32_ReadBuf,       // entrada
                                             int    s32_ReadBufLen,     // o tamanho do array ReadBuf[] no elemento int
                                             int*   ps32_ReadLen);      // saída- int ps32_ReadLen [1] - contagem real de dados lidos no elemento int

    static EXPFUNC int __stdcall ReadFromDouble(SOCKET  h_Client,       // entrada
                                                double* pd_ReadBuf,     // entrada
                                                int     s32_ReadBufLen, // o tamanho do array ReadBuf[] no elemento double
                                                int*    ps32_ReadLen);  // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento double

    static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,        // entrada
                                                char*  ps8_ReadBuf,     // entrada
                                                int    s32_ReadBufLen,  // o tamanho do array ReadBuf[] no elemento char
                                                int*   ps32_ReadLen);   // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento char
//*******************************************************************************************************************
protegido:
    static DWORD SendTo(SOCKET h_Client,   char* ps8_SendBuf, INT s32_SendBufLen);
    static DWORD ReadFrom(SOCKET h_Client, char* ps8_ReadBuf, INT s32_ReadBufLen, INT& s32_ReadLen);
};


1.2. O arquivo FastStart.zip

Este arquivo contém os códigos-fonte de todos os programas usados em exemplos de demonstração. Programas em C++ são representados como projetos do Microsoft Visual Studio 2010: Client e EchoServer. Os códigos-fonte de programas MQL4 também estão presentes neste arquivo juntamente com o arquivo ImportNetEventsProcDLL.mqh, usado para importar funções DLL aos programas MQL4. Coloque esse arquivo na pasta "MetaTrader 4\experts\include\".

Em uma discussão mais aprofundada, os códigos-fonte de todos esses programas estão listados no texto. Vamos considerar 3 exemplos que demonstram o trabalho com todas as funções DLL nas linguagens de programação MQL4 e C++ :

  • Seção 1.2.1. demonstra a troca de informações entre o Expert Advisor МetaТrader 4 -servidor e cliente C++.

  • Seção 1.2.2. demonstra a troca de informações entre Servidor C++ e Expert Advisor МetaТrader 4 -cliente.

  • Seção 1.2.3. demonstra a troca de informações entre Expert Advisors MetaTrader 4. Um desses EAs atua como um servidor que fornece a outros Expert Advisors МetaТrader 4 - indicadores (os Expert Advisors-clientes) com os valores do indicador. Em outras palavras, temos implementado a distribuição dos valores de um indicador "seguro" aos clientes.


1.2.1. Expert Advisor МetaТrader 4-servidor & C++ programa-cliente

Considere esta tarefa tradicional de troca de informações entre Expert Advisor МetaТrader 4 e o programa C++:

  • EchoServer.mq4 - Expert Advisor que funciona como um servidor echo.

  • Client.cpp - Programa C++ que atua como um cliente para este Expert Advisor-servidor.

Cliente C++ lê as mensagens inseridas pelo usuário no console e as envia ao Expert Advisor. O Expert Advisor recebe essas mensagens, as exibe na janela do terminal e as envia de volta ao destinatário. As imagens abaixo ilustram esta ideia:

Figura 1. Expert Advisor МetaТrader 4-servidor & C++ programa-cliente

Aqui está o código fonte do Expert Advisor MetaTrader 4. EchoServer.mq4 que atua como um servidor echo:

//+---------------------------------------------------------------------------+
//|                                            EchoServer.mq4                 |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+
#property copyright "Copyright © 2012, https://www.mql4.com/ ru/users/more"
#property link      "https://www.mql4.com/ ru/users/more"
#include <ImportNetEventsProcDLL.mqh>
/*int ServerOpen(int  s32_Port); // entrada
*/
/*int ServerClose();
*/
/*int GetAllConnections(int& ph_Client[],         // saída - int ph_Client[62]
                        int& ps32_ClientIP[],     // saída - int ps32_ClientIP[62]
                        int& ps32_ClientCount[]); // saída - int ps32_ClientCount[1]
*/                      
/*int SendToString(int    h_Client,               // entrada
                   string ps8_SendBuf,            // entrada
                   int    s32_SendBufLen);        // entrada - tamanho da string SendBuf no elemento char
*/ 
/*int ReadFromString(int     h_Client,            // entrada
                   string    ps8_ReadBuf,         // entrada
                   int       s32_ReadBufLen,      // entrada  - tamanho da string ReadBuf no elemento char
                   int&      ps32_ReadLen[]);     // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento char     
*/                              
// Variáveis Globals
int s32_Error;
int i;

int s32_Port = 2000;
bool b_ServerOpened = false;

// para GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount)
int ph_Client       [62];
int ps32_ClientIP   [62];
int ps32_ClientCount[1 ];


// para int ReadFromString(h_Client, ps8_Buf, s32_BufLen, ps32_ReadLen)
// para int SendToString  (h_Client, ps8_Buf, s32_BufLen)
string ps8_Buf = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int    s32_BufLen;
int    ps32_ReadLen[1];
//+------------------------------------------------------------------+
//| Função de inicialização do expert                                |
//+------------------------------------------------------------------+
int init()
{
   //----
   s32_BufLen = StringLen(ps8_Buf);
   
   if (!b_ServerOpened)
   {
      s32_Error = ServerOpen(s32_Port);
      Print("ServerOpen() return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
      {
         b_ServerOpened = true;
         Print("Server is Opened and Waiting fo Client connection requests...");
      }
   }
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Função de desinicialização do expert                             |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   if (b_ServerOpened)
   {
      s32_Error = ServerClose();
      Print("ServerClose() return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
         b_ServerOpened = false;
   }
//----
   return(0);
  }  

int start()
{
//----
   if (!b_ServerOpened)
      return(0);
      
   s32_Error = GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount);
   
   if (s32_Error != 0)
   {
      Print("GetAllConnections(...) return is: ",GetErrMsg(s32_Error));
      return(1);
   }
   

   Print("ClientCount = ", ps32_ClientCount[0]);

   
   for (i = 0; i<ps32_ClientCount[0]; i++)
   {
      Print("h_Client = ", ph_Client[i], "      Client IP =  ", FormatIP(ps32_ClientIP[i])); 
       
      s32_Error = ReadFromString(ph_Client[i], ps8_Buf, s32_BufLen, ps32_ReadLen);
      
      Print("ReadFromString(",ph_Client[i],",...) return is: ", GetErrMsg(s32_Error));
      
      if (s32_Error == 0)
      {
         Print("ReadFromString(",ph_Client[i],",...) ps32_ReadLen = ",ps32_ReadLen[0]);
         
         if (ps32_ReadLen[0] > 0)
         {
            Print("ReadFromString(",ph_Client[i],",...) Read data is: ", StringSubstr(ps8_Buf,0,ps32_ReadLen[0]));
            
            s32_Error = SendToString(ph_Client[i], ps8_Buf, StringLen(StringSubstr(ps8_Buf,0,ps32_ReadLen[0])));
            
            Print("SendToString(", ph_Client[i],",...) return is: ",GetErrMsg(s32_Error));
         }
      }
   }
//----
   return(0);
  }
//+------------------------------------------------------------------+

E aqui está o código fonte do Client.cpp - programa C++ que age como um cliente ao Expert Advisor-servidor:

//+---------------------------------------------------------------------------+
//|                                            Client.cpp                     |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+

// Client.cpp

#include <winsock2.h>
#pragma comment(lib, "ws2_32")
#include <iostream>
#pragma comment(lib, "NetEventsProcDLL") // NetEventsProcDLL.lib placed in ...\FastStart\Client\
#include  "cNetEventsProcDLL.h"

// LocalIp = 0x6401a8c0 -> 192.168.1.100
// Retorna uma lista de todos os locais do IP deste computador (múltiplo de IP se várias placas de rede)
DWORD GetLocalIPs(char s8_IpList[][20], int &s32_IpCount);

/* Este é um Cliente simples.
   Ele se conecta ao Servidor Echo, envie sua entrada para o Servidor Echo e lê Echo a partir do Servidor.

   ATENÇÃO !!! Se o seu desejo é se conectar ao servidor local, não use "127.0.0.1" !!! ATENÇÃO !!! 
   Você pode obter todos os seus locais de IP por meio da função GetLocalIPs(...), aqui temos um exemplo de chamada:
   // Lista de IP Local
        char s8_IpList[10][20];

        int s32_IpCount;
        DWORD u32_Err = GetLocalIPs(s8_IpList, s32_IpCount);

        if (u32_Err)
        {
                printf("Get local IP's error !, no local IP means no network available...");
                return 1;
        }

        printf("\nLocal IP's list:\n");

        for (int i = 0; i<s32_IpCount; i++)
                printf("\n%s\n",s8_IpList[i]);
        //
*/

// Este é o servidor que deseja se conectar...
#define SERVER_IP   "192.168.1.5"  //este é o meu local de IP obtido por meio da função GetLocalIPs(...),
                                   // não use chamada de auto-retorno do endereço  "127.0.0.1"!!! 
#define PORT         2000

int main()
{
        // Lista de IP Local
        char s8_IpList[10][20];

        int s32_IpCount;
        DWORD u32_Err = GetLocalIPs(s8_IpList, s32_IpCount);

        if (u32_Err)
        {
                printf("Get local IP's error !, no local IP means no network available...");
                return 1;
        }

        printf("\nLocal IP's list:\n");

        for (int i = 0; i<s32_IpCount; i++)
                printf("\n%s\n",s8_IpList[i]);
        //

        char s8_ServerIP[] = SERVER_IP;
        int  s32_Port      = PORT;
        int  ph_Client[1];

        int  h_Client;

        DWORD u32_Error = cNetEventsProcDLL::ConnectTo(SERVER_IP, PORT, ph_Client);

        if (u32_Error)
        {
                printf("\nConnectTo(...) failed with error: %d\n", u32_Error);
                return 1;
        }
        else
                printf("\nConnectTo(...) OK, ph_Client[0] = : %d\n", ph_Client[0]);

        h_Client = ph_Client[0];

        // A conexão foi estabelecida com sucesso! Vamos ter alguns dados SendTo (...) nesta ligação ...
        char  ps8_SendData[200];
        int   s32_SendDataLen;;

        char   ps8_ReadBuf[1025];
        DWORD  s32_ReadBufLen = 1025;
        int    ps32_ReadLen[1];

        while(true)
        {
                std::cout << "\nDigite algo para enviar ao servidor ou Sair 'q':\n" << std::endl;

                std::cin.getline(ps8_SendData, 200);

                s32_SendDataLen = strlen(ps8_SendData);

                OemToCharBuff(ps8_SendData, ps8_SendData, s32_SendDataLen);

                if (ps8_SendData[0] == 'q')
                {
                        u32_Error = cNetEventsProcDLL::ConnectClose(h_Client);
                        if (u32_Error)
                        {
                                printf("\nConnectClose(...) failed with error: %d\n", u32_Error);
                                break;
                        }
                        else
                        {
                                printf("\nConnectClose(...) OK.\n");
                                break;
                        }
                }

                u32_Error = cNetEventsProcDLL::SendToString(h_Client, ps8_SendData, s32_SendDataLen);   

                switch (u32_Error)
                {
                case 0:
                        printf("\nSendTo(...) OK");
                        printf("\nSendTo(%d...) sent %d bytes\n", h_Client, s32_SendDataLen);
                        CharToOemBuff(ps8_SendData, ps8_SendData, s32_SendDataLen);
                        printf("\nSendTo(%d...) Sent Data: %s\n",h_Client, ps8_SendData);
                        printf("Waiting now for Echo....");
                        break;
                case ERROR_INVALID_PARAMETER:
                        printf("\nSendTo(%d...) return is: ERROR_INVALID_PARAMETER(%d)\n",h_Client, u32_Error);
                        printf("\nERROR_INVALID_PARAMETER -> One of this parms or more: h_Client, ps8_SendData, u32_SendDataLen is invalid...\n");
                        break;
                case WSAEWOULDBLOCK:
                        printf("\nSendTo(%d...) return is: WSAEWOULDBLOCK(%d)\n",h_Client, u32_Error);
                        printf("\nWSAEWOULDBLOCK -> The data will be send after the next FD_WRITE event, do nouthing\n");
                        break;

                case WSA_IO_PENDING:
                        printf("\nSendTo(%d...) return is: WSA_IO_PENDING(%d)\n",h_Client, u32_Error);
                        printf("\nWSA_IO_PENDING -> Error: A previous Send operation is still pending. This data will not be sent, try latter\n");
                        break;

                default:
                        printf("\nSendTo(%d...)failed with severe error: %d\n",h_Client, u32_Error);
                        // Erro grave -> abortar evento loop
                        printf("\nConnection was closed !\n");
                        break;
                };
                
                if (u32_Error == 0 || u32_Error == WSAEWOULDBLOCK)
                {
                        int ReadLen = 0;

                        while(!ReadLen)
                        {
                                u32_Error = cNetEventsProcDLL::ReadFromString(h_Client, ps8_ReadBuf, s32_ReadBufLen, ps32_ReadLen);

                                if (u32_Error)
                                {
                                        printf("\nReadFromString(%d...) failed with error: %d\n", h_Client, u32_Error);
                                        break;
                                }

                                ReadLen = ps32_ReadLen[0];
                        }
                        if (u32_Error)
                        {
                                printf("\nReadFromString(%d...) failed with error: %d\n", h_Client, u32_Error);
                        }
                        else
                        {
                                printf("\nReadFromString(%d...) OK, read %d  bytes\n", h_Client, ReadLen);
                        }
                                
                        if (ReadLen > 0)
                        {
                                CharToOemBuff(ps8_ReadBuf, ps8_ReadBuf, s32_SendDataLen);
                                ps8_ReadBuf[ReadLen] = 0;
                                printf("\nReadFromString(%d...) Read Data: %s\n", h_Client, ps8_ReadBuf);
                        }

                }

        }

        return 0;
}

// LocalIp = 0x6401a8c0 -> 192.168.1.100
// Retorna uma lista de todos os locais do IP deste computador (múltiplo de IP se várias placas de rede)
DWORD GetLocalIPs(char s8_IpList[][20], int &s32_IpCount)
{
        // Winsock versão 2.0 está disponível em TODOS os sistemas operacionais Windows 
        // exceto o Windows 95 que vem como o Winsock 1.1
        WSADATA k_Data;
        DWORD u32_Error = WSAStartup(MAKEWORD(2,0), &k_Data);
        if (u32_Error)
                return u32_Error;

        int ps32_IpList[20];

        char s8_Host[500];
        if (gethostname(s8_Host, sizeof(s8_Host)) == SOCKET_ERROR)
                return WSAGetLastError();
        
        struct hostent* pk_Host = gethostbyname(s8_Host);
        if (!pk_Host)
                return WSAGetLastError();

        s32_IpCount = 0;

        for (DWORD i=0; TRUE; i++)
        {
                if (!pk_Host->h_addr_list[i])
                        break; // A lista IP está terminada e zerada

                ps32_IpList[i] = *((DWORD*)pk_Host->h_addr_list[i]);

                s32_IpCount++;
        }

        if (!s32_IpCount)
                return WSAENETDOWN; // Significa que não há nenhuma rede IP local disponível

        for (int i = 0; i<s32_IpCount; i++)
        {
                BYTE* pu8_Addr = (BYTE*)&ps32_IpList[i];
        
                sprintf(s8_IpList[i],"%d.%d.%d.%d",pu8_Addr[0], pu8_Addr[1], pu8_Addr[2], pu8_Addr[3]);
        }
        return 0;
}

Para executar este exemplo de demonstração você precisa:

  1. Coloque o arquivo EchoServer.mq4 na pasta de dados terminal e fazer a compilação: "МetaТrader 4\experts\".

  2. Abra o projeto Client no Microsoft Visual Studio 2010 Ultimate e faça a construção do mesmo usando a configuração Release. Se você quiser construir o projeto em outra IDE, não se esqueça de especificar o módulo NetEventsProcDLL.lib como uma entrada adicional ao editor (compilação com: /EHsc/link NetEventsProcDLL.lib).

  3. Execute o módulo Client.exe. Você vai ter um erro com o código 10057 e na lista dos IPs locais de seu computador. Corrija o seguinte seqüência de caracteres no código-fonte client.cpp

    #define SERVER_IP   "192.168.1.5"

    substituindo "192.168.1.5" com a primeira (ou única) IP local e compilar o projeto novamente.

  4. Execute o Expert Advisor EchoServer em qualquer gráfico no terminal MetaTrader 4. Se tudo for feito corretamente, a janela de terminal exibirá as seguintes mensagens:"ServerOpen() return is: OK", "Server is Opened and Waiting for Client connection requests..." ( "Retorno do ServerOpen() está: OK", "Servidor esta Aberto e Aguardando solicitação de conexão pelo Cliente..."). Em seguida, em cada tick o Expert Advisor verifica as solicitações da conexão, conecta, lê as mensagens recebidas a partir de cada conexão e envia uma cópia da mensagem de volta ao destinatário. O Expert Advisor exibe todas as informações atuais na janela do terminal.

  5. Agora você pode executar o programa Client.exe construído na etapa 3.

  6. Você pode executar várias cópias deste programa e ver como o Expert Advisor troca informações com todas as cópias.

  7. Se você definir o IP global do computador onde o Expert Advisor-servidor está sendo executado, em vez de seu IP local no passo 3, você pode executar o Client.exe e comunicar com o Expert Advisor em qualquer outro computador, estando ligado à Internet. Não se esqueça de desativar todas as proteções de firewall atuais.


1.2.2. Programa C++-servidor & Expert Advisor MetaTrader 4-cliente

Considere esta tarefa tradicional de troca de informações entre Expert Advisor МetaТrader 4 e o programa C++:

  • EchoServer.cpp - Programa C++ atua como um servidor echo.

  • Client.mq4 - Expert Advisor atua como um cliente para este servidor C++.

O Expert Advisor-cliente lê cotações do símbolo sobre o qual ele está sendo executado e envia estas cotações ao servidor do C++. Servidor C++ retorna de volta a cotação ao destinatário, este recebe e exibe a cotação na janela do terminal. As imagens abaixo ilustram esta ideia:

Figura 2. Programa C++-servidor & Expert Advisor MetaTrader 4-cliente

Aqui está o código fonte do Expert Advisor МetaТrader 4, Client.mq4, que atua como um cliente:

//+---------------------------------------------------------------------------+
//|                                            Client.mq4                     |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+
#property copyright "Copyright © 2012, https://www.mql4.com/ ru/users/more"
#property link      "https://www.mql4.com/ ru/users/more"
#include <ImportNetEventsProcDLL.mqh>
/*int ConnectTo(string  ps8_ServerIP,         // entrada - string ps8_ServerIP = "0123456789123456"
                int     s32_Port,             // entrada 
                int&    ph_Client[]);         // saída - int ph_Client[1]
*/
/*int SendToDouble(int     h_Client,          // entrada
                   double& pd_SendBuf[],      // entrada
                   int     s32_SendBufLen);   // entrada - o tamanho da array SendBuf[] no elemento double 
*/
/*int ReadFromDouble(int     h_Client,        // entrada
                     double& pd_ReadBuf[],    // entrada
                     int     s32_ReadBufLen,  // entrada  - o tamanho de array ReadBuf[] no elemento double
                     int&    ps32_ReadLen[]); // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento double
*/                      
/*int ConnectClose(int h_Client);             // entrada
*/ 
       
// Variáveis Globals
int s32_Error;
int i;
// para int ConnectTo(ps8_ServerIP, s32_Port, ph_Client); // saída - int h_Client[1]
string ps8_ServerIP = "192.168.1.5";                     // meu IP local 
int    s32_Port = 2000;
int    ph_Client[1];

bool b_ConnectTo = false;

// para int SendToDouble(ph_Client[0], pd_Buf, s32_BufLen);  
// para int ReadFromDouble(ph_Client[0], pd_Buf, s32_BufLen, ps32_ReadLen);
double pd_Buf[1];
int    s32_BufLen = 1;
int    ps32_ReadLen[1];
//+------------------------------------------------------------------+
//| Função de inicialização do expert                                |
//+------------------------------------------------------------------+
int init()
{
//----
   if (!b_ConnectTo)
   {
      s32_Error = ConnectTo(ps8_ServerIP, s32_Port, ph_Client);
      Print("ConnectTo(...) return is: ",GetErrMsg(s32_Error));
      Print("ConnectTo(...) handle is: ",ph_Client[0]);
      
      if (s32_Error == OK)
      {
         b_ConnectTo = true;
         Print("Client now is ConnectTo the Server: ",ps8_ServerIP);
      }
   }
//----
   return(0);
}
//+------------------------------------------------------------------+
//| Função de desinicialização do expert                             |
//+------------------------------------------------------------------+
int deinit()
{
//----
   if (b_ConnectTo)
   {
      s32_Error = ConnectClose(ph_Client[0]);
      Print("ConnectClose(...) return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
         b_ConnectTo = false;
   }
//----
   return(0);
}  

int start()
{
//----
   if (!b_ConnectTo)
      return(0);
   
   RefreshRates();
   
   double pd_Value[1];
   
   pd_Value[0] = NormalizeDouble(Bid,Digits);
      
   s32_Error = SendToDouble(ph_Client[0], pd_Value, s32_BufLen);
   
   
   if (s32_Error != 0)
   {
      Print("SendToDouble(",ph_Client[0],"...) return is: ",GetErrMsg(s32_Error));
      return(1);
   }
   else
      Print("SendToDouble(",ph_Client[0],"...) return is: OK");
      
   s32_Error = ReadFromDouble(ph_Client[0], pd_Buf, s32_BufLen, ps32_ReadLen);      
   
   if (s32_Error != 0)
   {
      Print("ReadFromDouble(",ph_Client[0],"...) return is: ", GetErrMsg(s32_Error));
      return(1);
   }
   else
      Print("ReadFromDouble(",ph_Client[0],"...) return is: OK"); 
   
   pd_Buf[0] = NormalizeDouble(pd_Buf[0],Digits);
   
   if (ps32_ReadLen[0] > 0)
      Print("Read doble value is: ", pd_Buf[0]);
   
//----
   return(0);
  }
//+------------------------------------------------------------------+

E aqui está o código fonte do EchoServer.cpp, Programa C++ que atua como um servidor echo para o Expert Advisor-cliente:

//+---------------------------------------------------------------------------+
//|                                            EchoServer.cpp                 |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+

// EchoServer.cpp

#include <winsock2.h>
#pragma comment(lib, "NetEventsProcDLL") // NetEventsProcDLL.lib placed in ...\FastStart\EchoServer\ 
#include <iostream>
#include <conio.h>

#include  "cNetEventsProcDLL.h"

BOOL FormatIP(DWORD u32_IP, char* s8_IP);

int main()
{
        int s32_Port = 2000;
        
        // Tente criar uma escuta do servidor na porta 2000
        // Voce pode alterar a porta
        DWORD u32_Error = cNetEventsProcDLL::ServerOpen(s32_Port);

        if (u32_Error)
        {
                printf("\nServerOpen() failed with error: %d\n", u32_Error);
                return 1;
        }
        else
                printf("\nServerOpen() fine, we now are waiting for connections...\n");
        
        DWORD u32_Count = 0;
        DWORD u32_CountOld = 0;
        
        double pd_Buf[1025];
        DWORD  u32_BufLen = 1025;
        int    ps32_ReadLen[1];

        pd_Buf[0] = 0;

        int ph_Client[62];
        int ps32_ClientIP[62];
        int ps32_ClientCount[1];

        while(!kbhit())
        {
                u32_Error = cNetEventsProcDLL::GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount);
                
                if (u32_Error)
                {
                        printf("\nGetAllConnections(...) failed with error: %d\n", u32_Error);
                        break;
                }
                else
                        u32_Count = ps32_ClientCount[0];

                if (u32_Count != u32_CountOld)
                {
                        u32_CountOld = u32_Count;

                        printf("\nNumber of connections now = %d\n", u32_Count);
                        printf("#     h_Connect   (peer IP)\n");
                        
                        for (DWORD i = 0; i<u32_Count; i++)
                        {
                                char  s8_IP[20];
                                sprintf(s8_IP, "%s","123456789012345");

                                FormatIP(ps32_ClientIP[i], s8_IP);

                                printf("%d      %d       (%s)\n", i, ph_Client[i], s8_IP);
                        }
                }

                for (DWORD i = 0; i<u32_Count; i++)
                {
                        u32_Error = cNetEventsProcDLL::ReadFromDouble(ph_Client[i], pd_Buf, u32_BufLen, ps32_ReadLen);

                        if (u32_Error)
                        {
                                printf("ReadFromDouble(%d...) failed with error: %d\n", ph_Client[i], u32_Error);
                        }

                        if (ps32_ReadLen[0])
                        {
                                printf("ReadFromDouble(%d...) read %d double values\n", ph_Client[i], ps32_ReadLen[0]);
                                printf("\nReadFromDouble(%d...) Read Data: %9.5f\n", ph_Client[i], pd_Buf[0]);
                        }
                        
                        if (ps32_ReadLen[0])
                        {
                                u32_Error = cNetEventsProcDLL::SendToDouble(ph_Client[i], pd_Buf, ps32_ReadLen[0]);

                                if (u32_Error)
                                {
                                        printf("SendToDouble(%d...) failed with error: %d\n", ph_Client[i], u32_Error);
                                }
                                else
                                {
                                        printf("SendToDouble(%d...) sent %d double values\n", ph_Client[i], ps32_ReadLen[0]);
                                        printf("SendToDouble(%d...) sent Data: %9.5f\n",ph_Client[i], pd_Buf[0]);
                                }
                        }

                }
                
        }

        u32_Error = cNetEventsProcDLL::ServerClose();

        if (u32_Error)
        {
                printf("\nServerClose() failed with error: %d\n", u32_Error);
                return 1;
        }
        else
                printf("\nServerClose() fine...\n");

        Sleep(10000);
        return 0;
}

BOOL FormatIP(DWORD u32_IP, char* s8_IP)
{
        DWORD u32_Len = strlen(s8_IP);

        if ( u32_Len < 15)
                return FALSE;
        
        BYTE* pu8_Addr = (BYTE*)&u32_IP;
        sprintf(s8_IP,"%d.%d.%d.%d",pu8_Addr[0], pu8_Addr[1], pu8_Addr[2], pu8_Addr[3]);

        return TRUE;
}

Para executar este exemplo de demonstração você precisa:

  1. Coloque o arquivo Client.mq4 na pasta de dados do terminal: "MetaTrader 4\experts\", atribua IP local (obtido no exemplo anterior 1.2.1.) na string

    string ps8_ServerIP = "192.168.1.5";

    e execute a compilação. Se o servidor C++ será executado em outro computador, então cole aqui o IP global deste computador. Não esqueça de desativar todas as proteções existentes no seu firewall.

  2. Abra o projeto Client no Microsoft Visual Studio 2010 Ultimate e faça a construção do mesmo usando a configuração Release. Se você quiser construir o projeto em outra IDE, não se esqueça de especificar o módulo NetEventsProcDLL.lib como uma entrada adicional ao editor (compilação com: /EHsc/link NetEventsProcDLL.lib).

  3. Execute o arquivo EchoServer.exe do Servidor C++. Se tudo for feito corretamente, o console irá exibir a seguinte mensagem: "ServerOpen() fine, we now are waiting for connections....." (ServerOpen() pronto, agora esperando por conexões.....). Ao pressionar qualquer tecla, fechará o servidor e encerrar o programa.

  4. Execute o cliente Client.mq4 em qualquer gráfico do terminal МetaТrader 4.

  5. Você pode executar o cliente simultaneamente em vários gráficos, ou vários terminais ou computadores diferentes.

  6. Observe como trabalha o servidor C++ e os clientes МetaТrader 4. Pressione qualquer tecla no console do servidor C++, então o programa C++ fechará o servidor e encerrará.


1.2.3. Expert Advisor МetaТrader 4-indicador (Expert Advisor-servidor) & Cliente МetaТrader 4-indicador

O Expert Advisor-indicator (Expert Advisor-servidor) fornece os Clientes-indicadores com os valores do indicador. Neste caso, estes são os valores do indicador padrão iEnvelops (...). Este exemplo pode ter um valor prático para a distribuição dos valores do indicador "protegido" a todos os assinantes-clientes.

As imagens abaixo ilustram esta ideia:

Figura 3. Expert Advisor МetaТrader 4-indicador (Expert Advisor-servidor) & Cliente МetaТrader 4-indicador

Aqui está o código fonte do Expert Advisor МetaТrader 4, ServerSendInd.mq4, atuando como o fornecedor dos valores do indicador iEnvelops(...):

//+---------------------------------------------------------------------------+
//|                                            ServerSendInd.mq4              |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+
#property copyright "Copyright © 2012, https://www.mql4.com/ ru/users/more"
#property link      "https://www.mql4.com/ ru/users/more"
#include <ImportNetEventsProcDLL.mqh>
/*int ServerOpen(int  s32_Port);                  // entrada
*/
/*int ServerClose();
*/
/*int GetAllConnections(int& ph_Client[],         // saída - int ph_Client[62]
                        int& ps32_ClientIP[],     // saída - int ps32_ClientIP[62]
                        int& ps32_ClientCount[]); // saída - int ps32_ClientCount[1]
*/                      
/*int ReadFromString(int     h_Client,            // entrada
                   string    ps8_ReadBuf,         // entrada
                   int       s32_ReadBufLen,      // entrada  - tamanho da string ReadBuf no elemento char
                   int&      ps32_ReadLen[]);     // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento char     
*/ 
/*int SendToDouble(int     h_Client,              // entrada
                   double& pd_SendBuf[],          // entrada
                   int     s32_SendBufLen);       // entrada -  Tamanho do Array SendBuf[] no elemento double   
*/                                            
// Variáveis Globals
int s32_Error;
int i;

int s32_Port = 2000;
bool b_ServerOpened = false;

// para GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount)
int ph_Client       [62];
int ps32_ClientIP   [62];
int ps32_ClientCount[1 ];

// para int ReadFromString(h_Client, ps8_ReadBuf, s32_ReadBufLen, ps32_ReadLen)
string ps8_ReadBuf = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int    s32_ReadBufLen;
int    ps32_ReadLen[1];

// para int SendToDouble(ph_Client[0], pd_SendBuf, s32_SendBufLen);  
#define  BARS_COUNT  200
double pd_SendBuf      [BARS_COUNT];  //BARS_COUNT/2 Bars for each of 2 line 
int    s32_SendBufLen = BARS_COUNT;

//+------------------------------------------------------------------+
//| Função de inicialização do expert                                |
//+------------------------------------------------------------------+
int init()
{
   //----
   s32_ReadBufLen = StringLen(ps8_ReadBuf);
   
   if (!b_ServerOpened)
   {
      s32_Error = ServerOpen(s32_Port);
      Print("ServerOpen() return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
      {
         b_ServerOpened = true;
         Print("Server is Opened and Waiting for Clients connection requests...");
      }
   }
//----
   return(0);
}
//+------------------------------------------------------------------+
//| Função de desinicialização do expert                             |
//+------------------------------------------------------------------+
int deinit()
{
//----
   if (b_ServerOpened)
   {
      s32_Error = ServerClose();
      Print("ServerClose() return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
         b_ServerOpened = false;
   }
//----
   return(0);
}  

int start()
{
//----
   if (!b_ServerOpened)
      return(0);
      
   s32_Error = GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount);
   
   if (s32_Error != 0)
   {
      Print("GetAllConnections(...) failed with error: ",GetErrMsg(s32_Error));
      return(1);
   }
   
   Print("ClientCount = ", ps32_ClientCount[0]);
   
   for (i = 0; i<ps32_ClientCount[0]; i++)
   {
      Print("h_Client = ", ph_Client[i], "      Client IP =  ", FormatIP(ps32_ClientIP[i]));
      
      s32_Error = ReadFromString(ph_Client[i], ps8_ReadBuf, s32_ReadBufLen, ps32_ReadLen); 
      
      if (s32_Error != 0)
      {
         Print("ReadFromString(",ph_Client[i],") failed with error: ", GetErrMsg(s32_Error));
         continue;
      }

      if (ps32_ReadLen[0] > 0)
      {
         // ps8_ReadBuf = "EURUSDMinuts"   i.e. "Symbol+Timeframe"
         string Sym       = StringSubstr(ps8_ReadBuf,0,6);
         int    TimeFrame = StrToInteger(StringSubstr(ps8_ReadBuf,6,ps32_ReadLen[0]-6));
      

         int k;                
         for (k = 0; k<BARS_COUNT/2; k++)
         {
            while(true)
            {
               double UpperLine_k = iEnvelopes(Sym, TimeFrame, 14, MODE_SMA, 0, PRICE_CLOSE, 0.1, MODE_UPPER, k);
               if (GetLastError() != 0)
                  continue;
               else
                  break;   
            }    
            while(true)
            {           
               double LowerLine_k = iEnvelopes(Sym, TimeFrame, 14, MODE_SMA, 0, PRICE_CLOSE, 0.1, MODE_LOWER, k);
               if (GetLastError() != 0)
                  continue;
               else
                  break;
            }
             
            pd_SendBuf[k]              = UpperLine_k;
            pd_SendBuf[k+BARS_COUNT/2] = LowerLine_k;
         }
         
         s32_Error = SendToDouble(ph_Client[i], pd_SendBuf, s32_SendBufLen); 
         if (s32_Error != 0)
         {
            Print("SendToDouble(",ph_Client[i],") failed with error: ", GetErrMsg(s32_Error));

            continue;
         }
      }  
   }
//----
   return(0);
}
//+------------------------------------------------------------------+

E aqui está o código fonte Cliente-indicador, ClientIndicator.mq4, que recebe os valores do indicador iEnvelops(...) do Expert Advisor ServerSendInd.mq4:

//+---------------------------------------------------------------------------+
//|                                            ClientIndicator.mq4            |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+
#property copyright "Copyright © 2012, https://www.mql4.com/ en/users/more"
#property link      "https://www.mql4.com/ ru/users/more"
#include <ImportNetEventsProcDLL.mqh>

/*int ConnectTo(string  ps8_ServerIP,         // entrada - string ps8_ServerIP = "0123456789123456"
                int     s32_Port,             // entrada 
                int&    ph_Client[]);         // saída - int ph_Client[1]
*/
/*                      
/*int ConnectClose(int h_Client);             // entrada
*/
/*int SendToString(int    h_Client,           // entrada
                   string ps8_SendBuf,        // entrada
                   int    s32_SendBufLen);    // entrada - tamanho da string SendBuf no elemento char
*/
/*int ReadFromDouble(int     h_Client,        // entrada
                     double& pd_ReadBuf[],    // entrada
                     int     s32_ReadBufLen,  // entrada  - o tamanho de array ReadBuf[] no elemento double
                     int&    ps32_ReadLen[]); // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento double
*/
// Variáveis Globals
int s32_Error;
int i;
// para int ConnectTo(ps8_ServerIP, s32_Port, ph_Client);  // out - int h_Client[1]
string ps8_ServerIP = "192.168.1.5";                      // mine local IP
int    s32_Port = 2000;
int    ph_Client[1]; 

bool b_ConnectTo = false;

// para int SendToString  (h_Client, ps8_SendBuf, s32_SendBufLen)
string ps8_SendBuf = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int    s32_SendBufLen;  
 
// para int ReadFromDouble(ph_Client[0], pd_ReadBuf, s32_ReadBufLen, ps32_ReadLen);
#define BARS_COUNT  200
double  pd_ReadBuf      [BARS_COUNT];
int     s32_ReadBufLen = BARS_COUNT;
int     ps32_ReadLen[1]; 
               
string Indicator_Name = "Envelopes: ";
//----
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Blue
#property indicator_color2 Red

double UpperLine[];
double LowerLine[];

//+------------------------------------------------------------------+
//| Função inicializar do indicador personalizado                    |
//+------------------------------------------------------------------+
int init()
{
//---- indicadores
   SetIndexStyle (0,DRAW_LINE);
   SetIndexBuffer(0,UpperLine);
   
   SetIndexStyle (1,DRAW_LINE);
   SetIndexBuffer(1,LowerLine);
   
   s32_SendBufLen = StringLen(ps8_SendBuf);
   
   if (!b_ConnectTo)
   {
      s32_Error = ConnectTo(ps8_ServerIP, s32_Port, ph_Client);
      Print("ConnectTo(...) return is: ",GetErrMsg(s32_Error));
      Print("ConnectTo(...) handle is: ",ph_Client[0]);
      
      if (s32_Error == OK)
      {
         b_ConnectTo = true;
         Print("Client now is ConnectTo the Server: ",ps8_ServerIP);
      }
   }
//----   
   return(0);
}

//+------------------------------------------------------------------+
//| Função desinicializar do indicador personalizado                 |
//+------------------------------------------------------------------+
int deinit()
{
    //----
   if (b_ConnectTo)
   {
      s32_Error = ConnectClose(ph_Client[0]);
      Print("ConnectClose(...) return is: ",GetErrMsg(s32_Error));
      
      if (s32_Error == OK)
         b_ConnectTo = false;
   }
//----
   return(0);
}
//+------------------------------------------------------------------+
//| Função de iteração do indicador personalizado                    |
//+------------------------------------------------------------------+
int start()                          
{
//----
   if (!b_ConnectTo)
      return(0);
      
    string Sym       = Symbol();
    int    TimeFrame = Period();
    ps8_SendBuf = Symbol() + DoubleToStr(Period(),0);
    
    s32_Error = SendToString(ph_Client[0], ps8_SendBuf, StringLen(ps8_SendBuf));
    
    if (s32_Error != 0) 
    {       
      Print("SendToString(", ph_Client[0],",...) failed with error: ",GetErrMsg(s32_Error));
      return (1);
    }
    
    s32_Error = ReadFromDouble(ph_Client[0], pd_ReadBuf, s32_ReadBufLen, ps32_ReadLen);      
   
   if (s32_Error != 0)
   {
      Print("ReadFromDouble(",ph_Client[0],"...) return is: ", GetErrMsg(s32_Error));
      return(1);
   }
   
   if (ps32_ReadLen[0] == 0)
      return (0);
 
//--------------------------------------------------------------------
    int Counted_bars = IndicatorCounted();       // Número de barras calculados 
    i = Bars - Counted_bars - 1;                 // Índice da primeira não calculada
    if (i > BARS_COUNT/2-1)  i = BARS_COUNT/2-1; // Calcula contagem especificada, se houverem muitas barras
//-----------------------------------------------------------------------  
    for (i = BARS_COUNT/2-1; i >= 0; i--)
    {
         UpperLine  [i] = pd_ReadBuf[i];
         LowerLine  [i] = pd_ReadBuf[i+BARS_COUNT/2];
    }  
    
    return;                          
} // fim do int start()
//--------------------------------------------------------------------

Para executar este exemplo de demonstração você precisa:

  1. Coloque o arquivo ServerSendInd.mq4 na pasta de dados terminal e faça a compilação: "МetaТrader 4\experts\".

  2. Coloque o arquivo ClientIndicator.mq4 na pasta de dados terminal "МetaТrader 4\experts\indicators\" e atribua o IP local (obtido no exemplo 1.2.1.) para a string:

    string ps8_ServerIP = "192.168.1.5";

    Se o ServerSendInd for executado em outro computador, então cole aqui o IP global deste computador. Não esqueça de desativar todas as proteções existentes no seu firewall. Faça a Compilação.

  3. Execute o ServerSendInd. Se tudo for feito corretamente, o console irá exibir a seguinte mensagem: "ServerOpen() fine, we now are waiting for connections....." (ServerOpen() pronto, agora esperando por conexões.....).

  4. Em qualquer gráfico do terminal МetaТrader execute o indicador ClientIndicator.mq4. Duas linhas de indicador aparecerão no gráfico. Se você executar o indicador Envelopes padrão no mesmo gráfico, certifique-se de que ambas as linhas do nosso indicador coincidem com as linhas de indicador padrão.

  5. Você pode executar o indicador ClientIndicator simultaneamente em vários gráficos, ou vários terminais ou computadores diferentes.

  6. Observe como trabalha o servidor ServerSendInd e ClientIndicator. Tente mudar o timeframe de gráfico com o indicador ClientIndicator. O servidor ServerSendInd será criado imediatamente para enviar valores do indicador para este timeframe.


2. Especificação da Interface DLL

Nesta seção iremos descrever em detalhes todas as funções DLL e parâmetros que são necessários para chamá-las. Todas as funções retornam zero em caso de execução bem-sucedida. Caso contrário, as funções retornam códigos de erro API Winsock2. Todas as funções DLL exportadas são dadas na declaração da classe C++, cNetEventsProcDLL.h, portanto iremos fornecer o código-fonte deste arquivo:

//+---------------------------------------------------------------------------+
//|                                            cNetEventsProcDLL.h            |
//|                      Copyright © 2012, https://www.mql4.com/ en/users/more  |
//|                                       tradertobe@gmail.com                |
//+---------------------------------------------------------------------------+
//--- cNetEventsProcDLL.h
#pragma once
#define EXPFUNC __declspec(dllexport)
//---   
class cNetEventsProcDLL
  {
public:
   static BOOL MessageDLL_PROCESS_ATTACH(void);
   static BOOL MessageDLL_PROCESS_DETACH(void);
//---
   static EXPFUNC int __stdcall ConnectTo(char *ps8_ServerIP,             // entrada - ps8_ServerIP = "0123456789123456"
                                          int   s32_Port,                 // entrada 
                                          int*  ph_Client);               // saída - int ph_Client[1]
//---
   static EXPFUNC int __stdcall ConnectClose(int h_Client);               // entrada
//---
   static EXPFUNC int __stdcall ServerOpen(int s32_Port);                 // entrada
//---
   static EXPFUNC int __stdcall GetAllConnections(int* ph_Client,         // saída - int ph_Client[62]
                                                  int* ps32_ClientIP,     // saída - int ps32_ClientIP[62]
                                                  int* ps32_ClientCount); // saída - int ps32_ClientCount[1]
//---
   static EXPFUNC int __stdcall DisconnectClient(SOCKET h_Client);        // entrada 
//---
   static EXPFUNC int __stdcall ServerClose();
//---
   static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,             // entrada
                                          int   *ps32_SendBuf,         // entrada
                                          int    s32_SendBufLen);      // entrada -  Tamanho do Array SendBuf[] no elemento int 
//---
   static EXPFUNC int __stdcall SendToDouble(SOCKET  h_Client,         // entrada
                                             double* pd_SendBuf,       // entrada
                                             int     s32_SendBufLen);  // entrada -  Tamanho do Array SendBuf[] no elemento double 
//---
   static EXPFUNC int __stdcall SendToString(SOCKET h_Client,          // entrada
                                             char*  ps8_SendBuf,       // entrada
                                             INT   s32_SendBufLen);    // o tamanho da string SendBuf no elemento char
//---
   static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,           // entrada
                                            int   *ps32_ReadBuf,       // entrada
                                            int    s32_ReadBufLen,     // o tamanho do array ReadBuf[] no elemento int
                                            int   *ps32_ReadLen);      // saída - int ps32_ReadLen[1] - conta real da leitura de dados no elemento int
//---
   static EXPFUNC int __stdcall ReadFromDouble(SOCKET  h_Client,       // entrada
                                               double *pd_ReadBuf,     // entrada
                                               int     s32_ReadBufLen, // o tamanho do array ReadBuf[] no elemento double
                                               int    *ps32_ReadLen);  // saída - int ps32_ReadLen[1] - conta real da leitura de dados no elemento double
//---
   static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,        // entrada
                                               char  *ps8_ReadBuf,     // entrada
                                               int    s32_ReadBufLen,  // o tamanho do array ReadBuf[] no elemento char
                                               int*   ps32_ReadLen);   // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento char
//---
protegido:
   static DWORD SendTo(SOCKET h_Client,char *ps8_SendBuf,INT s32_SendBufLen);
   static DWORD ReadFrom(SOCKET h_Client,char *ps8_ReadBuf,INT s32_ReadBufLen,INT &s32_ReadLen);
  };

Agora vamos considerar todas as funções DLL na ordem que aparecem neste arquivo:

  1. ConnectTo - solicitação ao servidor para criar conexão:

    static EXPFUNC int __stdcall ConnectTo(char* ps8_ServerIP, // entrada - ps8_ServerIP = "0123456789123456"
                                           int   s32_Port,     // entrada 
                                           int*  ph_Client);   // entrada- int ph_Client[1]
    

    Parâmetros da função:

    • char * ps8_ServerIP - endereço IP do servidor que você deseja se conectar (por exemplo, "93.127.110.162"). Se o servidor é local, então especificar o IP, não "127.0.0.1", mas o IP obtido tal como foi descrito no exemplo 1.2.1.

    • int s32_Port - número da porta que o servidor "escuta para".

    • int* ph_Client - manipulador de conexão é colocado nessa variável, se a função for completada com sucesso. Este manipulador deve ser usado em todas as operações subsequentes a esta conexão.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    DWORD u32_Error = cNetEventsProcDLL::ConnectTo(SERVER_IP, PORT, ph_Client);
    
    if (u32_Error)
    {
            printf("\nConnectTo(...) failed with error: %d\n", u32_Error);
            return 1;
    }
    else
            printf("\nConnectTo(...) OK, ph_Client[0] = : %d\n", ph_Client[0]);
    
    int h_Client = ph_Client[0];
    
  2. ConnectClose - solicitação ao servidor para fechar a conexão.

    static EXPFUNC int __stdcall ConnectClose(int h_Client); // entrada 

    Parâmetros da função:

    • int h_Client - manipulador de conexão que deve ser fechado. Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API.

    Exemplo C++:

    int u32_Error = cNetEventsProcDLL::ConnectClose(h_Client);
    
        if (u32_Error)
                printf("\nConnectClose(...) failed with error: %d\n", u32_Error);
        else
                printf("\nConnectClose(...) OK.\n");
    
  3. ServerOpen - solicitação para criar o servidor.

    static EXPFUNC int __stdcall ServerOpen(int s32_Port); //entrada

    Parâmetros da função:

    • int s32_Port - número da porta que o servidor irá "escutar para", esperando as solicitaçoes dos clientes. Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API.

    Exemplo C++:

    int u32_Error = cNetEventsProcDLL::ServerOpen(s32_Port);
    
    if (u32_Error)
    {
            printf("\nServerOpen() failed with error: %d\n", u32_Error);
            return 1;
    }
    else
            printf("\nServerOpen() fine, we now are waiting for connections...\n");
    
  4. GetAllConnections - solicitação ao servidor para obter informações sobre todas as conexões atuais.

    static EXPFUNC int __stdcall GetAllConnections(int* ph_Client,         // saída - int ph_Client[62]
                                                   int* ps32_ClientIP,     // saída - int ps32_ClientIP[62]
                                                   int* ps32_ClientCount); // saída - int ps32_ClientCount[1]
    

    Parâmetros da função:

    • int ph_Client[62] - saída do array para que o servidor colocado trate de todas as conexões atuais.

    • int ps32_ClientIP[62] - saída do array para que o servidor coloque endereços IP de todas as conexões atuais. Para converter esses endereços para o formato padrão, como "92.127.110.161", use a função string FormatIP(int IP) para Expert Advisor МetaТrader 4 ou qualquer função semelhante, fornecida em exemplos de programas C++. O número 62 em tamanho do array é especificado não por coincidência, mas porque designa o limite do número de conexões possíveis (clientes) por um servidor.

    • int * ps32_ClientCount - servidor coloca o número das conexões atuais para esta variável, isto é, o número de elementos nos arrays acima mencionados.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    int ph_Client[62];
    int ps32_ClientIP[62];
    int ps32_ClientCount[1];
    
    int u32_Error = cNetEventsProcDLL::GetAllConnections(ph_Client, ps32_ClientIP, ps32_ClientCount);
                    
    if (u32_Error)
    {
        printf("\nGetAllConnections(...) failed with error: %d\n", u32_Error);
    }
    else
       int u32_Count = ps32_ClientCount[0];
    
  5. DisconnectClient - solicitação ao servidor para fechar conexão com um de seus clientes.

    static EXPFUNC int __stdcall DisconnectClient(int h_Client); // entrada 

    Parâmetros da função:

    • int h_Client - manipulador de conexão que deve ser fechado. Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API.

    Exemplo C++:

    int u32_Error = cNetEventsProcDLL::DisconnectClient(h_Client);
    
    if (u32_Error)
            printf("\nDisconnectClient(...) failed with error: %d\n", u32_Error);
    else
            printf("\nDisconnectClient(...) OK.\n");
    
  6. ServerClose - solicitação para fechar o servidor.

    static EXPFUNC int __stdcall ServerClose();  

    Ao fechar o servidor, todas as conexões atuais serão fechadas, de modo que cada cliente receberá o código de retorno "no connection" (nenhuma conexão), em resposta para qualquer operação. Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    int u32_Error = cNetEventsProcDLL::ServerClose();
    
    if (u32_Error)
            printf("\nServerClose() failed with error: %d\n", u32_Error);
    else
            printf("\nServerClose() OK.\n");
    
  7. O próximo grupo de funções corresponde diretamente a troca de dados via a conexão atual. Recebimento de dados para cada conexão corrente é realizado no modo assíncrono, ou seja, sem resposta do destinatário.

    Todos os dados são enviados e recebidos na forma de unidade independente de troca, ou seja, bloco. Todos os blocos para cada destinatário são acumulados na pilha FIFO. O destinatário pode recuperar esses blocos na pilha a qualquer momento. Cada função de troca opera com um único bloco.

    Para o envio de dados de operações, é possível que a operação seja bem sucedida, mas o código de retorno seja diferente de zero e pode ter os seguintes valores:

    • WSAEWOULDBLOCK - Operação foi bem sucedida, os dados ainda não foram enviados ao destinatário, mas serão a um tempo adequado. Não são necessárias ações do usuário.

    • WSA_IO_PENDING - Entrega de dados anterior ainda não está completa, o usuário deve tentar enviar dados posteriormente. Esta é uma situação normal, então considera-se que a função foi executada com sucesso.

    Qualquer outro código de retorno indica erro do usuário.

  8. SendToInt - solicitação para enviar bloco de dados (array tipo int) através da conexão atual.

    static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,        // entrada
                                           int*   ps32_SendBuf,    // in
                                           int    s32_SendBufLen); // entrada - o tamanho do array SendBuf[] no elemento int
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • int ps32_SendBuf[s32_SendBufLen] - bloco único (array tipo int) que precisa ser enviado ao cliente.

    • int s32_SendBufLen - tamanho do array.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    int ps32_SendBuf[200];
    int s32_SendBufLen=200;
    
    int u32_Error = cNetEventsProcDLL::SendToInt(h_Client, ps32_SendBuf, s32_SendBufLen);   
    
    switch (u32_Error)
    {
       case 0:
           printf("\nSendTo(...) OK");
           break;
       case WSAEWOULDBLOCK:
           printf("\nSendTo(%d...) return is: WSAEWOULDBLOCK(%d)\n",h_Client, u32_Error);
           printf("\nWSAEWOULDBLOCK -> The data will be send after the next FD_WRITE event, do nouthing\n");
           break;
       case WSA_IO_PENDING:
           printf("\nSendTo(%d...) return is: WSA_IO_PENDING(%d)\n",h_Client, u32_Error);
           printf("\nWSA_IO_PENDING -> Error: A previous Send operation is still pending. This data will not be sent, try latter\n");
           break;
    
       default:
           printf("\nSendTo(%d...)failed with severe error: %d\n",h_Client, u32_Error);
           break;
    };
    
  9. SendToDouble - solicitação de envio de blocos de dados (array tipo double) através da conexão atual.

    static EXPFUNC int __stdcall SendToDouble(SOCKET h_Client,          // entrada
                                              double*  pd_SendBuf,      // entrada
                                              int      s32_SendBufLen); // entrada - Tamnho do array SendBuf[] array no elemento int
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • double pd_SendBuf[s32_SendBufLen] - Bloco único (array tipo double) que necessita ser enviado ao cliente.

    • int s32_SendBufLen - tamanho do array.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    double pd_SendBuf[200];
    int    s32_SendBufLen=200;
    
    int u32_Error = cNetEventsProcDLL::SendToDouble(h_Client, pd_SendBuf, s32_SendBufLen);   
    
    switch (u32_Error)
    {
       case 0:
           printf("\nSendTo(...) OK");
           break;
       case WSAEWOULDBLOCK:
           printf("\nSendTo(%d...) return is: WSAEWOULDBLOCK(%d)\n",h_Client, u32_Error);
           printf("\nWSAEWOULDBLOCK -> The data will be send after the next FD_WRITE event, do nouthing\n");
           break;
       case WSA_IO_PENDING:
           printf("\nSendTo(%d...) return is: WSA_IO_PENDING(%d)\n",h_Client, u32_Error);
           printf("\nWSA_IO_PENDING -> Error: A previous Send operation is still pending. This data will not be sent, try latter\n");
           break;
    
       default:
           printf("\nSendTo(%d...)failed with severe error: %d\n",h_Client, u32_Error);
           break;
    };
    
  10. SendToString - solicitação para enviar bloco de dados (array tipo char) através da conexão atual.

    static EXPFUNC int __stdcall SendToString(SOCKET h_Client,        // entrada
                                              char*  ps8_SendBuf,     // entrada
                                              int    s32_SendBufLen); // entrada -  o tamanho do array SendBuf[] no elemento int
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • char ps8_SendBuf[s32_SendBufLen] - bloco único (array tipo int) que necessita ser enviado ao cliente.

    • int s32_SendBufLen - tamanho do array.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    char ps8_SendBuf[200];
    int  s32_SendBufLen=200;
    
    int u32_Error = cNetEventsProcDLL::SendToString(h_Client, ps8_SendBuf, s32_SendBufLen);   
    
    switch (u32_Error)
    {
       case 0:
           printf("\nSendTo(...) OK");
           break;
       case WSAEWOULDBLOCK:
           printf("\nSendTo(%d...) return is: WSAEWOULDBLOCK(%d)\n",h_Client, u32_Error);
           printf("\nWSAEWOULDBLOCK -> The data will be send after the next FD_WRITE event, do nouthing\n");
           break;
       case WSA_IO_PENDING:
           printf("\nSendTo(%d...) return is: WSA_IO_PENDING(%d)\n",h_Client, u32_Error);
           printf("\nWSA_IO_PENDING -> Error: A previous Send operation is still pending. This data will not be sent, try latter\n");
           break;
    
       default:
           printf("\nSendTo(%d...)failed with severe error: %d\n",h_Client, u32_Error);
           break;
    };
    
  11. ReadFromInt - solicitação para enviar bloco de dados (array tipo int) através da conexão atual.

    static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,       // entrada
                                             int*   ps32_ReadBuf,   // entrada
                                             int    s32_ReadBufLen, // o tamanho do array ReadBuf[] no elemento int
                                             int*   ps32_ReadLen);  // saída- int ps32_ReadLen[1] - conta real da leitura de dados no elemento int
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • int ps32_ReadBuf[s32_ReadBufLen] - array tipo int para receber bloco de dados.

    • int s32_ReadBufLen - tamanho do array de recebimento.

    • int* ps32ReadLen - esta variável contém o tamanho real do bloco de dados que foram recebidos e colocados no array ps32_ReadBuf[]. Se o tamanho do array de recebimento não for suficiente para receber o bloco de dados, então, com um sinal menos, esta variável irá manter o tamanho necessária para receber o bloco de dados. O bloco permanece na pilha e o código de retorno será igual a zero. Se não houver dados na pilha de cliente com manipulador especificado, esta variável será igual a zero e o código de retorno será também igual a zero.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    int ps32_ReadBuf[2000];
    int s32_ReadBufLen=2000;
    int ps32_ReadLen[1];
    int u32_Error = cNetEventsProcDLL::ReadFromInt(h_Client, ps32_ReadBuf, s32_ReadBufLen, ps32_ReadLen);   
            
    if(u32_Error)
        printf("ReadFromInt(%d...) failed with error: %d", h_Client, u32_Error);
    else
        if(ps32_ReadLen[0] >= 0) 
            printf("ReadFromInt(%d...) fine, %d int number was read", h_Client, ps32_ReadLen[0]);
        else 
            printf("ReadFromInt(%d...) fine, but ReadBuf must be at least %d int number size", h_Client, -ps32_ReadLen[0]);
    
  12. ReadFromDouble - solicitação para receber bloco de dados (array tipo double) através da conexão atual.

    static EXPFUNC int __stdcall ReadFromDouble(SOCKET h_Client,        // entrada
                                                double* pd_ReadBuf,     // entrada
                                                int     s32_ReadBufLen, // o tamanho do array ReadBuf[] no elemento double
                                                int*    ps32_ReadLen);  // saída - int ps32_ReadLen[1] - conta que realmente lê os dados no elemento double
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • double pd_ReadBuf[s32_ReadBufLen] - array tipo double para receber bloco de dados.

    • int s32_ReadBufLen - tamanho do array de recebimento.

    • int* ps32ReadLen - Esta variável contém o tamanho real do bloco de dados que foram recebidos e colocados no array ps32_ReadBuf[]. Se o tamanho do array de recebimento não for suficiente para receber o bloco de dados, então, com um sinal menos, esta variável irá manter o tamanho necessária para receber o bloco de dados. O bloco permanece na pilha e o código de retorno será igual a zero. Se não houver dados na pilha de cliente com manipulador especificado, esta variável será igual a zero e o código de retorno será também igual a zero.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    double ps32_ReadBuf[2000];
    int    s32_ReadBufLen = 2000;
    int    ps32_ReadLen[1];
    int    u32_Error = cNetEventsProcDLL::ReadFromDouble(h_Client, pd_ReadBuf, s32_ReadBufLen, ps32_ReadLen);   
            
    if(u32_Error)
        printf("ReadFromDouble(%d...) failed with error: %d", h_Client, u32_Error);
    else
        if(ps32_ReadLen[0] >= 0) 
            printf("ReadFromDouble(%d...) fine, %d double number was read", h_Client, ps32_ReadLen[0]);
        else 
            printf("ReadFromDouble(%d...) fine, but ReadBuf must be at least %d double number size", h_Client, -ps32_ReadLen[0]);
    
  13. ReadFromString - Solicitação para receber bloco de dados (array tipo double) através da conexão atual.

    static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,       // entrada
                                                char*  ps8_ReadBuf,    // entrada
                                                int    s32_ReadBufLen, // o tamanho do array ReadBuf[] no elemento char
                                                int*   ps32_ReadLen);  // out - int ps32_ReadLen[1] - conta atual da leitura dos dados no elemento char
    

    Parâmetros da função:

    • SOCKET h_Client - manipulador da conexão atual.

    • char ps8_ReadBuf[s32_ReadBufLen] - array tipo char para receber bloco de dados.

    • int s32_ReadBufLen - tamanho do array de recebimento.

    • int* ps32ReadLen - Esta variável contém o tamanho real do bloco de dados que foram recebidos e colocados no array ps32_ReadBuf[]. Se o tamanho do array de recebimento não for suficiente para receber o bloco de dados, então, com um sinal menos, esta variável irá manter o tamanho necessária para receber o bloco de dados. O bloco permanece na pilha e o código de retorno será igual a zero. Se não houver dados na pilha de cliente com manipulador especificado, esta variável será igual a zero e o código de retorno será também igual a zero.

    Se bem sucedida, a função retornará verdadeira, caso contrário ele retorna o código de erro Winsock2 API. Exemplo C++:

    char ps8_ReadBuf[2000];
    int  s32_ReadBufLen = 2000;
    int  ps32_ReadLen[1];
    int  u32_Error = cNetEventsProcDLL::ReadFromString(h_Client, ps8_ReadBuf, s32_ReadBufLen, ps32_ReadLen);   
            
    if(u32_Error)
        printf("ReadFromStrung(%d...) failed with error: %d", h_Client, u32_Error);
    else
        if(ps32_ReadLen[0] >= 0)
            printf("ReadFromString(%d...) fine, %d char was read", h_Client, ps32_ReadLen[0]);
        else 
            printf("ReadFromString(%d...) fine, but ReadBuf must be at least %d char size", h_Client, -ps32_ReadLen[0]);
    


3. Implementação do Projeto

O arquivo anexado, NetServerClient.zip, contém dois projetos Microsoft Visual Studio 2010 Ultimate:

  • NetEventsProc - to build NetEventsProc.exe
  • NetEventsProcDLL - to build NetEventsProcDLL.dll

Os códigos-fonte são comentados em detalhes. Você pode olhar para os detalhes da implementação e personalizar os projetos com suas necessidades específicas, se quiser.

NetEventsProc.exe implementa o servidor e os clientes usando soquetes assíncronos. Para comutar soquetes ao modo assíncrono, um dos possíveis métodos de operar no modo assíncrono é usado: soquetes de ligação aos eventos de rede WSAEventSelect (h_Socket, h_Event, FD_ALL_EVENTS).

Se este artigo causar interesse aos leitores, então na próxima versão do artigo iremos discutir todos os detalhes da implementação. Mas isso é tudo por agora. Observe mais uma vez que este projeto baseia-se no trabalho fundamental de um grande mestre, Elmue.


4. Conclusão

Espero que este artigo resolva o problema do intercâbio de informações entre o terminal МetaТrader 4 e outros aplicativos, independentemente da sua localização: se no computador local ou remoto em relação ao terminal instalado. Procurei não colocar neste artigo um monte de "código espaguete", fazendo do processo de usar as funções DLL algo bastante simples e claro. Pelo menos, eu fiz esforços para tal.

Algumas coisas muito importantes devem ser observadas:

Quase todos os computadores são os nós de uma rede local (LAN). Mesmo se você tiver apenas um computador, muito provavelmente o nó de LAN consiste de um computador.

A conexão na ampla área de rede (WAN) é realizada através de um dispositivo adicional de hardware que pode ser chamado de roteador, modem ou algum outro termo técnico. Para simplificar, vamos chamá-lo de roteador. É por esse dispositivo que se atribue o endereço IP global.

Roteadores resolvem certos problemas de segurança quando se trabalha com WAN, permitem economizar endereços IP globais e ajudam organizar a conexão WAN a partir da rede local, mas ao mesmo tempo distorce o sentido inicial da WAN que implica possibilidade de uma conexão direta via Peer-To-Peer de dois computadores.

Isto é devido ao fato de praticamente todo o roteador executar a chamada Network Address Translation (NAT). O endereço de rede é representado pela tupla <protocol, IP, port>. Qualquer elemento desta tupla pode ser alterado pelo roteador, tudo depende de determinado modelo de roteador.

Neste caso, o computador que acessa a WAN da LAN não tem todos os benefícios proporcionados por uma WAN pura. A grande maioria dos roteadores tem o recurso OUTBOUND que permite lembrar de endereços da rede de cliente LAN que está endereçado na rede global WAN com alguma solicitação.

Graças a esta característica, o roteador pode enviar ao cliente todas as informações recebidas como resposta à solicitação do cliente, assim cliente LAN podem se conectar a servidores WAN. No entanto, isto nem sempre é verdade, pois por razões de segurança e disciplina de trabalho, alguns endereços de rede podem ser bloqueados pelo hardware.

Portanto, para organizar servidor num computador LAN é necessário configurar o chamado encaminhamento de porta. Como no exemplo deste artigo, no caso mais simples de um computador da LAN, onde você precisa transmitir a número da porta 2000. Você pode fazê-lo sozinho, conectando-se ao mini-site roteador no navegador ou você pode recorrer a um profissional. Muito provavelmente este mini-site está disponível no link 192.168.1.1.

Isso tudo deve ser considerado, se você quiser ter possibilidade de trocar informações via WAN.

Na próxima versão do artigo, vamos considerar o tipo de conexão Peer-To-Peer (P2P).

Traduzido do russo por MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/1361

Arquivos anexados |
EXE.zip (21.35 KB)
FastStart.zip (28.26 KB)
NetServerClient.zip (56.32 KB)
Uma Sandbox Aleatória Uma Sandbox Aleatória

O artigo inclui uma "sandbox" interativa como um arquivo do Excel que simula dados aleatórios para backtest de Expert Advisor. Os leitores podem usar a sandbox para ajudar a explorar e compreender mais profundamente as métricas de desempenho dos EAs oferecidos por padrão com o MetaTrader. O texto do artigo é projetado para ajudar o usuário durante esta experiência.

MetaTrader 4 no Linux MetaTrader 4 no Linux

Linux é um sistema operacional Unix-like com software de código aberto para desenvolvimento e distribuição livre. Muitos usuários de PCs domésticos preferem o MS Windows. Sistemas Linux são amplamente utilizados em smartphones e hardware de servidor. Neste artigo, você vai descobrir como trabalhar o terminal MetaTrader 4 através de uma das versões do Linux - Ubuntu.

Sistema de Negociação Mecânica "Triângulo de Chuvashov's" Sistema de Negociação Mecânica "Triângulo de Chuvashov's"

Deixe-me oferecer-lhe uma visão geral e um código de programa do sistema de negociação mecânica baseado nas ideias de Stanislav Chuvashov. A construção do triângulo é baseada na intersecção de duas linhas de tendência construídas pelos fractais de alta e de baixa.

LibMatrix: Biblioteca de Álgebra Matricial (Parte I) LibMatrix: Biblioteca de Álgebra Matricial (Parte I)

O autor familiariza os leitores com uma simples biblioteca de álgebra matricial e fornece descrições e peculiaridades das principais funções.