MetaTrader 4 Expert Advisor tauscht Informationen mit der Außenwelt aus

more | 29 April, 2016

Einführung

Hier ist ein Software-Tool, das MetaTrader 4 die Fähigkeit Server und Clients zu erzeugen bietet. Clients können eine Verbindung sowohl mit ihren eigenen Servern, als auch mit anderen Arten von Servern herzustellen, die Peer-to-Peer Protokoll-Verbindungen bereitstellen. Das angebotene Software-Tool besteht aus zwei Komponenten:

Dieser Artikel ist auf die folgende Weise strukturiert:

Das angehangene NetServerClient.zip Archiv enthält zwei Microsoft Visual Studio 2010 Ultimate Projekte: NetEventsProc - zum Erzeugen von NetEventsProc.exe, und NetEventsProcDLL - zum Erzeugen von NetEventsProcDLL.dll. Die Quellcodes sind in Einzelheiten kommentiert. Sie können einen Blick in die Einzelheiten der Umsetzung werfen, und das Projekt nach ihren speziellen Bedürfnissen anpassen, wenn Sie möchten.

NetEventsProc.exe implementiert Server und Clients mit asynchronen Sockets. Um die Sockets in den asynchronen Modus umzuschalten, wird eine der möglichen Methoden des Betriebs in asynchronem Modus verwendet: anbinden der Sockets an die WSAEventSelect(h_Socket, h_Event, FD_ALL_EVENTS) Netzwerk-Ereignisse.

Dieses Projekt basiert auf der grundlegenden Arbeit eines großartigen Master Elmue.



1. Schnell-Start

1.1. Das exe.zip Archiv

Entpacken Sie dieses Archiv. Sie werden die folgenden Komponenten finden:

Die folgenden Komponenten dieses Archivs stellen den Mechanismus zum Importieren von DLL Funktionen von NetEventsProcDLL.dll zu Anwendungen bereit:

Damit ist der Konfigurationsprozess eigentlich abgeschlossen. Jetzt können Sie MetaTrader 4 Expert Advisors in jeder Programmiersprache mit der Verwendung von DLL-Funktionen für die Erstellung von Servern und Clients schreiben.

Um nicht abzuschweifen, stellen wir die Quellcodes von ImportNetEventsProcDLL.mqh und cNetEventsProcDLL.h direkt hier bereit. Die ImportNetEventsProcDLL.mqh Header Datei enthält Prototypen von importieren DLL-Funktionen des NetEventsProcDLL.dll Programms und zwei zusätzliche Dienstfunktionen:

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

Die GetErrMsg Funktion konvertiert die Rückgabecodes von DLL-Funktionen in Text. Die FormatIP Funktion konvertiert die binäre Darstellung von IP-Adressen in das Standard-Textformat, wie "93.127.110.161". Sie sollten die ImportNetEventsProcDLL.mqh Datei in den Terminal Datenordner "MetaTrader 4\experts\include\" platzieren.

Hier ist der Quellcode von ImportNetEventsProcDLL.mqh (nur ein Teil der Datei ist angegeben, der direkt der Definition von Prototypen von importierten DLL-Funktionen entspricht):

// ImportNetEventsProcDLL.mqh

#import "NetEventsProcDLL.dll"
// Only for Clients:
int ConnectTo(string  ps8_ServerIP,             // in - string ps8_ServerIP = "0123456789123456"
              int     s32_Port,                 // in 
              int&    h_Client[]);              // out - int h_Client[1]
                 
int ConnectClose(int h_Client);                 // in
//
// Only for Server:
int ServerOpen(int  s32_Port);                  // in

int GetAllConnections(int& ph_Client[],         // out - int ph_Client[62]
                      int& ps32_ClientIP[],     // out - int ps32_ClientIP[62]
                      int& ps32_ClientCount[]); // out - int ps32_ClientCount[1]
                       
int DisconnectClient(int h_Client);             // in

int ServerClose();
//
// For both: Clients and Server
int SendToInt   (int  h_Client,             // in
                 int& ps32_SendBuf[],       // in
                 int  s32_SendBufLen);      // in - SendBuf[] array size in int element 
                 
int SendToDouble(int     h_Client,          // in
                 double& pd_SendBuf[],      // in
                 int     s32_SendBufLen);   // in - SendBuf[] array size in double element 
                 
int SendToString(int    h_Client,           // in
                 string ps8_SendBuf,        // in
                 int    s32_SendBufLen);    // in - SendBuf string size in char element
                 

int ReadFromInt   (int h_Client,            // in
                   int& ps32_ReadBuf[],     // in 
                   int  s32_ReadBufLen,     // in  - ReadBuf[] array size in int element
                   int& ps32_ReadLen[]);    // out - int ps32_ReadLen[1] - count of actually read data in int element
                  
int ReadFromDouble(int     h_Client,        // in
                   double& pd_ReadBuf[],    // in
                   int     s32_ReadBufLen,  // in  - ReadBuf[] array size in double element
                   int&    ps32_ReadLen[]); // out - int ps32_ReadLen[1] - count of actually read data in double element
                   
int ReadFromString(int     h_Client,        // in
                   string  ps8_ReadBuf,     // in
                   int     s32_ReadBufLen,  // in  - ReadBuf   string size in char element
                   int&    ps32_ReadLen[]); // out - int ps32_ReadLen[1] - count of actually read data in char element
//                   
#import
//***************************************************************************************
...
...
...
// Get a human readable error message for an API error code
string GetErrMsg(int s32_Error)
{
   ...
   .. 
}

// Convert DWORD IP to string IP
string FormatIP(int IP)
{
   ...
   ...
   ...
}       

Die cNetEventsProcDLL.h Datei hat C++ Klassendefinition mit allen DLL-Funktionen importiert aus NetEventsProcDLL.dll. Hier ist der Quellcode dieser Datei:

//+---------------------------------------------------------------------------+
//|                                            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, //in - ps8_ServerIP = "0123456789123456"
                                           int   s32_Port,     //in 
                                           int*  ph_Client);   //out - int ph_Client[1]

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

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

    static EXPFUNC int __stdcall GetAllConnections(int* ph_Client,         // out - int ph_Client[62]
                                                   int* ps32_ClientIP,     // out - int ps32_ClientIP[62]
                                                   int* ps32_ClientCount); // out - int ps32_ClientCount[1]
    
    static EXPFUNC int __stdcall DisconnectClient(SOCKET h_Client);        // in 

    static EXPFUNC int __stdcall ServerClose();

    static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,             // in
                                           int*   ps32_SendBuf,         // in
                                           int    s32_SendBufLen);      // in - SendBuf[] array size in int element

    static EXPFUNC int __stdcall SendToDouble(SOCKET  h_Client,         // in
                                              double* pd_SendBuf,       // in
                                              int     s32_SendBufLen);  // in - SendBuf[] array size in double element

    static EXPFUNC int __stdcall SendToString(SOCKET h_Client,          // in
                                              char*  ps8_SendBuf,       // in
                                              int    s32_SendBufLen);   // SendBuf string size in char element

    static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,           // in
                                             int*   ps32_ReadBuf,       // in
                                             int    s32_ReadBufLen,     // ReadBuf[] array size in int element
                                             int*   ps32_ReadLen);      // out - int ps32_ReadLen[1] - actual count of read data in int element

    static EXPFUNC int __stdcall ReadFromDouble(SOCKET  h_Client,       // in
                                                double* pd_ReadBuf,     // in
                                                int     s32_ReadBufLen, // ReadBuf[] array size in double element
                                                int*    ps32_ReadLen);  // out - int ps32_ReadLen[1] - actual count of read data in double element

    static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,        // in
                                                char*  ps8_ReadBuf,     // in
                                                int    s32_ReadBufLen,  // ReadBuf[] array size in char element
                                                int*   ps32_ReadLen);   // out - int ps32_ReadLen[1] - actual count of read data in char element
//*******************************************************************************************************************
protected:
    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. Das FastStart.zip Archiv

Dieses Archiv enthält Quellcodes aller in Demo-Beispielen verwendeter Programme. C++ Programme sind dargestellt als die Microsoft Visual Studio 2010 Ultimate Projekte Client und EchoServer. Die Quellcodes von MQL4 Programmen sind ebenfalls in diesem Archiv vorhanden, zusammen mit der ImportNetEventsProcDLL.mqh Datei zum Import von DLL-Funktionen in MQL4 Programme. Platzieren Sie diese Datei in dem Ordner "MetaTrader 4\experts\include\".

Bei der weiteren Diskussion werden die Quellcodes all dieser Programme im Text aufgeführt.Wir werden 3 Beispiele betrachten, welche die Arbeit mit allen DLL-Funktionen in MQL4 und C++ Programmiersprachen demonstrieren:

1.2.1. MetaTrader 4 Expert Advisor-Server & C++ Programm-Client

Betrachten Sie diese traditionelle Aufgabe des Informationsaustauschs zwischen MetaTrader 4 Expert Advisor und einem C++ Programm:

Der C++ Client liest von dem Anwender in die Konsole eingegebene Nachrichten und sendet diese an den Expert Advisor. Der Expert Advisor empfängt diese Nachrichten, zeigt sie im Terminalfenster an und sendet sie zurück an den Empfänger. Die folgenden Bilder veranschaulichen diesen Gedanken:

Figure 1. ?eta?rader 4 Expert Advisor-server & C++ Program-client

Hier ist der Quellcode des MetaTrader 4 Expert Advisor EchoServer.mq4 der als in Echo Server fungiert:

//+---------------------------------------------------------------------------+
//|                                            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); // in
*/
/*int ServerClose();
*/
/*int GetAllConnections(int& ph_Client[],         // out - int ph_Client[62]
                        int& ps32_ClientIP[],     // out - int ps32_ClientIP[62]
                        int& ps32_ClientCount[]); // out - int ps32_ClientCount[1]
*/                      
/*int SendToString(int    h_Client,               // in
                   string ps8_SendBuf,            // in
                   int    s32_SendBufLen);        // in - SendBuf string size in char element
*/ 
/*int ReadFromString(int     h_Client,            // in
                   string    ps8_ReadBuf,         // in
                   int       s32_ReadBufLen,      // in  - ReadBuf   string size in char element
                   int&      ps32_ReadLen[]);     // out - int ps32_ReadLen[1] - count of actually read data in char element     
*/                              
// Globals variables
int s32_Error;
int i;

int s32_Port = 2000;
bool b_ServerOpened = false;

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


// for int ReadFromString(h_Client, ps8_Buf, s32_BufLen, ps32_ReadLen)
// for int SendToString  (h_Client, ps8_Buf, s32_BufLen)
string ps8_Buf = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int    s32_BufLen;
int    ps32_ReadLen[1];
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Und hier ist der Quellcode von Client.cpp - das C++ Programm, das als Client für den Expert Advisor-Server fungiert:

//+---------------------------------------------------------------------------+
//|                                            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
// Returns a list of all local IP's on this computer (multiple IP's if multiple network adapters)
DWORD GetLocalIPs(char s8_IpList[][20], int &s32_IpCount);

/* This is the simple Client.
   It connects to Echo Server, send your input to Echo Server and read Echo from Server.

   ATTENTION !!! If your want to connect to local Server do not use  "127.0.0.1" !!!ATTENTION !!! 
   You may get all your local IP's by means of GetLocalIPs(...) function, here is call example:
   // Local IP's list
        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]);
        //
*/

// This is the Server we want to connect to...
#define SERVER_IP   "192.168.1.5"  //this is mine local IP's get by means of GetLocalIPs(...) function,
                                   // do not use so called loopback  "127.0.0.1" address !!! 
#define PORT         2000

int main()
{
        // Local IP's list
        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];

        // Connection is established successfully ! Let's  have some SendTo(...) data on this connection...
        char  ps8_SendData[200];
        int   s32_SendDataLen;;

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

        while(true)
        {
                std::cout << "\nEnter something to send to Server or Exit '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);
                        // Severe error -> abort event 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
// Returns a list of all local IP's on this computer (multiple IP's if multiple network adapters)
DWORD GetLocalIPs(char s8_IpList[][20], int &s32_IpCount)
{
        // Winsock version 2.0 is available on ALL Windows operating systems 
        // except Windows 95 which comes with 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; // The IP list is zero terminated

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

                s32_IpCount++;
        }

        if (!s32_IpCount)
                return WSAENETDOWN; // no local IP means no network available

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

Zum Ausführen dieses Demo-Beispiels müssen Sie:

  1. Platzieren Sie die EchoServer.mq4 Datei in den Terminal Datenordner "MetaTrader 4\experts\" und kompilieren Sie sie.

  2. Öffnen Sie das Client Projekt in Microsoft Visual Studio 2010 Ultimate und erstellen Sie es mit der Release Konfiguration. Wenn Sie das Projekt in einer anderen IDE erstellen möchten, vergessen Sie nicht das NetEventsProcDLL.lib Modul als einen zusätzlichen Eintrag für den Editor anzugeben (kompilieren Sie es mit: /EHsc/link NetEventsProcDLL.lib).

  3. Führen sie das ausführbare Modul Client.exe aus. Sie werden einen Fehler mit dem Code 10057 erhalten, und die Liste mit den lokalen IPs von Ihrem Computer. Korrigieren Sie den folgenden String in dem Client.cpp Quellcode.

    #define SERVER_IP   "192.168.1.5"

    durch ersetzen von "192.168.1.5" mit der ersten (oder einzigen) lokalen IP und kompilieren Sie das Projekt erneut.

  4. Führen Sie den EchoServer Expert Advisor auf jedem Chart im MetaTrader 4 Terminal aus. Wenn alles richtig gemacht wurde, wird das Terminalfenster die folgenden Nachrichten anzeigen: "ServerOpen() return is: OK", "Server is Opened and Waiting for Client connection requests...". Dann prüft der Expert Advisor bei jedem Tick Verbindungsanforderungen, verbindet, liest eingehende Nachrichten von jeder Verbindung und sendet eine Kopie der Nachricht zurück an den Empfänger. Der Expert Advisor zeigt alle aktuellen Informationen im Terminalfenster an.

  5. Sie können jetzt das in Schritt 3 erstellte Client.exe Programm ausführen.

  6. Sie können mehrere Kopien von diesem Programm ausführen und beobachten, wie der Expert Advisor mit allen Kopien Informationen austauscht.

  7. Wenn Sie eine globale IP des Computers einstellen, auf dem der Expert Advisor ausgeführt wird, anstatt seiner lokalen IP wie in Schritt 3, können Sie in diesem Fall Client.exe ausführen und mit dem Expert Advisor auf jedem anderen Computer kommunizieren, der mit dem Internet verbunden ist. Natürlich dürfen Sie nicht vergessen den aktuellen Firewall-Schutz zu deaktivieren.

1.2.2. C++ Programm-Server & MetaTrader 4 Expert Advisor-Client

Betrachten wir nun die traditionelle Aufgabe des Informationsaustauschs zwischen MetaTrader 4 und einem C++ Programm:

Der Expert Advisor-Client liest die Kurse des Symbols auf dem er ausgeführt wird und sendet diese Kurse an den C++ Server. Der C++ Server gibt die Kurse zurück an den Empfänger, der diese empfängt und in dem Terminalfenster anzeigt. Die Bilder unten veranschaulichen diesen Gedanken:

Figure 2. C++ Program-server & MetaTrader 4 Expert Advisor-client

Hier ist der Quellcode des MetaTrader 4 Expert Advisor Client.mq4 der als Client fungiert:

//+---------------------------------------------------------------------------+
//|                                            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,         // in - string ps8_ServerIP = "0123456789123456"
                int     s32_Port,             // in 
                int&    ph_Client[]);         // out - int ph_Client[1]
*/
/*int SendToDouble(int     h_Client,          // in
                   double& pd_SendBuf[],      // in
                   int     s32_SendBufLen);   // in - SendBuf[] array size in double element 
*/
/*int ReadFromDouble(int     h_Client,        // in
                     double& pd_ReadBuf[],    // in
                     int     s32_ReadBufLen,  // in  - ReadBuf[] array size in double element
                     int&    ps32_ReadLen[]); // out - int ps32_ReadLen[1] - count of actually read data in double element
*/                      
/*int ConnectClose(int h_Client);             // in
*/ 
       
// Globals variables
int s32_Error;
int i;
// for 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;

// for int SendToDouble(ph_Client[0], pd_Buf, s32_BufLen);  
// for int ReadFromDouble(ph_Client[0], pd_Buf, s32_BufLen, ps32_ReadLen);
double pd_Buf[1];
int    s32_BufLen = 1;
int    ps32_ReadLen[1];
//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Und hier ist der Quellcode des EchoServer.cpp C++ Programm, das als Echo Server für den Expert Advisor-Client fungiert:

//+---------------------------------------------------------------------------+
//|                                            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;
        
        // Try to create server listening on port 2000
        // You may change port.
        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;
}

Zum Ausführen dieses Demo-Beispiels müssen Sie:

  1. Platzieren Sie die Client.mq4 Datei in den Terminal Datenordner "MetaTrader 4\experts\", weisen Sie die lokale IP (erhalten in dem vorherigen Beispiel 1.2.1.) dem String zu.

    string ps8_ServerIP = "192.168.1.5";

    und kompilieren Sie sie. Wenn der C++ Server auf einem anderen Computer ausgeführt wird, dann fügen Sie die globale IP dieses Computers ein. Vergessen Sie nicht bestehenden Schutz Ihrer Firewall zu deaktivieren.

  2. Öffnen Sie das Client Projekt in Microsoft Visual Studio 2010 Ultimate und erstellen Sie es mit der Release Konfiguration. Wenn Sie das Projekt in einer anderen IDE erstellen möchten, vergessen Sie nicht das NetEventsProcDLL.lib Modul als einen zusätzlichen Eintrag für den Editor anzugeben (kompilieren Sie es mit: /EHsc/link NetEventsProcDLL.lib).

  3. Führen Sie den EchoServer.exe C++ Server aus. Wenn alles richtig gemacht wurde, wird das Terminalfenster die folgende Nachrichte anzeigen: "ServerOpen() fine, we now are waiting for connections.....". Das Drücken einer beliebigen Taste wird den Server schließen und das Programm beenden.

  4. Führen Sie den Client.mq4 Client auf jeden Chart des MetaTrader 4 Terminal aus.

  5. Sie können den Client auf mehreren Charts gleichzeitig ausführen, in einem oder in verschiedenen Terminals und einem oder verschiedenen Computern.

  6. Beobachten Sie wie der C++ Server und die MetaTrader Clients arbeiten. Drücken Sie eine beliebige Taste in der C++ Server-Konsole und das Programm wird den Server schließen und beenden.

1.2.3. MetaTrader 4 Expert Advisor-Indikator (der Expert Advisor-Server) & MetaTrader 4 Client-Indikator

Der Expert Advisor-Indikator (der Expert Advisor-Server) versorgt Client-Indikatoren mit den Indikatorwerten. In diesem Fall sind dies die Werte des Standard iEnvelops(...) Indikators. Dieses Beispiel kann einen praktischen Wert haben für die Verteilung von Werten des "gesicherten" Indikators an alle Teilnehmer-Clients.

Die Bilder unten veranschaulichen diesen Gedanken:

Figure 3. ?eta?rader 4 Expert Advisor-indicator (the Expert Advisor-server) & ?eta?rader 4 Client-indicator

Hier ist der Quellcode des MetaTrader 4 Expert Advisor ServerSendInd.mq4, der als Anbieter der iEnvelops(...) Indikatorwerte fungiert:

//+---------------------------------------------------------------------------+
//|                                            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);                  // in
*/
/*int ServerClose();
*/
/*int GetAllConnections(int& ph_Client[],         // out - int ph_Client[62]
                        int& ps32_ClientIP[],     // out - int ps32_ClientIP[62]
                        int& ps32_ClientCount[]); // out - int ps32_ClientCount[1]
*/                      
/*int ReadFromString(int     h_Client,            // in
                   string    ps8_ReadBuf,         // in
                   int       s32_ReadBufLen,      // in  - ReadBuf   string size in char element
                   int&      ps32_ReadLen[]);     // out - int ps32_ReadLen[1] - count of actually read data in char element     
*/ 
/*int SendToDouble(int     h_Client,              // in
                   double& pd_SendBuf[],          // in
                   int     s32_SendBufLen);       // in - SendBuf[] array size in double element   
*/                                            
// Globals variables
int s32_Error;
int i;

int s32_Port = 2000;
bool b_ServerOpened = false;

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

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

// for 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;

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+

