Обсуждение статьи "Связь с MetaTrader 5 через именованные каналы без применения DLL" - страница 3

 
Renat:
Я думаю, что вопрос о связи между терминалами имеет малую долю применения.

А вот связь с внешними системами более важна и применима. Именно для этого и открывался безопасный канал. 

C этим никто не спорит, я имею в виду, декларацию на предмет: для чего все это затевалось. Но трабла в том, как было реализовано. Ведь внешние системы не обязательно написаны на С, а могут быть созданы на различных языках программирования. А законнектиться к терминалу через именованный канал на многих языках программирования можно только в качестве клиента через файловые операции. Но технология ведь была выбрана клиент-сервер, т.е. такая, что два клиента друг с другом не встретятся, если между ними пропасть без моста - шлюза. Т.е. либо надо было предусмотреть технологию клиент-клиент, либо шлюз. А так получилось все как в басне дедушки Крылова "Лиса и виноград":

Хоть видит око,

Да зуб неймет.

© И. Крылов


Надо было посмотреть, на предмет, как у других аналогичная связь связь с внешними системами реализована. Например: VMWare, MS SQL Server, MySQL, внешние модемы и проч. У них серверная часть реализована внутри. И присоединяться очень удобно без всяких костылей, хоть через именованный канал, хоть по TCP/IP и проч. каналы связи. А можно даже и на выбор, например, через именованный канал, но удаленно по TCP/IP c помощью утилиты "Named Pipe TCP Proxy". Т.е. пользователям не надо создавать никаких дополнительных костылей, а только выбрать наиболее подходящее клиентское приложение и сразу же подключиться и работать.
 
Юрий, Вы явно путаете. Серверные пайпы легко создаются на других языках. Это же просто WinAPI.

Учитывайте, что MQL5 - это безопасная прикладная среда, причем явно клиентская, в которой неразумно делать серверные функции.

Не уверен, что декларировалась серверная функциональность. Клиентский функционал был запланирован и реализован.
 

Вроде бы сделал код для шлюза. Пока еще не проверял. Загружу и установлю MinGW, а там видно будет, что не так.

#using <System.Core.dll>

using namespace System;
using namespace System::IO;
using namespace System::IO::Pipes;
using namespace System::Text;
using namespace System::Threading;


public ref class PipeServer
{
private:
       NamedPipeServerStream^ pipeServer1;
       NamedPipeServerStream^ pipeServer2;
    

public:
    static void Main()
    {
        int i;
        Thread server;

        Console::WriteLine("\n*** Named pipe server stream with impersonation example ***\n");
        Console::WriteLine("Waiting for client connect...\n");
        // Создаем первый подпроцесс для копирования информации из именованных каналов
        Thread server = gcnew Thread(gcnew ThreadStart(&ServerThread));
        // Запускаем его
        server->Start();
        Thread::Sleep(250);
        // Если клиент отвалился
        if (servers != nullptr)
        {
          if (server->Join(250))
                                        {
            Console::WriteLine("Server finished.", server->ManagedThreadId);
            servers = nullptr;
          }
        }
        Console::WriteLine("\nServer threads exhausted, exiting.");
    }

private:
    static void ServerThread()
    {
        int threadId;
        bool needclose = false;
        if (i == 0) 
        {
            // Создаем два именованных канала
            pipeServer1 = gcnew NamedPipeServerStream("\\\\.\\pipe\\testpipe1", PipeDirection::InOut, numThreads);
            pipeServer2 = gcnew NamedPipeServerStream("\\\\.\\pipe\\testpipe2", PipeDirection::InOut, numThreads);
        }
                                
        threadId = Thread::CurrentThread->ManagedThreadId;


        // Ждем коннекта от клиента
        pipeServer->WaitForConnection();

        Console::WriteLine("Client connected on thread.", threadId);
        try
        {  
          // В зависимости от очередности подпроцесса копируем данные из одного именованного канала в другой
          if (i == 0) {
            needclose = true;
            i++;
            // Создаем еще один подпроцесс для встречного копирования информации из именованных каналов
            Thread server = gcnew Thread(gcnew ThreadStart(&ServerThread));
            // Запускаем подпроцесс
            server->Start();
            pipeServer1->CopyToAsync(pipeServer2);
          } else {
            pipeServer2->CopyToAsync(pipeServer1);
          }
        }
        // Обработчик ислючений для отвалившихся клиентов 
        catch (IOException^ e)
        {
            Console::WriteLine("ERROR: {0}", e->Message);
        }
        if (needclose) {
          pipeServer1->Close();
          pipeServer2->Close();
        }
     }
};

int main()
{
    PipeServer::Main();
}
 

MinGW установил, настроил и прикрутил к NetBeans.

Идея с созданием полнодуплексного шлюза из двух серверных  каналов и перенаправлением информации клиентам из одного канала в другой не прошла. Если один серверный подпроцесс читает канал, а второй в него что-то передает, то полный дуплекс не работает (по крайней мере на Windows XP), т.к. иногда тот подпроцесс, который читает из канала, перехватывает сообщения от того подпроцесса, который пишет в этот же канал и возвращает информацию обратно.

Если убрать один из подпроцессов, то передача идет по симплексу от клиента к клиенту без всяких проблем.

