NamedPipes / WaitForMultipleObjects



  • Hallo Gemeinde,

    ich habe hier ein Problem mit NamedPipes und / oder WaitForMultipleObjects. In dem Server Code unten kehrt WaitForMultipleObjects nie zurück, auch wenn der Client verbunden ist und auf Daten wartet. Ändere ich das WAIT in FALSE dann kehrt die Funktion zwar zurück liefert aber für i0 immer INSTANCES+1.

    Kann mir jemand weiterhelfen, ich komme hier nun schon seit einiger Zeit nicht weiter.

    // NP_Server.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <Windows.h>
    #include <ctime>
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    const int Instances = 2;
    
    struct SData
    {
      int idx;
      time_t t;
    };
    
    enum class ServerState
    {
      notConnected,
      Connected
    };
    
    void PipeFunction(HANDLE *hPipe, HANDLE *hEvents, volatile time_t* t)
    {
      OVERLAPPED ov[Instances];
      ServerState curState[Instances];
      int i;
      DWORD dwBytes;
      DWORD gle;
    
      for (i = 0; i < Instances; i++)
      {
        ov[i].hEvent = hEvents[i];
        BOOL res = ConnectNamedPipe(hPipe, &ov[i]);
        cout << i << " ConnectNamedPipe = " << res << endl;
    
        curState[i] = ServerState::notConnected;
      }
      do
      {
        int i0 = WaitForMultipleObjects(Instances + 2, hEvents, TRUE, INFINITE);
        int iObj = i0 - WAIT_OBJECT_0;
        cout << "WaitForMultipleObjects = " << iObj << endl;
        if (iObj >= 0 && iObj < Instances)
        {
    
          if (GetOverlappedResult(hPipe, &ov[iObj], &dwBytes, FALSE))
          {
            if (curState[iObj] == ServerState::notConnected)
              curState[iObj] = ServerState::Connected;
          }
        }
        else if (iObj == Instances)
        {
          for (i = 0; i < Instances; i++)
          {
            if (curState[i] == ServerState::Connected)
              DisconnectNamedPipe(hPipe[i]);
            CloseHandle(hPipe[i]);
          }
          return;
        }
        else if (iObj == Instances + 1)
        {
          SData data;
          data.idx = i;
          data.t = *t;
          for (i = 0; i < Instances; i++)
          {
            if (curState[i] == ServerState::Connected)
            {
              if (WriteFile(hPipe[i], &data, sizeof(SData), &dwBytes, NULL))
              { // success
                if (dwBytes > 0)
                {
                  cout << i << "Writefile succeded, " << dwBytes << " bytes written. (" << data.t << ")" << endl;
                }
                else
                {
                  gle = GetLastError();
                  cout << i << "Writefile failed, GLE = " << gle << endl;
                  DisconnectNamedPipe(hPipe);
                  curState[i] = ServerState::notConnected;
                  BOOL res = ConnectNamedPipe(hPipe, &ov[i]);
                  cout << i << " ConnectNamedPipe = " << res << endl;
                }
              }
              else
              {
                gle = GetLastError();
                cout << i << "Writefile failed, GLE = " << gle << endl;
                DisconnectNamedPipe(hPipe);
                curState[i] = ServerState::notConnected;
                BOOL res = ConnectNamedPipe(hPipe, &ov[i]);
                cout << i << " ConnectNamedPipe = " << res << endl;
              }
            }
          }
          ResetEvent(hEvents[iObj]);
        }
      } while (1);
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      HANDLE hPipe[Instances];
      HANDLE hEvents[Instances+2];
      std::thread tPipe;
    
      volatile time_t t = time(nullptr);
    
      for (int i = 0; i < Instances; i++)
      {
        hPipe[i] = CreateNamedPipe(
          L"\\\\.\\pipe\\APMS_Server",
          PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED,
          PIPE_TYPE_BYTE | PIPE_WAIT,
          Instances,
          sizeof(SData),
          sizeof(SData),
          5000,
          NULL);
    
        hEvents[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
        if (hEvents[i] == NULL)
        {
          cout << "CreateEvent(" << i << ") failed";
          return 0;
        }
      }
      hEvents[Instances  ] = CreateEvent(NULL, TRUE, FALSE, NULL);
      hEvents[Instances+1] = CreateEvent(NULL, TRUE, FALSE, NULL);
    
      tPipe = std::thread(PipeFunction, hPipe, hEvents, &t);
    
      for (int i = 0; i < 100; ++i)
      {
        t = time(nullptr);
        SetEvent(hEvents[Instances + 1]);
        Sleep(1000);
      }
    
      SetEvent(hEvents[Instances]);
      tPipe.join();
    
      return 0;
    }
    


  • Wenn du das Event so erstellst:
    CreateEvent(NULL, TRUE, FALSE, NULL);
    dann bedeutet das, dass es nicht wieder selbständig in den nicht signalisierten Zustand zurückgeht.

    MSDN schrieb:

    then you must use the ResetEvent function to manually reset the state to nonsignaled

    https://msdn.microsoft.com/en-us/library/aa915030.aspx

    Also: entweder Autoreset einschalten oder ResetEvent()

    Für WaitForMultipleObjects() scheint fWaitAll=TRUE keinen Sinn zu machen:

    MSDN schrieb:

    fWaitAll

    [in] Specifies the wait type. This parameter must be set to FALSE.

    Siehe:
    https://msdn.microsoft.com/en-us/library/aa915354.aspx



  • Danke soweit, den Teil der Events habe ich soweit verstanden.

    Nun bleibt noch das Problem, das der Verbindungsaufbau nicht erkannt wird. Ich habe den Anfang der do /while Scheifen etwas geändert um zu erkennen woran es hängt. Die Variable conn bleibt für immer gleich 0.

    Im unten gezeigten Client Code bin ich im Debugger bis ReadFile gekommen wo er dann verständlicherweise hängen beibt.

    void PipeFunction(HANDLE *hPipe, HANDLE *hEvents, volatile time_t* t)
    {
      OVERLAPPED ov[Instances];
      ServerState curState[Instances];
      int i;
      DWORD dwBytes;
      DWORD gle;
    
      for (i = 0; i < Instances; i++)
      {
        ov[i].hEvent = hEvents[i];
        BOOL res = ConnectNamedPipe(hPipe, &ov[i]);
        cout << i << " ConnectNamedPipe = " << res << endl;
        gle = GetLastError();
        if (gle == ERROR_IO_PENDING)
          curState[i] = ServerState::notConnected;
        else if (gle == ERROR_PIPE_CONNECTED)
          curState[i] = ServerState::Connected;
        else
          curState[i] = ServerState::notConnected;
      }
      do
      {
        int conn = 0;
        for (i = 0; i < Instances; i++)
        {
    
          if (curState[i] == ServerState::notConnected)
            if(HasOverlappedIoCompleted(&ov[i]))
              curState[i] = ServerState::Connected;
          if (curState[i] == ServerState::Connected)
            conn++;
        }
        if (conn == 0)
        {
          Sleep(500);
          continue;
        }
    ...
    
    #include "stdafx.h"
    #include <Windows.h>
    #include <iostream>
    
    using namespace std;
    
    struct SData
    {
      int idx;
      time_t t;
    };
    
    int _tmain(int argc, _TCHAR* argv[])
    {
      HANDLE hPipe;
      LPTSTR lpszPipeName = L"\\\\.\\pipe\\APMS_Server";
      while (true)
      {
        hPipe = CreateFile(lpszPipeName,
          GENERIC_READ,
          0,
          NULL,
          OPEN_EXISTING,
          0,
          NULL);
    
        if (hPipe != INVALID_HANDLE_VALUE)
          break;
    
        if (GetLastError() != ERROR_PIPE_BUSY)
        {
          cout << "Could not open pipe. GLE=" << GetLastError() << endl;
          return -1;
        }
    
        if (!WaitNamedPipe(lpszPipeName, 20000))
        {
          cout << "Could not open pipe: 20 second wait time out.\n";
          return -1;
        }
      }
    
      // The pipe connected; change to message-read mode.
      //DWORD dwMode   = PIPE_READMODE_BYTE;
      //int   fSuccess = SetNamedPipeHandleState(hPipe, &dwMode, NULL, NULL);
      //if (!fSuccess)
      //{
      //  cout << "SetNamedPipeHandleState failed. GLE=" << GetLastError() << endl;
      //  return -1;
      //}
    
      BOOL fSuccess = 0;
      do
      {
        SData  data;
        DWORD  s;
        fSuccess = ReadFile(hPipe, &data, sizeof(data), &s, NULL);
    
        if (!fSuccess && GetLastError() != ERROR_MORE_DATA)
          break;
    
        cout << "idx = " << data.idx << " t = " << data.t << endl;
      } while (fSuccess);
    
      if (!fSuccess)
      {
        cout << "ReadFile from pipe failed. GLE=" << GetLastError() << endl;
        return -1;
      }
    
      cout << "End of message, connection closed and exit.\n";
      CloseHandle(hPipe);
    
    	return 0;
    }
    


  • Was mir noch auffällt, ist, dass die (wg Instances) zweimal eine Pipe mit demselben Name öffnest.

    MSDN schrieb:

    lpName [in]

    The unique pipe name.

    Hier nachzulesen:
    https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150%28v=vs.85%29.aspx

    Ich würde sagen, dass du beim zweiten Mal nur das Handle zur ersten Pipe. Frage ist, warum zwei?

    Als nächstes: Overlapping ist wohl auch ein Problem. Du hast ja nich FILE_FLAG_OVERLAPPED gesetzt. Für ConnectNamedPipe:

    MSDN schrieb:

    If hNamedPipe was not opened with FILE_FLAG_OVERLAPPED, the function does not return until a client is connected or an error occurs. Successful synchronous operations result in the function returning a nonzero value if a client connects after the function is called.

    Somit solltest du eine Connection haben, wenn ConnectNamedPipe(...) zurückkommt. Vermutlich wird der overlapped-Parameter ignoriert.
    Link:
    https://msdn.microsoft.com/en-us/library/windows/desktop/aa365146%28v=vs.85%29.aspx



  • Danke, soweit.

    Aber ich verstehe das so, daß für jede Instanz CreateNamedPipe aufgerufen werden muß.

    Creates an instance of a named pipe and returns a handle for subsequent pipe operations. A named pipe server process uses this function either to create the first instance of a specific named pipe and establish its basic attributes or to create a new instance of an existing named pipe.

    Den Hinweis auf FILE_FLAG_OVERLAP verstehe ich jetzt nicht, ist doch gesetzt?

    Wenn ich das ganze ohne FILE_FLAG_OVERLAP und NULL für lpOVERLAP baue, komme ich zu dem Problem das ConnectNamedPipe blockierend ist. Wenn sich ein Client verbindet wird die Verbindung dann zwar erkannt, aber für die Threads mit den Instanzen die noch nicht verbunden worden sind gibt es keine (mir bekannte) Lösung den Thread zu beenden. CloseHandle auf das entsprechende hPipe funktioniert nicht (habe ich probiert). Ich könnte jetzt bei beenden nur vom Server aus eine Verbindung erzeugen um aus ConnectNamedPipe herauszukommen.



  • Vieleicht gab es ein Mißverständnis. Der Client Code ist blockierend (ohne Overlapping), das ist für mich auch OK so.



  • Den Hinweis auf FILE_FLAG_OVERLAP verstehe ich jetzt nicht, ist doch gesetzt?

    Stimmt, habe ich übersehen.

    Ich denke, es macht keinen Unterschied.
    Für FILE_FLAG_OVERLAPPED:

    MSDN schrieb:

    The ReadFile, WriteFile, ConnectNamedPipe, and TransactNamedPipe functions can execute either synchronously or as overlapped operations.

    Für PIPE_WAIT

    MSDN schrieb:

    Blocking mode is enabled. When the pipe handle is specified in the ReadFile, WriteFile, or ConnectNamedPipe function, the operations are not completed until there is data to read, all data is written, or a client is connected.

    Es scheint so, dass PIPE_WAIT FILE_FLAG_OVERLAPPED überschreibt.

    Aber ich verstehe das so, daß für jede Instanz CreateNamedPipe aufgerufen werden muß.

    Ja, aber wozu braucht man mehr als eine Instanz derselben Pipe?

    aber für die Threads mit den Instanzen die noch nicht verbunden worden sind gibt es keine (mir bekannte) Lösung den Thread zu beenden.

    Mach es doch folgendermaßen:
    1. Setze an [bool] Flag für das Beenden des Threads.
    2. Verbinde dich mit der Pipe wie beim Client (über CreateFile(...)).

    Dann sollten die Probleme mit Overlapping eigentlich gelöst sein. (So ähnlich mach ich das auch mit einem TCP/IP Server)



  • coder777 schrieb:

    Es scheint so, dass PIPE_WAIT FILE_FLAG_OVERLAPPED überschreibt.

    Das kann eingentlich nicht sein PIPE_WAIT ist mit 0 definiert.

    coder777 schrieb:

    Ja, aber wozu braucht man mehr als eine Instanz derselben Pipe?

    Ich habe einen Server der zu mehreren Clients die gleichen Daten schreiben soll. Im Moment schreibt der Server einfach ein File und die Clients checken periodisch ob das File neu geschrieben wurde. Funktioniert zwar, wirkt aber fehleranfällig und nicht besonders elegant.

    coder777 schrieb:

    aber für die Threads mit den Instanzen die noch nicht verbunden worden sind gibt es keine (mir bekannte) Lösung den Thread zu beenden.

    Mach es doch folgendermaßen:
    1. Setze an [bool] Flag für das Beenden des Threads.
    2. Verbinde dich mit der Pipe wie beim Client (über CreateFile(...)).

    Dann sollten die Probleme mit Overlapping eigentlich gelöst sein. (So ähnlich mach ich das auch mit einem TCP/IP Server)

    Ja darauf wird es wohl hinauslaufen! Nicht besonders schön aber so funktioniert es dann wenigstens.

    Ich danke Dir für Deine Zeit, jetzt werde ich den vorgeschlagenen Weg implementieren.


Anmelden zum Antworten