未收到 C++ Windows 异步 IO 命名管道第一条消息

Posted

技术标签:

【中文标题】未收到 C++ Windows 异步 IO 命名管道第一条消息【英文标题】:C++ Windows Asynch IO Named Pipe first message not received 【发布时间】:2015-02-11 12:15:55 【问题描述】:

修改后的代码来自 使用重叠 I/O 的命名管道服务器 https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx

服务器代码如下:

#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; 
 

客户端代码如下:

 #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; 

服务器永远不会收到消息A,但是B,C,D都收到了。 如果在客户端取消注释 //Sleep(1000),则只收到 B 和 D。

知道为什么会这样吗?没有休眠的服务器输出如下:

 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】:

在服务器的读取循环中,您将丢弃任何异步到达的数据。

GetOverlappedResult() 报告挂起的 I/O 操作完成后,缓冲区包含来自该操作的数据。您忽略了该数据并向同一个缓冲区发出新的读取操作。

您收到任何消息的唯一原因是(在大多数运行中)所有四个消息将同时写入管道的内部缓冲区。第一条消息是异步到达的,因此您会错过它,但其余三条消息已经在管道中,因此这些读取可以立即完成。

【讨论】:

感谢哈利的回复,这很有帮助:)

以上是关于未收到 C++ Windows 异步 IO 命名管道第一条消息的主要内容,如果未能解决你的问题,请参考以下文章

C++ 错误:“unordered_map”未命名类型

Windows 命名信号量未锁定

C++ 的非线程异步 IO 简介?

使用C++ boost从零构建一个异步文件IO系统

C++ 中未命名命名空间的使用

什么时候适合在 C++ 中使用静态(通过未命名的命名空间)?