Expert Advisor MetaTrader 4 para Intercâmbio de Informações com o Mundo Exterior
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:

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:
Coloque o arquivo EchoServer.mq4 na pasta de dados terminal e fazer a compilação: "МetaТrader 4\experts\".
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).
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.
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.
Agora você pode executar o programa Client.exe construído na etapa 3.
Você pode executar várias cópias deste programa e ver como o Expert Advisor troca informações com todas as cópias.
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:

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:
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.
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).
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.
Execute o cliente Client.mq4 em qualquer gráfico do terminal МetaТrader 4.
Você pode executar o cliente simultaneamente em vários gráficos, ou vários terminais ou computadores diferentes.
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:

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:
Coloque o arquivo ServerSendInd.mq4 na pasta de dados terminal e faça a compilação: "МetaТrader 4\experts\".
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.
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.....).
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.
Você pode executar o indicador ClientIndicator simultaneamente em vários gráficos, ou vários terminais ou computadores diferentes.
- 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:
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];
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");
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");
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];
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");
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");
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.
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; };
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; };
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; };
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]);
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]);
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]);
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:
Qualquer outro código de retorno indica erro do usuário.
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 pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1361
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Sistema de Negociação Mecânica "Triângulo de Chuvashov's"
Uma Sandbox Aleatória
LibMatrix: Biblioteca de Álgebra Matricial (Parte I)
MetaTrader 4 no Linux
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso