İnternet üzerinden Terminaller arasında Veri Alışverişi için WinInet.dll Kullanımı

--- | 22 Aralık, 2021

MetaTrader 5, deposunda bir dizi yeni kullanıcı arayüzü öğesi kullanarak kullanıcılar için benzersiz fırsatlar sunar. Bu nedenle, daha önce kullanılamayan işlevler artık maksimum düzeyde kullanılabilir.

Bu derste şunları öğreneceğiz:

MQL5 Kod Tabanı, wininet.dll kitaplığıyla çalışan ve sunucu sayfası isteğinin bir örneğini gösteren bir script dosyası örneği içerir. Ancak bugün çok daha ileri gideceğiz ve sunucuyu bize yalnızca sayfayı vermekle kalmayacak, aynı zamanda bu verileri diğer talep eden terminallere sonraki aktarımlar için gönderip depolayacak hale getireceğiz.

Not: PHP ile yapılandırılmış bir sunucuya erişimi olmayanlar için Denwer kitini indirmelerini ve çalışma platformu olarak kullanmalarını öneririz. Ayrıca, test için yerel ana bilgisayarınızda Apache sunucusunu ve PHP'yi kullanmanızı da öneririz.

Sunucuya herhangi bir istek göndermek için kitaplığın 7 ana işlevine ihtiyacımız olacak.

InternetAttemptConnect  Bir İnternet bağlantısı bulmaya ve kurmaya çalışır
InternetOpen
WinInet kitaplığı işlevlerinin çalışması için yapıyı başlatır. Bu işlev, kitaplığın diğer işlevleri etkinleştirilmeden önce etkinleştirilmelidir.
InternetConnect Adres HTTP URL'si veya FTP tarafından belirtilen kaynağı açar. Tanımlayıcıyı açık bir bağlantıya döndürür.
HttpOpenRequest Bağlantı kurmak için HTTP istekleri için bir tanımlayıcı oluşturur
HttpSendRequest Oluşturulan tanımlayıcıyı kullanarak bir sorgu gönderir
InternetReadFile Sorgu işlendikten sonra sunucudan alınan verileri okur
InternetCloseHandle Aktarılan tanımlayıcıyı serbest bırakır

 
Tüm işlevlerin ve parametrelerinin ayrıntılı açıklaması MSDN Yardım sisteminde bulunabilir.

Unicode çağrılarının ve bağlantı yoluyla hat aktarımlarının kullanılması dışında, işlevlerin bildirimi MQL4'tekiyle aynı kaldı.

#import "wininet.dll"
int InternetAttemptConnect(int x);
int InternetOpenW(string &sAgent,int lAccessType,string &sProxyName,string &sProxyBypass,int lFlags);
int InternetConnectW(int hInternet,string &szServerName,int nServerPort,string &lpszUsername,string &lpszPassword,int dwService,int dwFlags,int dwContext);
int HttpOpenRequestW(int hConnect,string &Verb,string &ObjectName,string &Version,string &Referer,string &AcceptTypes,uint dwFlags,int dwContext);
int HttpSendRequestW(int hRequest,string &lpszHeaders,int dwHeadersLength,uchar &lpOptional[],int dwOptionalLength);
int HttpQueryInfoW(int hRequest,int dwInfoLevel,int &lpvBuffer[],int &lpdwBufferLength,int &lpdwIndex);
int InternetReadFile(int hFile,uchar &sBuffer[],int lNumBytesToRead,int &lNumberOfBytesRead);
int InternetCloseHandle(int hInet);
#import

//To make it clear, we will use the constant names from wininet.h.
#define OPEN_TYPE_PRECONFIG     0           // use the configuration by default
#define FLAG_KEEP_CONNECTION    0x00400000  // do not terminate the connection
#define FLAG_PRAGMA_NOCACHE     0x00000100  // no cashing of the page
#define FLAG_RELOAD             0x80000000  // receive the page from the server when accessing it
#define SERVICE_HTTP            3           // the required protocol