Und hier ist der Quellcode des Client-Indikators ClientIndicator.mq4, der die iEnvelops(...) Indikatorwerte von dem ServerSendInd.mq4 Expert Advisor empfängt:

//+---------------------------------------------------------------------------+
//|                                            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,         // in - string ps8_ServerIP = "0123456789123456"
                int     s32_Port,             // in 
                int&    ph_Client[]);         // out - int ph_Client[1]
*/
/*                      
/*int ConnectClose(int h_Client);             // in
*/
/*int SendToString(int    h_Client,           // in
                   string ps8_SendBuf,        // in
                   int    s32_SendBufLen);    // in - SendBuf string size in char element
*/
/*int ReadFromDouble(int     h_Client,        // in
                     double& pd_ReadBuf[],    // in
                     int     s32_ReadBufLen,  // in  - ReadBuf[] array size in double element
                     int&    ps32_ReadLen[]); // out - int ps32_ReadLen[1] - count of actually read data in double element
*/
// Globals variables
int s32_Error;
int i;
// for 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;

// for int SendToString  (h_Client, ps8_SendBuf, s32_SendBufLen)
string ps8_SendBuf = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
int    s32_SendBufLen;  
 
// for 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[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
//---- indicators
   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);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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();       // Number of calculated bars 
    i = Bars - Counted_bars - 1;                 // Index of first not-calculated
    if (i > BARS_COUNT/2-1)  i = BARS_COUNT/2-1; // Calculate specified count if there are many bars
//-----------------------------------------------------------------------  
    for (i = BARS_COUNT/2-1; i >= 0; i--)
    {
         UpperLine  [i] = pd_ReadBuf[i];
         LowerLine  [i] = pd_ReadBuf[i+BARS_COUNT/2];
    }  
    
    return;                          
} // end of int start()
//--------------------------------------------------------------------

Zum Ausführen dieses Demo-Beispiels müssen Sie:

  1. Platzieren Sie die ServerSendInd.mq4 Datei in den Terminal Datenordner "MetaTrader 4\experts\" and compile it.

  2. Platzieren Sie die ClientIndicator.mq4 Datei in den Terminal Datenordner "MetaTrader 4\experts\indicators\" und weisen Sie die lokale IP (erhalten in Beispiel 1.2.1.) dem String zu:

    string ps8_ServerIP = "192.168.1.5";

    Wenn ServerSendInd auf einem anderen Computer ausgeführt wird, dann fügen Sie die globale IP-Adresse dieses Computers ein. Vergessen Sie nicht den bestehenden Schutz Ihrer Firewall zu deaktivieren. Kompilieren Sie sie.

  3. Führen Sie ServerSendInd aus. Wenn alles richtig gemacht wurde, wird das Terminalfenster die folgende Nachricht anzeigen: "ServerOpen() fine, we now are waiting for connections.....".

  4. Führen Sie auf jedem Chart des MetaTrader 4 Terminal den ClientIndicator.mq4 Indikator aus. Zwei Indikator-Linien werden auf dem Chart erscheinen. Wenn Sie den Standard Envelope Indikator auf dem gleichen Chart verwenden, stellen Sie sicher, dass beide Linien von unserem Indikator mit Linien des Standard-Indikators übereinstimmen.

  5. Sie können den ClientIndicator Indikator auf mehreren Charts gleichzeitig ausführen, auf einem oder in verschiedenen Terminals, auf einem oder auf verschiedenen Computern.

  6. Beobachten Sie wie der ServerSendInd Server und der ClientIndicator arbeiten. Versuchen Sie den Zeitrahmen des Charts mit dem ClientIndikator Indikator zu ändern. Der ServerSendInd Server wird sofort eingestellt die Indikatorwertefür diesen Zeitrahmen zu senden.


2. DLL Schnittstellen-Spezifikation

In diesem Abschnitt beschreiben wir ausführlich alle DLL-Funktionen und Parameter, die zu deren Aufruf erforderlich sind. Alle Funktionen geben bei erfolgreicher Ausführung Null zurück. Andernfalls geben die Funktionen winsock2 API Fehler Codes zurück. Alle exportierten DLL-Funktionen sind in der C++ Klassendeklaration cNetEventsProcDLL.h angegeben, weshalb wir den Quellcode dieser Datei bereitstellen:

//+---------------------------------------------------------------------------+
//|                                            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,             // in - ps8_ServerIP = "0123456789123456"
                                          int   s32_Port,                 // in 
                                          int*  ph_Client);               // out - int ph_Client[1]
//---
   static EXPFUNC int __stdcall ConnectClose(int h_Client);               // in 
//---
   static EXPFUNC int __stdcall ServerOpen(int s32_Port);                 // in
//---
   static EXPFUNC int __stdcall GetAllConnections(int* ph_Client,         // out - int ph_Client[62]
                                                  int* ps32_ClientIP,     // out - int ps32_ClientIP[62]
                                                  int* ps32_ClientCount); // out - int ps32_ClientCount[1]
//---
   static EXPFUNC int __stdcall DisconnectClient(SOCKET h_Client);        // in 
//---
   static EXPFUNC int __stdcall ServerClose();
//---
   static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,             // in
                                          int   *ps32_SendBuf,         // in
                                          int    s32_SendBufLen);      // in -  SendBuf[] array size in int element
//---
   static EXPFUNC int __stdcall SendToDouble(SOCKET  h_Client,         // in
                                             double* pd_SendBuf,       // in
                                             int     s32_SendBufLen);  // in -  SendBuf[] array size in double element
//---
   static EXPFUNC int __stdcall SendToString(SOCKET h_Client,          // in
                                             char*  ps8_SendBuf,       // in
                                             INT   s32_SendBufLen);    // SendBuf string size in char element
//---
   static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,           // in
                                            int   *ps32_ReadBuf,       // in
                                            int    s32_ReadBufLen,     // ReadBuf[] array size in int element
                                            int   *ps32_ReadLen);      // out - int ps32_ReadLen[1] - actual count of read data in int element
//---
   static EXPFUNC int __stdcall ReadFromDouble(SOCKET  h_Client,       // in
                                               double *pd_ReadBuf,     // in
                                               int     s32_ReadBufLen, // ReadBuf[] array size in double element
                                               int    *ps32_ReadLen);  // out - int ps32_ReadLen[1] - actual count of read data in double element
//---
   static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,        // in
                                               char  *ps8_ReadBuf,     // in
                                               int    s32_ReadBufLen,  // ReadBuf[] array size in char element
                                               int*   ps32_ReadLen);   // out - int ps32_ReadLen[1] - actual count of read data in char element
//---
protected:
   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);
  };

