zoukankan      html  css  js  c++  java
  • 使用命名管道的OVERLAPPED方式实现非阻塞模式编程 .

     命令管道是进程间通讯的一种常用方式,对于命令管道的介绍可以参考别的资料和书籍,这里推荐一个《VC++下命名管道编程的原理及实现》这篇博文,写得比较清楚。但是都是介绍了阻塞模式的编程,我这里主要是介绍利用命令管道OVERLAPPED方式使用非阻塞模式编程。注:文中使用的方法就是函数的意思。

     参考MSDN,服务器端创建命令管道(使用CreateNamedPipe方法),不使用FILE_FLAG_OVERLAPPED模式时,当使用ConnectNamedPipe方法时,服务器端会进入阻塞。我们一般处理会创建一个工作线程,在工作线程中使用命令管道,但是会引入一个问题,当我们的程序退出时,这个工作线程没有办法结束,会阻塞在ConnectNamedPipe方法中。使用OVERLAPPED方式可以很好的解决这个问题。在codeproject上有一篇文章《One use for Overlapped I/O》写的比较好,提出了解决方法,大致的思路用下面的代码表示:

    OVERLAPPED  op;
    HANDLE      h,
                handleArray[2];
    BOOL        bStop = FALSE;
      
    memset(&op, 0, sizeof(op));
    handleArray[0] = op.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    handleArray[1] = gbl_hStopEvent;
      
    while (bStop == FALSE)
    {
        h = CreateNamedPipe(FILE_FLAG_OVERLAPPED);

        ConnectNamedPipe(h, &op);
        
        switch (WaitForMultipleObjects(2, handleArray, FALSE, INFINITE))
        {
        case WAIT_OBJECT_0:
            _beginthread(threadProc, 0, h);
            ResetEvent(handleArray[0]);
            break;

        case WAIT_OBJECT_0 + 1:
            CloseHandle(h);
            bStop = TRUE;
            break;
        }
    }

    CloseHandle(handleArray[0]);大致的思路是创建2个事件,一个用于退出线程,一个用于OVERLAPPED接受的event事件绑定,在CreateNamedPipe时,使用FILE_FLAG_OVERLAPPED模式,这样在ConnetNamePipe时,就不会阻塞,会立即返回。然后使用WaitForMultipleObjects方法等待这两个事件。这样就可以实现退出。这篇文章提出的思路是对的,但是在解决实现上还需要细化。首先,对于创建的2个事件,参考MSDN,与OVERLAPPED结构关联的event,要是manual-reset event,同时初始态要为signaled,也就是要使用参数TRUE(为什么初始态要为signaled,这个后面解释)。另外一个是退出线程的事件。在使用CreateNamedPipe(FILE_FLAG_OVERLAPPED)(伪码);使用ConnectNamedPipe(h, &op)后,会立即返回,这个时候一般是返回FALSE,使用GetLastError()会得到ERROR_IO_PENDING,表示这个请求是悬而未决的。我使用一个BOOL fPendingIO标识来记录所有悬而未决的的请求,fPendingIO=TRUE。然后使用WaitForMultipleObjects方法等待这2个事件。线程现在就会阻塞在这里,直到有相关的事件处于signaled态。现在来解释一下为什么开始创建事件时初始态为signaled。按照常理,WaitForMultipleObjects不会被阻塞,因为其中一个事件的状态为signaled。其实不然,它的状态在connectNamedPipe(h, &op)后已经改变了。对以OVERLAPPED关联的事件,当使用OVERLAPPED相关的方法操作后,其状态会可能会改变的,主要基于下面3个原则:1)当实际操作在函数返回前已经完成,事件的状态不会改变。2)当函数返回是,实际的操作没有完成,也即是说这个操作是Pending的,这个时候事件会被设置为nonsignaled.3) 当操作的Pending完成后,事件会被设置为signaled。有了上面的3条原则,OVERLAPPED关联的事件的状态变化就好理解了。当使用connectNamedPipe(h, &op)方法时,函数会立即返回,而实际这个操作并没有进行,而是Pending了,所以,event会由signaled变为nonsignaled,当真正有Client连接时,这个操作才会完成,这个时候,event会由nonsignaled变为signaled。这个时候,WaitForMultipleObjects会继续执行下去。对于Pending后的操作,一定要使用GetOverlappedResult方法,判断结果。上面的原则适用ReadFile, WriteFile, ConnectNamedPipe, 和 TransactNamedPipe等函数。下面是我的代码,设计思路是利用namedpipe实现2个进程间的通讯,客户端发送3个整数给服务器端。Server端:m_hEvents[0] = CreateEvent(NULL,TRUE,TRUE,NULL);   // OVERLPPED‘s event
    m_hEvents[1] = CreateEvent(NULL,TRUE,FALSE,NULL);  // exit eventnamepipe线程:NamedPipeWorkThread(LPVOID lParam)
    {
     TRACE("NamedPipeWorkThread/n");
     
     CServerDlg * pDlg = (CServerDlg*)lParam;
     BOOL fSuccess;
     OVERLAPPED op;
     memset(&op,0,sizeof(op));
     op.hEvent = pDlg->m_hEvents[0];

     LPTSTR lpszPipename = TEXT("////.//pipe//mynamedpipe");
     HANDLE hPipeInst = CreateNamedPipe(
      lpszPipename,            // pipe name
      PIPE_ACCESS_DUPLEX |     // read/write access
      FILE_FLAG_OVERLAPPED,    // overlapped mode
      PIPE_TYPE_MESSAGE |      // message-type pipe
      PIPE_READMODE_MESSAGE |  // message-read mode
      PIPE_WAIT,               // blocking mode
      1,               // number of instances
      BUFSIZE*sizeof(TCHAR),   // output buffer size
      BUFSIZE*sizeof(TCHAR),   // input buffer size
      PIPE_TIMEOUT,            // client time-out
      NULL);                   // default security attributes
     
     if(hPipeInst == INVALID_HANDLE_VALUE) {
      AfxMessageBox("CreateNamedPipe failed with %d./n", GetLastError());
      return 0;
     }

     PT_COLOR  PtColor;
     DWORD dwBytesReaded;

     BOOL fConnected,fPendingIO = FALSE;
     fConnected = ConnectNamedPipe(hPipeInst, &op);

     if (fConnected) {
      AfxMessageBox("ConnectNamedPipe failed with %d./n", GetLastError());
      return 0;
     }
     
     switch (GetLastError()) {
      // The overlapped connection in progress.
     case ERROR_IO_PENDING:
      fPendingIO = TRUE;
      break;
      
      // Client is already connected, so signal an event.
      
     case ERROR_PIPE_CONNECTED:
      if (SetEvent(op.hEvent))
                break;
      
      // If an error occurs during the connect operation...
     default:
      {
       AfxMessageBox("ConnectNamedPipe failed with %d./n", GetLastError());
       return 0;
      }
     }
     
     DWORD dwRet;
     BYTE btState = 0;
     while(1){
      DWORD dwResult = WaitForMultipleObjects(2,pDlg->m_hEvents,FALSE,INFINITE);
      
      if(0 == dwResult - WAIT_OBJECT_0){
       if(fPendingIO){
        fSuccess = GetOverlappedResult(
           hPipeInst, // handle to pipe
           &op, // OVERLAPPED structure
           &dwRet,            // bytes transferred
           FALSE);            // do not wait
        switch(btState){
        case CONNECTING_STATE:
         if (! fSuccess) {
          AfxMessageBox("Error %d./n", GetLastError());
          return 0;
         }
         btState = READING_STATE;
         break;

        case READING_STATE:
         if(!fSuccess || dwRet == 0){
          DisconnectNamedPipe(hPipeInst);
          return 0;
         }
         TRACE("Read bytes = %d/n",dwRet);
         TRACE("nX=%d,nY=%d,nColor=%d/n",PtColor.nX,PtColor.nY,PtColor.nColor);
         break;
        }
       }

       fSuccess = ReadFile( hPipeInst,&PtColor, sizeof(PT_COLOR),&dwBytesReaded,&op);
       if(fSuccess && dwBytesReaded != 0){
        fPendingIO = FALSE;
        TRACE("Read bytes = %d/n",dwBytesReaded);
        TRACE("nX=%d,nY=%d,nColor=%d/n",PtColor.nX,PtColor.nY,PtColor.nColor);
        continue;
       } 
       DWORD dwErr = GetLastError();
                if (! fSuccess && (dwErr == ERROR_IO_PENDING)) {
        fPendingIO = TRUE;
        continue;
                }
      
       
      }
      else{
       break;
      }
      
     }

     DisconnectNamedPipe(hPipeInst);

     TRACE("exit NamedPipeWorkThread/n");
     return 0;
    }客户端:typedef struct Tag_Pt_Color{
     int nX;
     int nY;
     int nColor;
    }PT_COLOR; LPTSTR lpszPipename = TEXT("////.//pipe//mynamedpipe");
     m_hPipe = CreateFile(
      lpszPipename,   // pipe name
      GENERIC_READ |  // read and write access
      GENERIC_WRITE,
      0,              // no sharing
      NULL,           // default security attributes
      OPEN_EXISTING,  // opens existing pipe
      0,              // default attributes
      NULL);          // no template file
     
     if(INVALID_HANDLE_VALUE == m_hPipe){
      AfxMessageBox("Create NamedPipe failed with %d./n");
      return;
     }

     DWORD dwMode = PIPE_READMODE_MESSAGE;
     BOOL fSuccess = SetNamedPipeHandleState(
      m_hPipe,    // pipe handle
      &dwMode,  // new pipe mode
      NULL,     // don't set maximum bytes
      NULL);    // don't set maximum time

     if ( ! fSuccess) {
      CString strMsg;
      strMsg.Format("SetNamedPipeHandleState failed. GLE=%d/n", GetLastError());
      AfxMessageBox(strMsg);
      return ;
     } PT_COLOR  ptColor;
     ptColor.nX = m_nX;
     ptColor.nY = m_nY;
     ptColor.nColor = m_nColor;

     DWORD dwBytesWritten;
     BOOL fSuccess = WriteFile(m_hPipe,&ptColor,sizeof(PT_COLOR),&dwBytesWritten,NULL);
     
     if (!fSuccess)
     {
      CString strMsg;
      strMsg.Format("WriteFile to pipe failed. GLE=%d/n", GetLastError());

      AfxMessageBox(strMsg);
      return ;
     }

    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/jiangdf/archive/2010/06/20/5681487.aspx

  • 相关阅读:
    C++宏定义详解
    编写Qt Designer自定义控件 MyPlugins
    关于MFC共享DLL的模块状态切换 .
    QT 与 MFC 的区别 .
    typedef
    C++ floor函数
    C++ floor函数 截断浮点数小数部分 转
    MFC的多国语言界面的实现 转
    新工作 Day16 周五
    新工作 Day15 周四
  • 原文地址:https://www.cnblogs.com/xtfnpgy/p/9285448.html
Copyright © 2011-2022 走看看