Download MetaTrader 5

Problem with Client-Server-Communication (named pipes)

To add comments, please log in or register
BobBillsworth
13
BobBillsworth  
Hello all! :-)

With Microsoft's help i have created a small multithreaded named pipe server. It is not finished yet. Finally i want to connect two MT4-Terminals as Client "X" (aka Client1) and as Client "Y" (aka Client2). At the moment i'am getting an unsorted output. It looks like a asynchronous communication. Perhaps you know what the problem is. Thanks in advance for your answers.

The output:

...
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 88↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 99↨!
The ID of thedata is: 0!
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED: 2987;2989 ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 86↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 98↨!
The ID of thedata is: 8!
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED: 2987;2989 ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 86↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 98↨!
The ID of thedata is: 8!
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED: 2987;2989 ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 88↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 99↨!
The ID of thedata is: 0!
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED: 2988.5;2990.5 ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 89↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 99↨!
The ID of thedata is: 1!
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED: 2988;2990 ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: X!
SENDING AN OK TO X!
I'am reading from X!
RECEIVED:  ! Now I'am writing to X!
The content of Client2 is: 2: Nothing received...
Size of Client2 is: 23 ! Size of TCHAR is: 1 !
The ID of thedata is: 29↨!
The ID of thedata is: 87↨!
The ID of thedata is: ;2↨!
The ID of thedata is: 98↨!
The ID of thedata is: 9!
InstanceThread: client disconnected.
InstanceThread exitting.
...

The server(C++):

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>

#define BUFSIZE 512

DWORD WINAPI InstanceThread(LPVOID);

struct theStruct {
                TCHAR Client1[BUFSIZE];
                TCHAR Client2[BUFSIZE];
                HANDLE thePipe;
   };

int main(VOID)
{
   BOOL   fConnected = FALSE;
   DWORD  dwThreadId = 0;
   HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
   LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\MyMT4PipeServer");
  
   theStruct *base = (theStruct*) malloc(sizeof(theStruct));
    
   StringCchCopy(base->Client1, 23*sizeof(TCHAR), TEXT("1: Nothing received..."));
   StringCchCopy(base->Client2, 23*sizeof(TCHAR), TEXT("2: Nothing received..."));
      
   for (;;)
   {
      printf("\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename);
      hPipe = CreateNamedPipe(
          lpszPipename,             // pipe name
          PIPE_ACCESS_DUPLEX,       // read/write access
          PIPE_TYPE_BYTE |      
          PIPE_READMODE_BYTE |  
          PIPE_WAIT,                // blocking mode
          PIPE_UNLIMITED_INSTANCES, // max. instances  
          BUFSIZE,                  // output buffer size
          BUFSIZE,                  // input buffer size
          0,                        // client time-out
          NULL);                    // default security attribute

      if (hPipe == INVALID_HANDLE_VALUE)
      {
          printf("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
                  free(base);
          return -1;
      }

      fConnected = ConnectNamedPipe(hPipe, NULL) ?
         TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

      if (fConnected)
      {
         printf("Client connected, creating a processing thread.\n");

                 base->thePipe = hPipe;
      
         hThread = CreateThread(
            NULL,              // no security attribute
            0,                 // default stack size
            InstanceThread,    // thread proc
            base,    // thread parameter
            0,                 // not suspended
            &dwThreadId);      // returns thread ID

         if (hThread == NULL)
         {
            printf("CreateThread failed, GLE=%d.\n", GetLastError());
                        free(base);
            return -1;
         }
         else CloseHandle(hThread);
       }
      else
        CloseHandle(hPipe);
   }
   free(base);
   return 0;
}

DWORD WINAPI InstanceThread(LPVOID lpvParam)

   DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
   BOOL fSuccess = FALSE;
   HANDLE hPipe  = NULL;

   if (lpvParam == NULL)
   {
       printf( "\nERROR - Pipe Server Failure:\n");
       printf( "   InstanceThread got an unexpected NULL value in lpvParam.\n");
       printf( "   InstanceThread exitting.\n");
       return (DWORD)-1;
   }
    
   printf("InstanceThread created, receiving and processing messages.\n");

   struct theStruct *thedata = (struct theStruct*) lpvParam;

   hPipe = (HANDLE) thedata->thePipe;

   while (1)
   {
        TCHAR theid[2];
        
      fSuccess = ReadFile(
         hPipe,        // handle to pipe
         theid,    // buffer to receive data
         2*sizeof(TCHAR), // size of buffer
         &cbBytesRead, // number of bytes read
         NULL);        // not overlapped I/O

      if (!fSuccess || cbBytesRead == 0)
      {  
          if (GetLastError() == ERROR_BROKEN_PIPE)
          {
              printf("InstanceThread: client disconnected.\n", GetLastError());
          }
          else
          {
              printf("InstanceThread ReadFile failed, GLE=%d.\n", GetLastError());
          }
          break;
      }
                  
      printf("The ID of thedata is: %s! \n",theid);
          
          if (_tcscmp(theid,"X") == 0) {
                  
                printf("SENDING AN OK TO X!\n");
                
                DWORD ok = 42;
          
            fSuccess = WriteFile(hPipe,&ok,sizeof(ok),&cbWritten,NULL);
          
            if (!fSuccess || sizeof(ok) != cbWritten)
        {  
          printf("InstanceThread WriteFile(int) failed, GLE=%d.\n", GetLastError());
          break;
        }
                  
                printf("I'am reading from X! \n");
                  
                fSuccess = ReadFile(
         hPipe,        // handle to pipe
         thedata->Client1,    // buffer to receive data
         BUFSIZE*sizeof(TCHAR), // size of buffer
         &cbBytesRead, // number of bytes read
         NULL);        // not overlapped I/O

      if (!fSuccess || cbBytesRead == 0)
      {  
          if (GetLastError() == ERROR_BROKEN_PIPE)
          {
              printf("InstanceThread: client disconnected.\n", GetLastError());
          }
          else
          {
              printf("InstanceThread ReadFile failed, GLE=%d.\n", GetLastError());
          }
          break;
      }
                  
          printf("RECEIVED: %s ! Now I'am writing to X! \n",thedata->Client1);
          
          DWORD cl2size = lstrlen(thedata->Client2)+1;
                  
          fSuccess = WriteFile(hPipe,&cl2size,sizeof(cl2size),&cbWritten,NULL);
          
          if (!fSuccess || sizeof(cl2size) != cbWritten)
      {  
          printf("InstanceThread WriteFile(int) failed, GLE=%d.\n", GetLastError());
          break;
      }
          
          printf("The content of Client2 is: %s \n",thedata->Client2);
          
          DWORD sizeOfClient2 = (lstrlen(thedata->Client2)+1)*sizeof(TCHAR);
          
          printf("Size of Client2 is: %d ! Size of TCHAR is: %d !\n",sizeOfClient2, sizeof(TCHAR));
          
          fSuccess = WriteFile(
         hPipe,        // handle to pipe
         thedata->Client2,// buffer to write from
         sizeOfClient2, //number of bytes to write
         &cbWritten,   // number of bytes written
         NULL);        // not overlapped I/O

      if (!fSuccess || sizeOfClient2 != cbWritten)
      {  
          printf("InstanceThread WriteFile(str) failed, GLE=%d.\n", GetLastError());
          break;
      }
                
          } else if (_tcscmp(theid,"Y") == 0) {
                // Not finished yet. See "X" for further details.
          }      
  }

   FlushFileBuffers(hPipe);
   DisconnectNamedPipe(hPipe);
   CloseHandle(hPipe);
  
   printf("InstanceThread exitting.\n");
   return 1;
}

The client(MQL4):

#property strict
//--- input parameters
input string      ID="X";

int fHandle;
string receive = "NOTHING YET!";

void contact () {
  
   FileWriteString(fHandle,ID,StringLen(ID)+1);
  
   FileFlush(fHandle);
   FileSeek(fHandle,0,SEEK_SET);
  
   int getOK = FileReadInteger(fHandle);
  
   FileFlush(fHandle);
   FileSeek(fHandle,0,SEEK_SET);
  
   if (getOK == 42) {
  
      string send = Bid + ";" + Ask;
      //Print(send);
      FileWriteString(fHandle,send,StringLen(send)+1);
      
      FileFlush(fHandle);
      FileSeek(fHandle,0,SEEK_SET);
      
      int getsize = FileReadInteger(fHandle);
      
      //FileFlush(fHandle);
      //FileSeek(fHandle,0,SEEK_SET);
        
      receive = FileReadString(fHandle,getsize);
          
      FileFlush(fHandle);
      FileSeek(fHandle,0,SEEK_SET);
      
   }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   fHandle = FileOpen("\\\\.\\pipe\\MyMT4PipeServer",
                      FILE_READ|FILE_WRITE|FILE_BIN);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   FileClose(fHandle);
   EventKillTimer();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   contact();
   Print(receive);
  }
//+------------------------------------------------------------------+

...

Carl Schreiber
7276
Carl Schreiber  

I would recommend you to use memory mapped files:

https://www.mql5.com/en/code/818
https://www.mql5.com/en/code/817
https://www.mql5.com/en/code/10568

File Mapping without DLL
File Mapping without DLL
  • votes: 29
  • 2012.01.16
  • o_O
  • www.mql5.com
The classes (conveted from C++ to MQL5) for working with memory mapped files.
BobBillsworth
13
BobBillsworth  
Carl Schreiber:

I would recommend you to use memory mapped files:

https://www.mql5.com/en/code/818
https://www.mql5.com/en/code/817
https://www.mql5.com/en/code/10568

Thanks for your fast answer, Carl. MMF seems to be much easier than what i did -wasted a whole weekend. I have not read the entire .mqh. Do i need something like a semaphore or is something like that included in the .mqh?

Carl Schreiber
7276
Carl Schreiber  

Sorry, I haven't worked with them yet - you have to try yourself. But I guess you have to define your own access management!

I would recommend to have only one directional mm-files means A only writes into f1 and only reads from f1 and as soon B reads from f1 it deletes its content. So that A 'adds' its text (either f1 is empty or it is added at the end) and B reads the whole content all the time and empties it. And as soon as one them accesses f1 it is blocked - which will take take only fractals of mSec.

BobBillsworth
13
BobBillsworth  
Carl Schreiber:

Sorry, I haven't worked with them yet - you have to try yourself. But I guess you have to define your own access management!

I would recommend to have only one directional mm-files means A only writes into f1 and only reads from f1 and as soon B reads from f1 it deletes its content. So that A 'adds' its text (either f1 is empty or it is added at the end) and B reads the whole content all the time and empties it. And as soon as one them accesses f1 it is blocked - which will take take only fractals of mSec.

Hello Carl, :-)

thanks for your help and excuse my late answer. The transfer of data works. I have done a lot of testing. Currently there is no need for a semaphore. The EA creates two instances of the provided class. Instance(A1) creates a file, only writes to it and the other terminal -instance(B2)- only reads from it. Instance(A2) opens the file the other terminal -instance(B1)- created and only reads from it. This solution is scaleable as long as there is enough RAM.

For synchronizisation purposes i have written a small include-file. It provides the local time with milliseconds.

From my current point of view synchronizisation is the hardest job. In an demo-account i have lost nearly 2200 in six minutes, just because the other demo-account stopped providing quotes and as a result both terminals got out of sync. Hopefully my new code using the small include-file will help.
To add comments, please log in or register