Betrachten wir nun alle DLL-Funktionen in der Reihenfolgen ihres Auftretens in dieser Datei:

  1. ConnectTo - Anforderung an den Server eine Verbindung herzustellen:

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

    Funktionsparameter:

    • char* ps8_ServerIP - IP Adresse des Servers zu dem Sie verbinden möchten (zum Beispiel, "93.127.110.162"). Wenn der Server lokal ist, geben Sie die IP an, nicht "127.0.0.1", sondern die erhaltene IP, wie es in Beispiel 1.2.1 beschrieben ist.

    • int s32_Port - Port-Nummer, die der Server "hört".

    • int* ph_Client - der Verbindung-Identifikator wird in dieser Variable platziert, wenn die Funktion erfolgreich beendet wurde. Dieser Identifikator muss bei allen nachfolgenden Operationen für diese Verbindung verwendet werden.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung an den Server die Verbindung zu trennen.

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

    Funktionsparameter:

    • int h_Client - Identifikator der Verbindung die getrennt werden soll. Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück.

    C++ Beispiel:

    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 - Anforderung den Server zu erstellen.

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

    Funktionsparameter:

    • int s32_Port - Port-Nummer der der Server "zuhört", während er auf eine Client-Anforderung wartet. Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück.

    C++ Beispiel:

    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 - Anforderung an den Server alle Informationen über alle bestehenden Verbindungen zu erhalten.

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

    Funktionsparameter:

    • int ph_Client[62] - Ausgabe Array, in dem der Server Indentifikatoren aller aktuellen Verbindungen platziert.

    • int ps32_ClientIP[62] - Ausgabe Array, in dem der Server IP-Adressen aller aktuellen Verbindungen platziert. Um diese Adressen in das Standardformat zu konvertieren, wie "92.127.110.161", verwenden Sie die string FormatIP(int IP) Funktion für MetaTrader 4 Expert Advisor oder eine ähnliche Funktion, angegeben in den Beispielen der C++ Programme. Die Anzahl 62 in der Arraygröße ist nicht zufällig festgelegt: sie bezeichnet das Limit der Anzahl möglicher Verbindungen (Clients) pro Server.

    • int* ps32_ClientCount - der Server platziert die Anzahl der aktuellen Verbindungen in diese Variable, das heißt, die Anzahl an Elementen in den oben erwähnten Arrays.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung an den Server die Verbindung mit einem seiner Clients zu schließen.

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

    Funktionsparameter:

    • int h_Client - Verbindung-Identifikator der geschlossen werden muss. Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück.

    C++ Beispiel:

    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 - Aufforderung den Server zu schließen.

    static EXPFUNC int __stdcall ServerClose(); 

    Wenn der Server geschlossen wird, werden alle aktuellen Verbindungen geschlossen, so dass jeder Client den "No Connection" Rückgabecode als Antwort auf jeden Vorgang erhält. Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    int u32_Error = cNetEventsProcDLL::ServerClose();
    
    if (u32_Error)
            printf("\nServerClose() failed with error: %d\n", u32_Error);
    else
            printf("\nServerClose() OK.\n");
  7. Die nächste Gruppe entspricht direkt dem Datenaustausch über die aktuelle Verbindung. Das Empfangen der Daten für jede aktuelle Verbindung wird indem asynchronen Modus ausgeführt, das heißt, ohne Antwort des Empfängers.

    Alle Daten werden in Form einer unabhängigen Austauscheinheit gesendet und empfangen, das heißt, als Block. Alle Blöcke für jeden Empfänger werden in dem FIFO-Stapel gesammelt. Der Empfänger kann diese Blöcke zu jeder Zeit von dem Stapel empfangen. Jede Austauschfunktion arbeitet mit einem einzigen Block.

    Für Daten-Sendeoperationen ist es möglich, dass sie erfolgreich war, aber der Rückgabecode von Null abweicht und die folgenden Werte haben kann:

    • WSAEWOULDBLOCK - Operation war erfolgreich, die Daten wurden noch nicht an den Empfänger gesendet, werden aber zu einem geeigneten Zeitpunkt gesendet. Es sind keine Benutzerhandlungen erforderlich.

    • WSA_IO_PENDING - die vorherige Datenauslieferung ist noch nicht abgeschlossen, der Benutzer muss versuchen die Daten später zu senden. Dies ist eine normale Situation, also wird angenommen, dass die Funktion erfolgreich ausgeführt wurde.

    Jeder andere Rückgabecode gibt einen Benutzerfehler an.

  8. SendToInt - Anforderung zum Senden des Datenblocks (int Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall SendToInt(SOCKET h_Client,        // in
                                           int*   ps32_SendBuf,    // in
                                           int    s32_SendBufLen); // in - SendBuf[] array size in int element

    Funktionsparameter:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • int ps32_SendBuf[s32_SendBufLen] - einzelner Block (int Typ Array) den Sie an den Client senden müssen.

    • int s32_SendBufLen - Arraygröße.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung zum Senden des Datenblocks (double Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall SendToDouble(SOCKET h_Client,          // in
                                              double*  pd_SendBuf,      // in
                                              int      s32_SendBufLen); // in - SendBuf[] array size in int element

    Function parameters:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • double pd_SendBuf[s32_SendBufLen] - einzelner Block (double Typ Array) den Sie an den Client senden müssen.

    • int s32_SendBufLen - Arraygröße.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung zum Senden des Datenblocks (char Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall SendToString(SOCKET h_Client,        // in
                                              char*  ps8_SendBuf,     // in
                                              int    s32_SendBufLen); // in -  SendBuf[] array size in int element

    Funktionsparameter:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • char ps8_SendBuf[s32_SendBufLen] - einzelner Block (char Typ Array) den Sie an den Client senden müssen.

    • int s32_SendBufLen - Arraygröße.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung zum Empfang des Datenblocks (int Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall ReadFromInt(SOCKET h_Client,       // in
                                             int*   ps32_ReadBuf,   // in
                                             int    s32_ReadBufLen, // ReadBuf[] array size in int element
                                             int*   ps32_ReadLen);  // out - int ps32_ReadLen[1] - actual count of read data in int element

    Funktionsparameter:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • int ps32_ReadBuf[s32_ReadBufLen] - int Typ Array für den Empfang des Datenblocks.

    • int s32_ReadBufLen - Größe des empfangenden Array.

    • int* ps32ReadLen - diese Variable hält die reale Größe des Datenblocks, der empfangen und in das ps32_ReadBuf[] Array platziert wurde. Wenn die Größe des empfangenden Array nicht ausreichend für den Empfang des Datenblocks ist, wird diese Variable die Größe halten, die für den Empfang des Datenblocks erforderlich ist, mit einem Minus-Zeichen. Der Block bleibt in dem Stapel und der Rückgabecode wird gleich Null sein. Wenn es in de Client-Stapel keine Daten mit dem angegebenen Identifikator gibt, wird diese Variable gleich Null sein und der Rückgabecode wird ebenfalls gleich Null sein.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung zum Empfang des Datenblocks (double Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall ReadFromDouble(SOCKET h_Client,        // in
                                                double* pd_ReadBuf,     // in
                                                int     s32_ReadBufLen, // ReadBuf[] array size in double element
                                                int*    ps32_ReadLen);  // out - int ps32_ReadLen[1] - actual count of read data in double element

    Funktionsparameter:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • double pd_ReadBuf[s32_ReadBufLen] - double Typ Array für den Empfang des Datenblocks.

    • int s32_ReadBufLen - Größe des empfangenden Array.

    • int* ps32ReadLen - diese Variable hält die reale Größe des Datenblocks, der empfangen und in das ps32_ReadBuf[] Array platziert wurde. Wenn die Größe des empfangenden Array nicht ausreichend für den Empfang des Datenblocks ist, wird diese Variable die Größe halten, die für den Empfang des Datenblocks erforderlich ist, mit einem Minus-Zeichen. Der Block bleibt in dem Stapel und der Rückgabecode wird gleich Null sein. Wenn es in de Client-Stapel keine Daten mit dem angegebenen Identifikator gibt, wird diese Variable gleich Null sein und der Rückgabecode wird ebenfalls gleich Null sein.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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 - Anforderung zum Empfang des Datenblock (char Typ Array) über die aktuelle Verbindung.

    static EXPFUNC int __stdcall ReadFromString(SOCKET h_Client,       // in
                                                char*  ps8_ReadBuf,    // in
                                                int    s32_ReadBufLen, // ReadBuf[] array size in char element
                                                int*   ps32_ReadLen);  // out - int ps32_ReadLen[1] - actual count of read data in char element

    Funktionsparameter:

    • SOCKET h_Client - Identifikator der aktuellen Verbindung.

    • char ps8_ReadBuf[s32_ReadBufLen] - char Typ Array zum Empfang des Datenblocks.

    • int s32_ReadBufLen - Größe des empfangenden Array.

    • int* ps32ReadLen - diese Variable hält die reale Größe des Datenblocks, der empfangen und in das ps32_ReadBuf[] Array platziert wurde. Wenn die Größe des empfangenden Array nicht ausreichend für den Empfang des Datenblocks ist, wird diese Variable die Größe halten, die für den Empfang des Datenblocks erforderlich ist, mit einem Minus-Zeichen. Der Block bleibt in dem Stapel und der Rückgabecode wird gleich Null sein. Wenn es in de Client-Stapel keine Daten mit dem angegebenen Identifikator gibt, wird diese Variable gleich Null sein und der Rückgabecode wird ebenfalls gleich Null sein.

    Bei Erfolg gibt die Funktion true zurück, ansonsten gibt sie einen winsock2 API Fehlercode zurück. C++ Beispiel:

    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. Projektumsetzung

Das angehangene NetServerClient.zip Archiv enthält zwei Microsoft Visual Studio 2010 Ultimate Projekte:

Die Quellcodes sind ausführlich kommentiert. Sie können sich die Einzelheiten der Umsetzung ansehen und die Projekte an Ihre Bedürfnisse anpassen, wenn Sie dies möchten.

NetEventsProc.exe implementiert Server und Clients mit asynchronen Sockets. Um die Sockets in den asynchronen Modus umzuschalten, wird eine der möglichen Methoden des Betriebs in asynchronem Modus verwendet: anbinden der Sockets an die WSAEventSelect(h_Socket, h_Event, FD_ALL_EVENTS) Netzwerk-Ereignisse.

Wenn dieser Artikel das Interesse der Leser weckt, dann werden wir in der nächsten Version alle Einzelheiten der Umsetzung besprechen. Für den Augenblick ist das aber alles. Noch einmal sei darauf hingewiesen, dass dieses Projekt auf der grundlegenden Arbeit eines des großen Master Elmue basiert.



4. Fazit

Ich hoffe, dass dieser Artikel das Problem des Informationsaustauschs zwischen dem MetaTrader 4 Terminal und Drittanbieter-Anwendungen löst, unabhängig von ihrem Standort: ob lokal oder auf einem Remote-Computer in Zusammenhang mit dem installierten Terminal. Ich hoffe, dass dieser Artikel nicht viel Spaghetti-Code hat, und der Vorgang der Verwendung von DLL-Funktionen ziemlich einfach und klar ist. Zumindest habe ich mich bemüht es so zu machen.

Auf eine weitere wichtige Sache muss hingewiesen werden:

Fast alle Computer sind Knoten eines lokalen Netzwerks (LAN). Selbst wenn Sie nur einen Computer haben, ist es sehr wahrscheinlich, dass der LAN Knoten aus einem Computer besteht.

Die Verbindung mit dem Wide Area Network (WAN) wird über zusätzlichem Hardware-Gerät durchgeführt, das als Router, Mode oder mit einem anderen technischen Begriff bezeichnet sein kann. Der Einfachheit halber werden wir es Router nennen. Es sind diese Router, die globale IP-Adressen zuweisen.

Router lösen bestimmte Sicherheitsprobleme bei der Arbeit mit WAN, ermöglichen zusätzliche globale IP-Adressen, helfen die WAN-Verbindung aus dem lokalen Netzwerk zu organisieren. Aber gleichzeitig verfälschen sie den ursprünglichen Sinn des WAN, der die Möglichkeit der direkten Peer-to-Peer Verbindung von zwei beliebigen Computern impliziert.

Ein solcher Effekt wird durch die Tatsache verursacht, dass praktisch jeder Router die sogenannte Netword Adress Translation (NAT) durchführt. Die Netzwerkadresse wird dargestellt durch das <protocol, IP, port> Tupel. Jedes Element dieses Tupel kann durch den Router ausgetauscht werden, abhängig von dem bestimmten Router-Modell.

In diesem Fall haben Computer, die aus dem LAN auf das WAN zugreifen, nicht alle durch das reine WAN vorgesehenen Leistungen. Die überwiegende Mehrheit der Router verfügt über die OUTBOUND Funktion, die es Routern ermöglicht die Netzwerkadresse des LAN-Client zu behalten, die das globale WAN mit einigen Anforderungen an das LAN gerichtet hat.

Dank dieser Funktion, können Router alle empfangenen Informationen als Antwort auf eine Client-Anforderung an den Client senden. So kann der LAN-Client sich mit WAN-Servern verbinden. Allerdings kann das nicht immer zutreffen, da aus Gründen der Sicherheit und der Arbeitsdisziplin einige Netzwerkadressen durch die Hardware blockiert sein könnten.

Deshalb müssen Sie, um einen Server auf einem LAN-Computer zu organisieren, das sogenannte Port-Forwarding einrichten. Für das Beispiel in diesem Artikel, müssen Sie den einfachsten Fall eines LAN-Computers an die Port-Nummer 2000 weiterleiten. Sie können dies entweder selbst machen, durch Verbinden mit der Router Mini-Webseite in einem Browser, oder Sie können einen Fachmann ansprechen. Höchstwahrscheinlich ist die Mini-Webseite unter der Adresse 192.168.1.1 verfügbar.

Dies alles muss berücksichtigt werden, wenn Sie die Möglichkeit haben wollen Informationen über das WAN auszutauschen.

In dem nächsten Artikel werden wir den Peer-To-Peer (p2p) Typ der Verbindung betrachten.