
DLL'leri Kullanmadan Adlandırılmış Kanalları Kullanarak MetaTrader 5 ile İletişim Kurma
Giriş
Birçok geliştirici aynı sorunla karşı karşıyadır: Güvenli olmayan DLL'ler kullanmadan alım satım terminali korumalı alanına nasıl ulaşılır?
En kolay ve en güvenli yöntemlerden biri, normal dosya işlemleri gibi çalışan standart Adlandırılmış Kanalları kullanmaktır. Bunlar, programlar arasında işlemciler arası istemci-sunucu iletişimini düzenlemenize olanak tanır. Bu konuda, yani DLL'lere erişimin mümkün olduğunu gösteren halihazırda yayınlanmış bir makale olmasına rağmen Adlandırılmış Kanalları kullanarak MetaTrader 5 terminalleri arasında iletişim kurmak için DLL içermeyen bir çözüm, istemci terminalinin standart ve güvenli özelliklerini kullanacağız.
Adlandırılmış kanallar hakkında daha fazla bilgiyi MSDN kitaplığında bulabilirsiniz, ancak biz pratik C++ ve MQL5 örneklerine geçeceğiz. Aralarında sunucu, istemci, veri alışverişi uygulayacağız ve ardından performansı kıyaslayacağız.
Sunucu Uygulaması
Basit bir sunucuyu C++ ile kodlayalım. Terminalden bir script dosyası bu sunucuya bağlanacak ve onunla veri alışverişi yapacaktır. Sunucu çekirdeği aşağıdaki WinAPI işlevlerine sahiptir:
- CreateNamedPipe - Adlandırılmış bir kanal oluşturur.
- ConnectNamedPipe - Sunucunun istemci bağlantılarını beklemesini sağlar.
- WriteFile - Kanala veri yazar.
- ReadFile - Kanaldan veri okur.
- FlushFileBuffers - Birikmiş arabellekleri temizler.
- DisconnectNamedPipe - Sunucunun bağlantısını keser.
- CloseHandle - Tanıtıcıyı kapatır.
Adlandırılmış bir kanal açıldığında, normal okuma/yazma dosya işlemleri için kullanılabilecek bir dosya tanıtıcısını döndürür. Sonuç olarak, ağ işlemlerinde herhangi bir özel bilgi gerektirmeyen çok basit bir mekanizma elde edersiniz.
Adlandırılmış kanalların bir ayırt edici özelliği vardır; bunlar hem yerel hem de ağ olabilir. Yani, istemci terminallerinden ağ bağlantılarını kabul edecek bir uzak sunucu uygulamak kolaydır.
Bayt değişim modunda çalışan tam çift yönlü kanal olarak yerel bir sunucu oluşturmaya ilişkin basit bir örnek şu şekildedir:
//--- open CPipeManager manager; if(!manager.Create(L"\\\\.\\pipe\\MQL5.Pipe.Server")) return(-1); //+------------------------------------------------------------------+ //| Create named pipe | //+------------------------------------------------------------------+ bool CPipeManager::Create(LPCWSTR pipename) { //--- check parameters if(!pipename || *pipename==0) return(false); //--- close old Close(); //--- create named pipe m_handle=CreateNamedPipe(pipename,PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES,256*1024,256*1024,1000,NULL); if(m_handle==INVALID_HANDLE_VALUE) { wprintf(L"Creating pipe '%s' failed\n",pipename); return(false); } //--- ok wprintf(L"Pipe '%s' created\n",pipename); return(true); }
İstemci bağlantısı almak için ConnectNamedPipe işlevini kullanmanız gerekir:
//+------------------------------------------------------------------+ //| Connect client | //+------------------------------------------------------------------+ bool CPipeManager::ConnectClient(void) { //--- pipe exists? if(m_handle==INVALID_HANDLE_VALUE) return(false); //--- connected? if(!m_connected) { //--- connect if(ConnectNamedPipe(m_handle,NULL)==0) { //--- client already connected before ConnectNamedPipe? if(GetLastError()!=ERROR_PIPE_CONNECTED) return(false); //--- ok } m_connected=true; } //--- return(true); }
Veri alışverişi, 4 basit işlev kullanılarak düzenlenir:
- CPipeManager::Send(void *data,size_t data_size)
- CPipeManager::Read(void *data,size_t data_size)
- CPipeManager::SendString(LPCSTR command)
- CPipeManager::ReadString(LPSTR answer,size_t answer_maxlen)
MQL5 uyumlu modda verileri ikili veri veya ANSI metin dizeleri olarak göndermenize/almanıza olanak tanır. Ayrıca, MQL5'teki CFilePipe bir dosyayı varsayılan olarak ANSI modunda açtığı için, alınıp gönderilirken dizeler otomatik olarak Unicode'a dönüştürülür. MQL5 programınız Unicode modunda (FILE_UNICODE) bir dosya açarsa, Unicode dizelerini değiştirebilir (BOM başlangıç imzasıyla).
İstemci Uygulaması
İstemcimizi MQL5'te yazacağız. Bu, Standart Kitaplık'tan CFilePipe sınıfını kullanarak normal dosya işlemlerini gerçekleştirebilecektir. Bu sınıf, CFileBin ile neredeyse aynıdır, ancak bu verileri okumadan önce bir sanal dosyada veri kullanılabilirliğinin önemli bir doğrulamasını içerir.
//+------------------------------------------------------------------+ //| Wait for incoming data | //+------------------------------------------------------------------+ bool CFilePipe::WaitForRead(const ulong size) { //--- check handle and stop flag while(m_handle!=INVALID_HANDLE && !IsStopped()) { //--- enough data? if(FileSize(m_handle)>=size) return(true); //--- wait a little Sleep(1); } //--- failure return(false); } //+------------------------------------------------------------------+ //| Read an array of variables of double type | //+------------------------------------------------------------------+ uint CFilePipe::ReadDoubleArray(double &array[],const int start_item,const int items_count) { //--- calculate size uint size=ArraySize(array); if(items_count!=WHOLE_ARRAY) size=items_count; //--- check for data if(WaitForRead(size*sizeof(double))) return FileReadArray(m_handle,array,start_item,items_count); //--- failure return(0); }
Adlandırılmış kanallar, yerel ve ağ modlarının uygulanmasında önemli farklılıklara sahiptir. Böyle bir doğrulama olmadan, büyük miktarda veri (64K üzeri) gönderirken ağ modu işlemleri her zaman bir okuma hatası döndürecektir.
Sunucuya iki kontrol ile bağlanalım: Ya 'RemoteServerName' adlı uzak bilgisayara ya da yerel makineye.
void OnStart() { //--- wait for pipe server while(!IsStopped()) { if(ExtPipe.Open("\\\\RemoteServerName\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break; if(ExtPipe.Open("\\\\.\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break; Sleep(250); } Print("Client: pipe opened");
Veri Alışverişi
Başarılı bir bağlantıdan sonra, sunucuya tanımlama bilgilerini içeren bir metin dizesi gönderelim. Dosya ANSI modunda açıldığı için, Unicode dizesi otomatik olarak ANSI'ye dönüştürülecektir.
//--- send welcome message if(!ExtPipe.WriteString(__FILE__+" on MQL5 build "+IntegerToString(__MQ5BUILD__))) { Print("Client: sending welcome message failed"); return; }
Yanıt olarak, sunucu "Kanal sunucusundan merhaba" dizesini ve 1234567890 tamsayısını gönderecektir. İstemci bir kez daha "Test dizesi" dizesini ve 1234567890 tamsayısını gönderecektir.
//--- read data from server string str; int value=0; if(!ExtPipe.ReadString(str)) { Print("Client: reading string failed"); return; } Print("Server: ",str," received"); if(!ExtPipe.ReadInteger(value)) { Print("Client: reading integer failed"); return; } Print("Server: ",value," received"); //--- send data to server if(!ExtPipe.WriteString("Test string")) { Print("Client: sending string failed"); return; } if(!ExtPipe.WriteInteger(value)) { Print("Client: sending integer failed"); return; }
Evet, basit veri alışverişini bitirdik. Şimdi performans karşılaştırması yapma zamanı.
Performans Karşılaştırması
Test etmek için, sunucudan istemciye 8 megabaytlık bloklar halinde çift tür sayıların bir dizisi olarak 1 gigabayt veri göndereceğiz, ardından blokların doğruluğunu kontrol edecek ve aktarım hızını ölçeceğiz.
C++ sunucusundaki bu kod şu şekildedir:
//--- benchmark double volume=0.0; double *buffer=new double[1024*1024]; // 8 Mb wprintf(L"Server: start benchmark\n"); if(buffer) { //--- fill the buffer for(size_t j=0;j<1024*1024;j++) buffer[j]=j; //--- send 8 Mb * 128 = 1024 Mb to client DWORD ticks=GetTickCount(); for(size_t i=0;i<128;i++) { //--- setup guard signatures buffer[0]=i; buffer[1024*1024-1]=i+1024*1024-1; //--- if(!manager.Send(buffer,sizeof(double)*1024*1024)) { wprintf(L"Server: benchmark failed, %d\n",GetLastError()); break; } volume+=sizeof(double)*1024*1024; wprintf(L"."); } wprintf(L"\n"); //--- read confirmation if(!manager.Read(&value,sizeof(value)) || value!=12345) wprintf(L"Server: benchmark confirmation failed\n"); //--- show statistics ticks=GetTickCount()-ticks; if(ticks>0) wprintf(L"Server: %.0lf Mb sent at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks); //--- delete[] buffer; }
ve MQL5 istemcisindeki:
//--- benchmark double buffer[]; double volume=0.0; if(ArrayResize(buffer,1024*1024,0)==1024*1024) { uint ticks=GetTickCount(); //--- read 8 Mb * 128 = 1024 Mb from server for(int i=0;i<128;i++) { uint items=ExtPipe.ReadDoubleArray(buffer); if(items!=1024*1024) { Print("Client: benchmark failed after ",volume/1024," Kb, ",items," items received"); break; } //--- check the data if(buffer[0]!=i || buffer[1024*1024-1]!=i+1024*1024-1) { Print("Client: benchmark invalid content"); break; } //--- volume+=sizeof(double)*1024*1024; } //--- send confirmation value=12345; if(!ExtPipe.WriteInteger(value)) Print("Client: benchmark confirmation failed "); //--- show statistics ticks=GetTickCount()-ticks; if(ticks>0) printf("Client: %.0lf Mb received at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks); //--- ArrayFree(buffer); }
Aktarım sırasında herhangi bir hata olmadığından emin olmak için aktarılan blokların ilk ve son öğelerinin kontrol edildiğini unutmayın. Ayrıca, aktarım tamamlandığında, istemci sunucuya başarılı veri alımı hakkında bir onay sinyali gönderir. Son onayları kullanmazsanız, taraflardan biri bağlantıyı çok erken kapatırsa kolayca veri kaybıyla karşılaşırsınız.
PipeServer.exe sunucusunu yerel olarak çalıştırın ve PipeClient.mq5 script dosyasını herhangi bir grafiğe ekleyin:
PipeServer.exe | PipeClient.mq5 |
---|---|
MQL5 Pipe Server Copyright 2012, MetaQuotes Software Corp. Pipe '\\.\pipe\MQL5.Pipe.Server' created Client: waiting for connection... Client: connected as 'PipeClient.mq5 on MQL5 build 705' Server: send string Server: send integer Server: read string Server: 'Test string' received Server: read integer Server: 1234567890 received Server: start benchmark ...................................................... ........ Server: 1024 Mb sent at 2921 Mb per second | PipeClient (EURUSD,H1) Client: pipe opened PipeClient (EURUSD,H1) Server: Hello from pipe server received PipeClient (EURUSD,H1) Server: 1234567890 received PipeClient (EURUSD,H1) Client: 1024 Mb received at 2921 Mb per second |
Yerel alışveriş için aktarım hızı gerçekten şaşırtıcı; saniyede neredeyse 3 gigabayt. Bu, hemen hemen her miktarda veriyi MQL5 programlarına aktarmak için adlandırılmış kanalların kullanılabileceği anlamına gelir.
Şimdi sıradan bir 1 gigabit LAN'da veri aktarım performansını karşılaştıralım:
PipeServer.exe | PipeClient.mq5 |
---|---|
MQL5 Pipe Server Copyright 2012, MetaQuotes Software Corp. Pipe '\\.\pipe\MQL5.Pipe.Server' created Client: waiting for connection... Client: connected as 'PipeClient.mq5 on MQL5 build 705' Server: send string Server: send integer Server: read string Server: 'Test string' received Server: read integer Server: 1234567890 received Server: start benchmark ...................................................... ........ Server: 1024 Mb sent at 63 Mb per second | PipeClient (EURUSD,H1) Client: pipe opened PipeClient (EURUSD,H1) Server: Hello from pipe server received PipeClient (EURUSD,H1) Server: 1234567890 received PipeClient (EURUSD,H1) Client: 1024 Mb received at 63 Mb per second |
Yerel ağda saniyede 63 megabayt hızında 1 gigabayt veri aktarıldı; bu çok iyi. Aslında, bu, gigabit ağının maksimum bant genişliğinin %63'üdür.
Sonuç
MetaTrader 5 alım satım platformunun koruma sistemi, MQL5 programlarının korumalı alanlarının dışında çalışmasına izin vermez ve yatırımcıları güvenilmeyen Expert Advisor'ları kullanırken tehditlere karşı korur. Adlandırılmış kanalları kullanarak, üçüncü taraf yazılımlarla kolayca entegrasyonlar oluşturabilir ve EA'ları dışarıdan yönetebilirsiniz. Güvenli bir şekilde.
MetaQuotes Ltd tarafından Rusçadan çevrilmiştir.
Orijinal makale: https://www.mql5.com/ru/articles/503






- Ücretsiz alım-satım uygulamaları
- İşlem kopyalama için 8.000'den fazla sinyal
- Finansal piyasaları keşfetmek için ekonomik haberler
Gizlilik ve Veri Koruma Politikasını ve MQL5.com Kullanım Şartlarını kabul edersiniz