Her işlev için bayrakların ayrıntılı bir açıklaması, aynı bölümde MSDN'de bulunabilir. Diğer sabitlerin ve işlevlerin bildirimini görmek istiyorsanız, makalenin eklerinde bulunan orijinal wininet.h dosyasını indirebilirsiniz.

1. İnternet oturumu oluşturma ve silme kılavuzları

Yapmamız gereken ilk şey bir oturum oluşturmak ve ana bilgisayara bir bağlantı açmaktır. Bir oturum, program başlatma sırasında tercihen yalnızca bir kez oluşturulmalıdır (örneğin, OnInit işlevinde). Veya Expert Advisor'ı başlatma işleminin en başında yapılabilir, ancak başarılı bir şekilde oluşturulmasının oturumun kapanmasından önce yalnızca bir kez yapıldığından emin olmak önemlidir. Ve OnStart veya OnTimer uygulamasının her yeni yinelemesinde tekrar tekrar ve ihtiyaç olmadan çağrılmamalıdır. Sık çağrılardan kaçınmak ve her çağrı için gerekli yapıların oluşturulması önemlidir.

Bu nedenle, oturum ve bağlantı tanımlayıcılarını tanımlamak için yalnızca bir genel sınıf örneği kullanacağız.

   string            Host;       // host name
   int               Port;       // port
   int               Session;    // session descriptor
   int               Connect;    // connection descriptor

bool MqlNet::Open(string aHost,int aPort)
  {
   if(aHost=="")
     {
      Print("-Host is not specified");
      return(false);
     }
   // checking the DLL resolution in the terminal  
   if(!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED))
     {
      Print("-DLL is not allowed");
      return(false);
     }
   // if the session was identifies, then we close
   if(Session>0 || Connect>0) Close();
   // record of attempting to open into the journal
   Print("+Open Inet...");
   // if we were not able to check for the presence of an Internet connection, then we exit
   if(InternetAttemptConnect(0)!=0)
     {
      Print("-Err AttemptConnect");
      return(false);
     }
   string UserAgent="Mozilla"; string nill="";
   // open a session
   Session=InternetOpenW(UserAgent,OPEN_TYPE_PRECONFIG,nill,nill,0);
   // if we were not able to open a session, then exit
   if(Session<=0)
     {
      Print("-Err create Session");
      Close();
      return(false);
     }
   Connect=InternetConnectW(Session,aHost,aPort,nill,nill,SERVICE_HTTP,0,0);
   if(Connect<=0)
     {
      Print("-Err create Connect");
      Close();
      return(false);
     }
   Host=aHost; Port=aPort;
   // otherwise all attempts were successful
   return(true);
  }

Başlatma işleminden sonra Oturum ve Bağlan tanımlayıcıları aşağıdaki işlevlerin tamamında kullanılabilir. Tüm çalışmalar tamamlandıktan ve MQL-programları kaldırıldıktan sonra, bunların kaldırılması gerekir. Bu, InternetCloseHandle işlevi kullanılarak yapılır.

void MqlNet::CloseInet()
  {
   Print("-Close Inet...");
   if(Session>0) InternetCloseHandle(Session); Session=-1;
   if(Connect>0) InternetCloseHandle(Connect); Connect=-1;
  }

Dikkat! İnternet işlevleriyle çalışırken, bunlardan türetilen tüm tanımlayıcıların, InternetCloseHandle işlevi kullanılarak boşaltılması gerekir.

2. Sunucuya istek gönderme ve sayfayı alma

Bir istek göndermek ve bu isteğe yanıt olarak bir sayfa almak için, kalan üç işleve ihtiyacımız olacak: HttpOpenRequest, HttpSendRequest и InternetReadFile. Bir isteğe yanıt olarak sayfanın alınmasının özü, temel olarak içeriğini yerel bir dosyaya kaydetme işlemidir.


İstekler ve içeriklerle çalışmanın rahat olması için iki evrensel işlev oluşturacağız.

Bir istek gönderme:

bool MqlNet::Request(string Verb,string Object,string &Out,bool toFile=false,string addData="",bool fromFile=false)
  {
   if(toFile && Out=="")
     {
      Print("-File is not specified ");
      return(false);
     }
   uchar data[];
   int hRequest,hSend,h;
   string Vers="HTTP/1.1";
   string nill="";
   if(fromFile)
     {
      if(FileToArray(addData,data)<0)
        {
         Print("-Err reading file "+addData);

         return(false);
        }
     } // read file in the array
   else StringToCharArray(addData,data);

   if(Session<=0 || Connect<=0)
     {
      Close();
      if(!Open(Host,Port))
        {
         Print("-Err Connect");
         Close();
         return(false);
        }
     }
   // create a request descriptor
   hRequest=HttpOpenRequestW(Connect,Verb,Object,Vers,nill,nill,FLAG_KEEP_CONNECTION|FLAG_RELOAD|FLAG_PRAGMA_NOCACHE,0);
   if(hRequest<=0)
     {
      Print("-Err OpenRequest");
      InternetCloseHandle(Connect);
      return(false);
     }
   // send request
   // headline for request
   string head="Content-Type: application/x-www-form-urlencoded";
   // sent file
   hSend=HttpSendRequestW(hRequest,head,StringLen(head),data,ArraySize(data)-1);
   if(hSend<=0)
     {
      Print("-Err SendRequest");
      InternetCloseHandle(hRequest);
      Close();
     }
   // read the page 
   ReadPage(hRequest,Out,toFile);
   // close all handles
   InternetCloseHandle(hRequest); 
   InternetCloseHandle(hSend);
   return(true);
  }

MqlNet:'in işlev parametreleri: İstek:

Alınan tanımlayıcının içeriğini okuma

void MqlNet::ReadPage(int hRequest,string &Out,bool toFile)
  {
   // read the page 
   uchar ch[100];
   string toStr="";
   int dwBytes,h;
   while(InternetReadFile(hRequest,ch,100,dwBytes))
     {
      if(dwBytes<=0) break;
      toStr=toStr+CharArrayToString(ch,0,dwBytes);
     }
   if(toFile)
     {
      h=FileOpen(Out,FILE_BIN|FILE_WRITE);
      FileWriteString(h,toStr);
      FileClose(h);
     }
   else Out=toStr;
  }

MqlNet:'in işlev parametreleri: ReadPage:

Ve bunların hepsini bir araya getirerek, İnternet ile çalışmak için bir MqlNet kitaplık sınıfı elde edeceğiz.

class MqlNet
  {
   string            Host;     // host name
   int               Port;     // port
   int               Session; // session descriptor
   int               Connect; // connection descriptor
public:
                     MqlNet(); // class constructor
                    ~MqlNet(); // destructor
   bool              Open(string aHost,int aPort); // create a session and open a connection
   void              Close(); // close session and connection
   bool              Request(string Verb,string Request,string &Out,bool toFile=false,string addData="",bool fromFile=false); // send request
   bool              OpenURL(string URL,string &Out,bool toFile); // somply read the page into the file or the variable
   void              ReadPage(int hRequest,string &Out,bool toFile); // read the page
   int               FileToArray(string FileName,uchar &data[]); // copy the file into the array for sending
  };

Temel olarak, İnternet ile çalışmak için çeşitli ihtiyaçları karşılaması muhtemel olan gerekli işlevlerin tümü budur. Kullanım örneklerini göz önünde bulundurun.

Örnek 1. MQL-programlarının terminalin klasörlerine otomatik olarak indirilmesi. MetaGrabber script dosyası

Sınıfın çalışmasını test etmeye başlamak için, en kolay görevlerle başlayalım - Sayfayı okumak ve içeriğini belirtilen klasöre kaydetmek. Ancak sayfaların basit bir okumasının çok ilginç olması muhtemel değildir; bu nedenle script dosyasının çalışmasından bir şeyler elde etmek için, ona sitelerden mql programlarının kavrama işlevini atayalım. MetaGrabber script dosyasının görevi şu şekilde olacaktır:

İkinci sorunu çözmek için MqlNet sınıfını kullanırız. Üçünicü görev için Kernel32.dll'den MoveFileEx işlevini kullanırız.