Впрочем, еще не все потеряно, т.к. есть еще и режим overlapped, т.е. когда создается, не два, а всего один канал, а к нему коннектятся одновременно несколько клиентов. В этом случае серверу не надо все время читать информацию из канала, т.к. все построено на событиях. Т.е. если какой либо клиент отправил информацию в канал, то сервер считывает его событие и выковыривает оттуда переданное сообщение. А перенаправить полученную информацию второму клиенту - это уже дело техники. Сейчас ваяю такую реализацию.

 

Все. Сдался я. Устал возиться с кодом на С++ да еще и через Win API. Не столько кодируешь, сколько роешься в разбросанных обрывках информации на MSDN, чтобы попытаться понять, как все должно работать. Опыта не хватает, поэтому отправил все это хозяйство в сервис Работа. См. Переделать код на C++ в двусторонний шлюз по полному дуплексу

Может быть кто-то более опытный запросто справится с этой задачей? Вполне не исключаю, что причина неудач в том, что не удалось разобраться с настройками именованных каналов. Т.е. возможно, что в этих самых настройках что-то нужно правильно прописать и все может быть заработает.  Мне пока ничего кроме как в симплексном режиме сделать и запустить так и не удалось.

 

Вот решил попробовать подружить MQL и AutoIt, через пайпы.

Короче одни грабли, причем везде :)

Ну с горем пополам получилось передать в AutoIt, только первые 4 байта пришлось выкинуть, там какой-то "мусор". Что это за "мусор" такой?

Потом попробовал передачу в MQL, тут еще веселей - вообще ничего не приходит. Или может я не правильно организую передачу... Может вся загвоздка в тех 4 байтах?

Что подскажете?

 
fyords:

Вот решил попробовать подружить MQL и AutoIt, через пайпы.

Короче одни грабли, причем везде :)

Ну с горем пополам получилось передать в AutoIt, только первые 4 байта пришлось выкинуть, там какой-то "мусор". Что это за "мусор" такой?

Потом попробовал передачу в MQL, тут еще веселей - вообще ничего не приходит. Или может я не правильно организую передачу... Может вся загвоздка в тех 4 байтах?

Что подскажете?

шо, и вы туда же?
Клуб Телепатов - MQL4 форум
  • www.mql5.com
Клуб Телепатов - MQL4 форум
 
sergeev:
шо, и вы туда же?

Да не, я рядом :) Значит, было дело так:

MQL5

#include <Files\FilePipe.mqh>

CFilePipe  ExtPipe;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- wait for pipe server
   while(!IsStopped())
     {
      if(ExtPipe.Open("\\\\.\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN|FILE_ANSI)!=INVALID_HANDLE) break;
      Sleep(250);
     }
   Print("Client: pipe opened");
//--- send welcome message
   string msg=__FILE__+" on MQL5 build "+IntegerToString(__MQ5BUILD__);
   //Print(msg);
   
   if(!ExtPipe.WriteString(msg))
     {
      Print("Client: sending welcome message failed");
      return;
     }
  }

AutoIt

#include <NamedPipes.au3>
#include <WinAPI.au3>

$pipe="\\.\pipe\MQL5.Pipe.Server"
$hwnd_pipe=_NamedPipes_CreateNamedPipe($pipe) ;//Creates an instance of a named pipe
_NamedPipes_ConnectNamedPipe($hwnd_pipe) ;//Enables a named pipe server process to wait for a client process to connect
ConsoleWrite(ReadMsg($hwnd_pipe)) ;//Writes data to the STDOUT stream

Func ReadMsg($hPipe) ;//ReadMsg
        Local $bSuccess, $iRead, $pBuffer, $tBuffer, $BUFSIZE=4096

        $tBuffer = DllStructCreate("char Text[4096]")
        $pBuffer = DllStructGetPtr($tBuffer)
        _WinAPI_ReadFile($hPipe, $pBuffer, 4, $iRead, 0)
        While 1
                $bSuccess = _WinAPI_ReadFile($hPipe, $pBuffer, $BUFSIZE, $iRead, 0)
                If $iRead = 0 Then ExitLoop
                If Not $bSuccess Or (_WinAPI_GetLastError() = 234) Then ExitLoop
                Return StringLeft(DllStructGetData($tBuffer, "Text"), $iRead)
        WEnd
 EndFunc
 
 Func WriteMsg($hPipe,$sMessage) ;//WriteMsg
        Local $iWritten, $iBuffer, $pBuffer, $tBuffer

        $iBuffer = StringLen($sMessage) + 1
        $tBuffer = DllStructCreate("char Text[" & $iBuffer & "]")
        $pBuffer = DllStructGetPtr($tBuffer)
        DllStructSetData($tBuffer, "Text", $sMessage)
        If Not _WinAPI_WriteFile($hPipe, $pBuffer, $iBuffer, $iWritten, 0) Then
                LogError("WriteMsg: _WinAPI_WriteFile failed")
        EndIf
EndFunc

Эта часть передача из MQL в AutoIt. Работает.

Строкой из  Func ReadMsg($hPipe) 

_WinAPI_ReadFile($hPipe, $pBuffer, 4, $iRead, 0)

выедаю первые 4 байта и все работает.

Вопрос: что содержат эти первые 4 байта? 

 
fyords:

выедаю первые 4 байта и все работает.

Вопрос: что содержат эти первые 4 байта? 

При передаче строк сначала идут 4 байта ее размера.
 
Dima_S:
Ренат, а когда планируется в МТ4 сделать пайпы?
Сделали в прошлом билде MetaTrader 4.
Причина обращения: