기고글 토론 "DLLs을 사용하지 않고 명명된 파이프를 사용하여 MetaTrader 5와 통신하기" - 페이지 3

 
Renat:
단말기 간 통신 문제는 적용 비중이 적다고 생각합니다.

하지만 외부 시스템과의 통신은 더 중요하고 적용 범위가 넓습니다. 이것이 보안 채널이 열린 이유입니다.

그 목적에 대해서는 아무도 이의를 제기하지 않습니다. 하지만 문제는 그것이 어떻게 구현되었는가입니다. 결국 외부 시스템은 반드시 C로 작성된 것이 아니라 다른 프로그래밍 언어로 만들 수 있습니다. 그리고 파일 작업을 통해 클라이언트로서 만 많은 프로그래밍 언어의 명명 된 채널을 통해 터미널에 연결할 수 있습니다. 그러나 이 기술은 클라이언트-서버, 즉 브리지-게이트웨이 없이 두 클라이언트 사이에 간격이 있으면 두 클라이언트가 서로 만나지 않도록 선택되었습니다. 즉, 클라이언트-클라이언트 기술 또는 게이트웨이가 제공되어야 했습니다. 그래서 크릴로프 할아버지의 우화 "여우와 포도"에서와 같은 결과가 나왔습니다:

눈은 보지만,

하지만 눈은 볼 수 없습니다.

© I. 크릴로프


다른 사람들이 외부 시스템과 어떻게 비슷한 연결을 실현했는지 살펴볼 필요가있었습니다. 예를 들어 VMWare, MS SQL Server, MySQL, 외부 모뎀 등이 있습니다. 그들의 서버 부분은 내부적으로 구현되어 있습니다. 또한 명명된 채널이나 TCP/IP 및 기타 통신 채널을 통해서도 목발 없이 가입할 수 있어 매우 편리합니다. 예를 들어, 명명된 채널을 통해 선택할 수도 있지만 "명명된 파이프 TCP 프록시" 유틸리티를 사용하여 TCP/IP를 통해 원격으로 선택할 수도 있습니다. 즉, 사용자는 추가 목발을 만들 필요 없이 가장 적합한 클라이언트 애플리케이션을 선택하고 즉시 연결하여 작업할 수 있습니다.
 
유리, 분명히 헷갈리셨군요. 서버 파이프라인은 다른 언어로 쉽게 만들 수 있습니다.

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에서는).

하위 프로세스 중 하나를 제거하면 문제없이 클라이언트에서 클라이언트로 전송이 단순하게 진행됩니다.

그러나 중복 모드, 즉 두 개가 아닌 하나의 채널만 생성되고 여러 클라이언트가 동시에 연결되는 경우도 있기 때문에 모든 것이 손실되는 것은 아닙니다. 이 경우 서버는 모든 것이 이벤트를 기반으로 하기 때문에 항상 채널에서 정보를 읽을 필요가 없습니다. 즉, 어떤 클라이언트가 채널에 정보를 보내면 서버는 해당 이벤트를 읽고 거기서 전송된 메시지를 추출합니다. 그리고 수신된 정보를 두 번째 클라이언트로 리디렉션하는 것은 이미 기술적인 문제입니다. 이러한 구현을 만드는 중입니다.

 

그게 다입니다. 포기했습니다. C++ 코드를 만지작거리는 것도 지쳤고 심지어 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;
//+------------------------------------------------------------------+
//| 스크립트 프로그램 시작 기능|
//+------------------------------------------------------------------+
void OnStart()
  {
//--- 파이프 서버 대기
   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");
//--- 환영 메시지 보내기
   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) ;//명명된 파이프의 인스턴스를 생성합니다.
_NamedPipes_ConnectNamedPipe($hwnd_pipe) ;//명명된 파이프 서버 프로세스가 클라이언트 프로세스가 연결될 때까지 기다릴 수 있도록 합니다.
ConsoleWrite(ReadMsg($hwnd_pipe)) ;//STDOUT 스트림에 데이터를 씁니다.

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:
MT4에서 언제 핍을 만들 계획인가요?
메타트레이더 4의 마지막 빌드에서 만들어졌습니다.