#import "Kernel32.dll"
bool MoveFileExW(string &lpExistingFileName, string &lpNewFileName, int dwFlags);
#import "Kernel32.dll"

İlk sorun için, URL satırını ayrıştırmak için küçük bir hizmet işlevi yapalım.

Adresten üç satır ayırmamız gerekir: Ana bilgisayar, sitenin yolu ve dosya adı.
Örneğin, http://www.mysite.com/folder/page.html satırında

- Ana Bilgisayar = www.mysite.com
- İstek = / klasör / page.html
- Dosya adı = page.html

MQL5 sitesinde Kod Tabanı olması durumunda, yol yolları aynı yapıyı içerir. Örneğin, https://www.mql5.com/ru/code/79 sayfasındaki ErrorDescription.mq5 kitaplığının yolu http://p.mql5.com/data/18/79/ErrorDescription.mqh gibi görünür. Bu yol, bağlantıya sağ tıklayıp "Bağlantıyı Kopyala" seçilerek kolayca elde edilir. Böylece, URL, biri istek için diğeri dosya depolama kolaylığı için dosya adı için olmak üzere iki bölüme ayrılır.

- Ana Bilgisayar = p.mql5.com
- İstek = / data/18/79/5/ErrorDescription.mqh
- Dosya adı = ErrorDescription.mqh

Bu, aşağıdaki ParseURL işlevinin ilgileneceği satır ayrıştırma türüdür.

void ParseURL(string path,string &host,string &request,string &filename)
  {
   host=StringSubstr(URL,7);
   // removed
   int i=StringFind(host,"/"); 
   request=StringSubstr(host,i);
   host=StringSubstr(host,0,i);
   string file="";
   for(i=StringLen(URL)-1; i>=0; i--)
      if(StringSubstr(URL,i,1)=="/")
        {
         file=StringSubstr(URL,i+1);
         break;
        }
   if(file!="") filename=file;
  }

Script dosyasının dış parametrelerinde yalnızca iki parametre yapacağız - URL (mql5 dosyasının yolu) ve sonraki yerleştirme klasörünün türü - yani, onu yerleştirmek isteyeceğiniz terminal klasörü.

Sonuç olarak, kısa ama çok kullanışlı bir script dosyası elde ederiz.

//+------------------------------------------------------------------+
//|                                                  MetaGrabber.mq5 |
//|                                 Copyright © 2010 www.fxmaster.de |
//|                                         Coding by Sergeev Alexey |
//+------------------------------------------------------------------+
#property copyright "www.fxmaster.de  © 2010"
#property link      "www.fxmaster.de"
#property version               "1.00"
#property description  "Download files from internet"

#property script_show_inputs

#include <InternetLib.mqh>

#import "Kernel32.dll"
bool MoveFileExW(string &lpExistingFileName,string &lpNewFileName,int dwFlags);
#import
#define MOVEFILE_REPLACE_EXISTING 0x1

enum _FolderType
  {
   Experts=0,
   Indicators=1,
   Scripts=2,
   Include=3,
   Libraries=4,
   Files=5,
   Templates=6,
   TesterSet=7
  };

input string URL="";
input _FolderType FolderType=0;
//------------------------------------------------------------------ OnStart
int OnStart()
  {
   MqlNet INet; // variable for working in the Internet
   string Host,Request,FileName="Recieve_"+TimeToString(TimeCurrent())+".mq5";

   // parse url
   ParseURL(URL,Host,Request,FileName);

   // open session
   if(!INet.Open(Host,80)) return(0);
   Print("+Copy "+FileName+" from  http://"+Host+" to "+GetFolder(FolderType));

   // obtained file
   if(!INet.Request("GET",Request,FileName,true))
     {
      Print("-Err download "+URL);
      return(0);
     }
   Print("+Ok download "+FileName);

   // move to the target folder
   string to,from,dir;
   // if there is no need to move it elsewhere
   if(FolderType==Files) return(0);

   // from
   from=TerminalInfoString(TERMINAL_DATA_PATH)+"\\MQL5\\Files\\"+FileName;

   // to
   to=TerminalInfoString(TERMINAL_DATA_PATH)+"\\";
   if(FolderType!=Templates && FolderType!=TesterSet) to+="MQL5\\";
   to+=GetFolder(FolderType)+"\\"+FileName;

   // move file 
   if(!MoveFileExW(from,to,MOVEFILE_REPLACE_EXISTING))
     {
      Print("-Err move to "+to);
      return(0);
     }
   Print("+Ok move "+FileName+" to "+GetFolder(FolderType));

   return(0);
  }
//------------------------------------------------------------------ GetFolder
string GetFolder(_FolderType foldertype)
  {
   if(foldertype==Experts) return("Experts");
   if(foldertype==Indicators) return("Indicators");
   if(foldertype==Scripts) return("Scripts");
   if(foldertype==Include) return("Include");
   if(foldertype==Libraries) return("Libraries");
   if(foldertype==Files) return("Files");
   if(foldertype==Templates) return("Profiles\\Templates");
   if(foldertype==TesterSet) return("Tester");
   return("");
  }
//------------------------------------------------------------------ ParseURL
void ParseURL(string path,string &host,string &request,string &filename)
  {
   host=StringSubstr(URL,7);
   // removed
   int i=StringFind(host,"/"); 
   request=StringSubstr(host,i);
   host=StringSubstr(host,0,i);
   string file="";
   for(i=StringLen(URL)-1; i>=0; i--)
      if(StringSubstr(URL,i,1)=="/")
        {
         file=StringSubstr(URL,i+1);
         break;
        }
   if(file!="") filename=file;
  }
//+------------------------------------------------------------------+


Denemeleri en sevdiğimiz bölüm üzerinde yapalım https://www.mql5.com/tr/code. İndirilen dosyalar düzenleyici gezgininde hemen görünür ve bunlar, terminal veya düzenleyici yeniden başlatılmadan derlenebilir. Ve dosyaları taşımak için istenen klasörü ararken dosya sisteminin uzun yollarında gezinmeyeceklerdir.

Dikkat! Birçok site, toplu içerik indirmeye karşı güvenlik sağlar ve bu tür bir toplu indirme durumunda IP adresiniz bu kaynak tarafından engellenebilir. Bu nedenle, sık eriştiğiniz ve erişiminizin yasaklanmasını istemediğiniz kaynaklardan dosya indirirken "makine" kullanımına çok dikkat edin.

Daha da ileri gitmek, yani önerilen hizmeti geliştirmek isteyenler, pano içeriğinin ele geçirilmesi ve daha fazla otomatik indirme ile Pano script dosyasını kullanabilir.

Örnek 2. Tek bir grafikte birden çok aracıdan gelen fiyat tekliflerini izleme

Böylece, İnternetten dosya almayı öğrendik. Şimdi daha ilginç soruyu, yani bu verilerin sunucuda nasıl gönderileceği ve saklanacağı konusunu ele alalım. Bunun için sunucuda bulunacak küçük bir ek PHP-script dosyasına ihtiyacımız var. Yazılı MqlNet sınıfını kullanarak, izleme için bir Expert Advisor oluşturuyoruz - MetaArbitrage. PHP script dosyası ile bağlantılı olarak expert'in görevi şu şekilde olacaktır:

MQL-modülü ile PHP script dosyası arasındaki etkileşimin şematik diyagramı aşağıdaki gibidir:


Bu görevleri çözmek için MqlNet sınıfını kullanacağız.

Verilerin tekrarlanmasını ve aynı zamanda eski fiyat tekliflerinin ayıklanmasını önlemek için 4 ana parametre göndereceğiz: Aracının sunucusunun adı (geçerli fiyatların kaynağı), para birimi, UTC cinsinden fiyatların ve fiyat tekliflerinin zamanı. Örneğin, şirketimizin kaynaklarından script dosyasına erişim isteği şu şekildedir:

www.fxmaster.de/metaarbitr.php?server=Metaquotes&pair=EURUSD&bid=1.4512&time=13286794

Bu parametreler ve gerçek fiyat teklifi sunucuda saklanır ve bu para biriminin diğer tüm saklanan fiyat teklifleriyle birlikte yanıt sayfasında yayınlanacaktır.

