C++ Windows Asynch IO Named Pipe first message not

2019-02-20 21:35发布

问题:

Modified code from Named Pipe Server Using Overlapped I/O https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx

The server code is as follows:

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

#define CONNECTING_STATE 0 
#define READING_STATE 1 

#define INSTANCES 4 
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096

typedef struct 
{ 
OVERLAPPED oOverlap; 
HANDLE hPipeInst; 
TCHAR chRequest[BUFSIZE]; 
DWORD cbRead;
TCHAR chReply[BUFSIZE];
DWORD cbToWrite; 
DWORD dwState; 
BOOL fPendingIO;

} PIPEINST, *LPPIPEINST; 

BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED); 

PIPEINST Pipe[INSTANCES]; 
HANDLE hEvents[INSTANCES]; 

int _tmain(VOID) 
{ 
DWORD i, dwWait, cbRet, dwErr; 
BOOL fSuccess; 
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); 

for (i = 0; i < INSTANCES; i++) 
{ 

    hEvents[i] = CreateEvent( 
        NULL,    // default security attribute 
        FALSE,    // manual-reset event 
        TRUE,    // initial state = signaled 
        NULL);   // unnamed event object 

    if (hEvents[i] == NULL) 
    {
        printf("CreateEvent failed with %d.\n", GetLastError()); 
        return 0;
    }

    Pipe[i].oOverlap.hEvent = hEvents[i]; 

    DWORD dwOpenMode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED;

    Pipe[i].oOverlap.Offset = 0;
    Pipe[i].oOverlap.OffsetHigh = 0;

    Pipe[i].hPipeInst = CreateNamedPipe( 
        lpszPipename,           
        dwOpenMode,     
        PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,               
        INSTANCES,               
        BUFSIZE*sizeof(TCHAR),   
        BUFSIZE*sizeof(TCHAR),   
        PIPE_TIMEOUT,            
        NULL);                   

    if (Pipe[i].hPipeInst == INVALID_HANDLE_VALUE) 
    {
        printf("CreateNamedPipe failed with %d.\n", GetLastError());
        return 0;
    }

    BOOL rc = ConnectNamedPipe(Pipe[i].hPipeInst, &Pipe[i].oOverlap);                   // Overlapped ConnectNamedPipe should return FALSE.
    if (!rc && GetLastError() == ERROR_PIPE_CONNECTED) {
        std::cout<<"pipe connected setting event " << std::endl;
        rc = SetEvent(&Pipe[i].oOverlap.hEvent);

    } else if (rc || GetLastError() != ERROR_IO_PENDING) {
        std::cout<<"exiting... " << std::endl;
        rc = CloseHandle(Pipe[i].hPipeInst);
        return 0;
    }

}// for INSTANCES

    while (1) 
    { 
        dwWait = WaitForMultipleObjects( 
            INSTANCES,    // number of event objects 
            hEvents,      // array of event objects 
            FALSE,        // does not wait for all 
            INFINITE);    // waits indefinitely 

        i = dwWait - WAIT_OBJECT_0;  // determines which pipe 

        if (i < 0 || i > (INSTANCES - 1)) 
        {
            printf("Index out of range.\n"); 
            return 0;
        }

        fSuccess = GetOverlappedResult( 
            Pipe[i].hPipeInst, // handle to pipe 
            &Pipe[i].oOverlap, // OVERLAPPED structure 
            &cbRet,            // bytes transferred 
            FALSE);            // do not wait 

        std::cout<<"GetOverlappedResult " << cbRet;
        std::cout<<" success " << fSuccess;
        std::cout<<" state " << Pipe[i].dwState;
        std::cout<<" GetLastError " << GetLastError() << std::endl;


        fSuccess = ReadFile( 
            Pipe[i].hPipeInst, 
            Pipe[i].chRequest, 
            BUFSIZE*sizeof(TCHAR), 
            &Pipe[i].cbRead, 
            &Pipe[i].oOverlap); 

        if(!fSuccess)
            std::wcout<<L" Error: "<< GetLastError() <<std::endl;

        if (fSuccess && Pipe[i].cbRead != 0) 
        { 
            Pipe[i].fPendingIO = FALSE; 
            Pipe[i].dwState = READING_STATE; 
            std::wcout<<L"Message " << Pipe[i].chRequest << std::endl;
            continue; 
        } 

        dwErr = GetLastError(); 
        if (! fSuccess && (dwErr == ERROR_IO_PENDING)) 
        { 
            std::cout<<"Error IO is still pending" << std::endl;
            Pipe[i].fPendingIO = TRUE; 
            continue; 
        } 
        break;
    }

    return 0; 
} 

The client code is as follows:

 #include <windows.h> 
 #include <stdio.h>
 #include <conio.h>
 #include <tchar.h>
 #include <string>
 #include <sstream>
 #include <time.h>
 #include <iostream>

 #define BUFSIZE 4096

int _tmain(int argc, TCHAR *argv[]) 
{ 
HANDLE hPipe; 

TCHAR  chBuf[BUFSIZE]; 
BOOL   fSuccess = FALSE; 
DWORD  cbRead, cbToWrite, cbWritten, dwMode; 
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe"); 
BOOL   rc;

 do {
    hPipe = CreateFileW(lpszPipename, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
    if (hPipe == INVALID_HANDLE_VALUE) {
      if (GetLastError() == ERROR_PIPE_BUSY) {
        // wait for the pipe to become available
        rc = WaitNamedPipeW(lpszPipename, NMPWAIT_USE_DEFAULT_WAIT);
        if (!rc) return false;
      } else {
        return false;
      }
    }
  } while (hPipe == INVALID_HANDLE_VALUE);

 dwMode = PIPE_READMODE_MESSAGE; 
   fSuccess = SetNamedPipeHandleState( 
      hPipe,    // pipe handle 
      &dwMode,  // new pipe mode 
      NULL,     // don't set maximum bytes 
      NULL);    // don't set maximum time 
   if ( ! fSuccess) 
   {
      _tprintf( TEXT("SetNamedPipeHandleState failed. GLE=%d\n"), GetLastError() ); 
      return -1;
   }

//===================================================================

while(1)
{
    std::cout<<"press a key to send " << std::endl;
    _getch();

    fSuccess = WriteFile( 
        hPipe,                  
        "A",
        1, 
        &cbWritten,             
        NULL);
 if ( ! fSuccess) 
    {
        _tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError()           ); 
        return -1;
    }

    fSuccess = WriteFile( 
        hPipe,                  
        "B",
        1, 
        &cbWritten,             
        NULL);
 if ( ! fSuccess) 
    {
        _tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError()    ); 
        return -1;
    }
    //Sleep(1000);

    fSuccess = WriteFile( 
        hPipe,                  
        "C",
        1, 
        &cbWritten,             
        NULL);

 if ( ! fSuccess) 
    {
        _tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError()       ); 
        return -1;
    }

    fSuccess = WriteFile( 
        hPipe,                  
        "D",
        1, 
        &cbWritten,             
        NULL);


    if ( ! fSuccess) 
    {
        _tprintf( TEXT("WriteFile to pipe failed. GLE=%d\n"), GetLastError()    ); 
        return -1;
    }
}// loop 


printf("\n<End of message, press ENTER to terminate connection and exit>");
_getch();

CloseHandle(hPipe); 

return 0; 

}

The server never receives message A but B, C, D are received OK. If you uncomment //Sleep(1000) in the client, only B and D are received.

Any idea as to why this happens? The server output with out the sleep is as follows:

 GetOverlappedResult 0 success 1 state 0 GetLastErr
 or 997
 Error: 997
 Error IO is still pending
 GetOverlappedResult 1 success 1 state 0 GetLastErr
 or 997
 Message B
 GetOverlappedResult 1 success 1 state 1 GetLastErr
 or 997
 Message C
 GetOverlappedResult 1 success 1 state 1 GetLastErr
 or 997
 Message D
 GetOverlappedResult 1 success 1 state 1 GetLastErr
 or 997
 Error: 997
 Error IO is still pending

回答1:

In the server's read loop, you are discarding any data that arrives asynchronously.

After GetOverlappedResult() has reported that the pending I/O operation is complete, the buffer contains the data from that operation. You're ignoring that data and issuing a new read operation into the same buffer.

The only reason you get any of the messages is that (on most runs) all four messages will be written into the pipe's internal buffer at the same time. The first message arrives asynchronously, so you miss it, but the remaining three messages are already in the pipe so those reads can complete immediately.