Bu alışverişin "teminat" kolaylığı, fiyat tekliflerinin MT4'ten olduğu kadar MT5'ten de gönderilebilmesidir!

Sunucu tarafından oluşturulan sayfa, normal bir CSV dosyasıdır. Bu, bu script dosyasında şu şekilde görünür:

ServerName1; Bid1; Time1
ServerName 2; Bid2; Time2
ServerName 3; Bid3; Time3

ServerName N; BidN; TimeN

Ancak buna kendi ek parametrelerinizi ekleyebilirsiniz (örneğin, sunucu türü - demo veya gerçek). Bu CSV-dosyasını saklarız ve ekranda görüntülenen bir değer ve satır fiyatları tablosunun çıktısıyla satır satır ayrıştırırız.

Bu dosyanın işlenmesi, her bir özel durumda gerekli olan bir kez seçilerek birçok farklı şekilde gerçekleştirilebilir. Örneğin, MetaTrader 4 demo sunucusundan vb. alınan fiyat tekliflerini filtreleyin.


İnternet sunucusunu kullanmanın avantajları açıktır - Başka bir yatırımcı tarafından alınabilecek ve görüntülenebilecek fiyat tekliflerinizi gönderirsiniz. Aynı şekilde, diğer yatırımcılara gönderilen fiyat tekliflerini alırsınız. Yani, terminaller arasındaki etkileşim iki taraflıdır; veri alışverişi aşağıdaki şemada gösterildiği gibi gerçekleştirilir:


Bu şema, herhangi bir sayıda terminal arasında bilgi alışverişi prensibinin temelini oluşturur. Tam bir MetaArbitrage Expert Advisor'ı ve açıklamalar içeren PHP-script dosyası eklerdeki bağlantıdan indirilebilir. PHP tarafından kullanılan işlevler hakkında daha fazla bilgiyi aşağıdaki sitede bulabilirsiniz: php.su

Örnek 3. Terminal içinde mesaj alışverişi (mini sohbet). MetaChat Expert Advisor

Alım satımdan ve rakamlardan bir adım uzaklaşalım ve terminalden çıkmadan aynı anda birkaç kişiyle sohbet etmemizi sağlayacak bir uygulama oluşturalım. Bunu yapmak için, genel olarak bir öncekine çok benzeyen bir PHP script dosyasına daha ihtiyacımız olacak. Bu script dosyasında, zaman fiyat tekliflerini analiz etmek yerine, bir dosyadaki satır sayısını analiz edeceğiz. Expert Advisor'ın görevi şunlar olacak:

MetaChat'in çalışması, önceki Expert Advisor'ın çalışmasından farklı olmayacaktır. Aynı prensip ve çıktı için aynı basit CSV dosyası.


MetaChat ve MetaArbitrage, geliştiricilerinin sitesinde tutulur. Çalışmaları için PHP-script dosyaları da orada bulunur.
Bu nedenle bir çalışmayı test etmek veya bu hizmeti kullanmak isterseniz aşağıdaki bağlantıdan ulaşabilirsiniz:
MetaСhat - www.fxmaster.de/metachat.php
MetaArbitrage - www.fxmaster.de/metaarbitr.php

Sonuç

Böylece HTTP-istekleri hakkında bilgi edinmiş olduk. İnternet üzerinden veri gönderip alabilme ve çalışma sürecini daha rahat organize edebilme becerisi kazandık. Ancak, özellikler her zaman geliştirilebilir. Aşağıdakiler, iyileştirmelerinin yeni potansiyel yönleri olarak düşünülebilir:

Bu makalede GET türü istekleri kullandık. Bunlar, sunucu analizleri için az sayıda parametre ile bir dosya almanız veya bir istek göndermeniz gerektiğinde görevi yeterince yerine getirirler.

Bir sonraki dersimizde, sunucuya dosya gönderme veya terminaller arasında dosya paylaşımı gibi POST isteklerini dikkatlice inceleyecek ve kullanım örneklerini ele alacağız.

Yararlı Kaynaklar