zoukankan      html  css  js  c++  java
  • 进程间通信之管道

    命名管道更加高级。它由一个名字来标识,以使得客户端和服务端应用程序可以通过它进行彼此通信。而且win32命名管道甚至可以在不同系统的进程间使用。命名管道有时候也被称为fifo。有了命名管道后。一个进程可以把数据放到管道中。另一个知道管道名字的进程把数据取走。命名管道与其他交换数据方式不同的地方在于。如果进程不知道这个管道的名字就不可能把数据取走。

    管道实际是用于进程间通信的一段共享内存。创建管道的进程称为管道服务器。连接到一个管道的进程为管道客户机。可以用以下函数创建管道。

    Handle CreateNamedPipe(LPCTSTR lpName,DWORD dwOpenMode,DWord dwPipeMode)

    lpName命名规范:\\[host_name]\pipe\[Path]Name

    比如:LPCSTR szPipeName=TEXT("\\\\.\\pipe\\ssnp\\");

    第一部分\\[host_name]指定了服务器的名字。命名管道服务即在此服务器创建。其字符串部分可表示为小数点(本机),星号(当前网络字段),域名或是一个真正的服务。第二部分"\pipe"是一个硬编码字符串,第三部分为命名管道的名字。而且可以设置多级目录。

    dwOpenMode:管道创建方式。可以是下面值的组合。

    pipe_access_inbound:管道只能用作接收数据。

    pipe_Access_outbound:管道只能用作发送数据。

    pipe_Access_Duplex:管道即可以发送也可以接收数据。

    上面这三个值只能够取其中一个。同时也可以包括以下一个或两个标示

    File_flag_write_through:管道用于同步发送和接收数据。在系统内部对于命名管道的处理上不经过缓冲区并能直接发送。并且只有在数据被发送到目标地址时发送函数才会返回。

    file_flag_overlapped管道可以用于异步输入和输出。

    dwPipeMode:命名管道模式:

     通信的实现流程

    1:连接建立

    服务端通过函数CreateNamedPipe创建一个命名管道的实例并返回用于今后操作的句柄,或为已存在的管道创建新的实例。如果在已定义超时值变为0以前。有一个实例管道可以使用。则创建成功并返回管道句柄。并用以侦听来自客户端的连接请求。该功能通过connectnamepipe实现。另一方面。客户端通过函数waitnamepipe使服务进程等待来自客户的实例连接。如果在超时值变为0以前。有一个管道可以为连接使用。则waitnamedpipe返回true.并通过调用createfile或callnamedpipe来呼叫对服务端的连接。此时服务端将接受客户端的连接请求。成功建立连接。服务端connectnamepipe返回true.客户端createFile返回一个指向管道文件的句柄。

    从时序上讲。首先是客户端通过waitnamedpipe使服务端的createfile在限定时间内创建实例成功。然后双方通过connectnamedpipe和createfile成功连接。并返回用于通信的文件句柄。此时双方即可进行通信。

    2:通信实现

    建立连接之后。客户和服务端可以通过得到的管道文件句柄利用readfile和writefile进行彼此间的信息交换。

    3:连接终止

    客户端应调用closefile而服务端应接着调用disconnectnamedpipe。当然服务端也可通过单方面调用disconnectnamedpipe终止连接。最后应调用closehandle来关闭该管道。

    实例解析

    服务端:

    (1)定义一个定时器不断的更新收到的消息。

    m_strPipe = thePipe.GetRequest();
        UpdateData(FALSE);
     (2)创建命名管道,并创建线程

    m_strReply=strReply;
     LPCSTR szPipeName=TEXT("\\\\.\\pipe\\ssnp\\"); 
     // 为接收消息创建命名管道.
     m_hPipe=CreateNamedPipe(szPipeName,
      PIPE_ACCESS_DUPLEX|FILE_FLAG_WRITE_THROUGH,
      // 阻塞模式.
      PIPE_WAIT|PIPE_TYPE_BYTE,
      PIPE_UNLIMITED_INSTANCES,
      128,128,NULL,NULL); 
     // 检查是否命名管道被创建.
     if (m_hPipe == INVALID_HANDLE_VALUE)
     {
      TRACE("Unable to create a named pipe.\n");
      return;
     }
     m_pThread=AfxBeginThread(ServerReadProc, this); // 启动线程.

    (3)等待客户端连接上并读写管道。

    为避免在服务程序出现阻塞现象。创建了一个进程。

    UINT CMyPipe::ServerReadProc(LPVOID lpVoid)
    {
     DWORD dwNumBytesRead,dwNumBytesWrite;
     char toDisptxt[80];
     
     // 允许客户连接命名管道,如果不成功,终止.
     TRACE("Waiting for connection... \n");
     
     CMyPipe *Parent= (CMyPipe*)lpVoid;
     //ConnectNmaepipe:服务进程准备好一个连接到客户进程的管道。并等待一个客户进程连接上为至;

    //当没有客户程序连接时。服务程序就停止在此处等待外界的连接。只有在接收到来自客户端的连接时。程序才继续向下执行。对于这种情况。如果不采用线程。服务程序窗口就不能显示出来。连接成功后。用readfile和writefile分别从管道里读取数据和向管道里写入数据。
     if(!ConnectNamedPipe(Parent->m_hPipe, (LPOVERLAPPED) NULL))
     {
      TRACE("Unable to connect a named pipe. Error: %d\n",GetLastError());
      CloseHandle(Parent->m_hPipe);
      return 1;
     }
     // 反复检查消息直到程序终止.
     while(1)
     {
      // 读取消息并检查读取数据是否成功.
      if (!ReadFile(Parent->m_hPipe, toDisptxt,sizeof(toDisptxt),
       &dwNumBytesRead, (LPOVERLAPPED) NULL))
      {
       TRACE("Unable to read from named pipe. Error: %d\n" ,GetLastError());
       CloseHandle(Parent->m_hPipe);
       return 1;
      }
      else
      {
       // 保存接收的字符串.
       Parent->m_strRequest=toDisptxt;
       strcpy(toDisptxt,Parent->m_strReply);
       // 写回一个字符串.
       WriteFile(Parent->m_hPipe, toDisptxt,sizeof(toDisptxt),
            &dwNumBytesWrite, (LPOVERLAPPED) NULL);
      }
     }

     return 0;
    }

    客户端:

    (1):在命名管道服务程序启动后。可以用createfile打开命名管道。就像创建文件一样。当文件名必须与服务端创建命名管道的名称相同。否则不能与服务相连。

    LPCSTR szPipeName=TEXT("\\\\.\\pipe\\ssnp\\"); 
     m_hPipe=CreateFile(szPipeName,
      GENERIC_READ|GENERIC_WRITE,
      FILE_SHARE_READ| FILE_SHARE_WRITE,NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_ARCHIVE|FILE_FLAG_WRITE_THROUGH,
      // FILE_FLAG_WRITE_THROUGH 设定阻塞.
      NULL); 
     // 检查并判别命名管道文件是否被打开,如果没有被打开,终止程序.
     if (m_hPipe == INVALID_HANDLE_VALUE)
      TRACE("Unable to create a named pipe. Error: %d\n",GetLastError());

    (2):向管道写入数据读出数据。

    m_strRequest=strRequest;
     char toSendtxt[80];

     // 反复发送消息直到程序终止.
     while(1)
     {
      TRACE("Sending...\n");
      DWORD dwNumBytesWritten,dwNumBytesRead; 
      strcpy(toSendtxt,m_strRequest);
      // 向管道写入消息.
      if (!WriteFile(m_hPipe,toSendtxt, (DWORD)sizeof(toSendtxt),
       &dwNumBytesWritten,(LPOVERLAPPED) NULL))
      {
       // 如果向命名管道写入时出现错误,终止程序.
       TRACE("Unable to write to named pipe. Error: %d\n",GetLastError());
       CloseHandle(m_hPipe);
      }
      else{
       ReadFile(m_hPipe,toSendtxt, (DWORD)sizeof(toSendtxt),
        &dwNumBytesRead,(LPOVERLAPPED) NULL);
       m_strReply=toSendtxt;
       break;
      }
      // 在再次发送消息前等待.
            Sleep(4800);
     }

  • 相关阅读:
    【干货】国外程序员整理的 C++ 资源大全–日常工作,我觉得用处确实很大,所以分享
    android实例 listview与sqlite数据绑定
    Android整理:SQlite数据库的使用以及通过listView显示数据
    Android之SimpleAdapter简单实例和SimpleAdapter参数说明
    LinearLayout里面的空间居中对齐
    context.startActivity(Intent intent)方法启动activity
    Context.startActivity出现AndroidRuntimeException
    ListFragment 使用ListView and 自定义Adapter
    匿名类型
    元组Tuple
  • 原文地址:https://www.cnblogs.com/lzjsky/p/1774537.html
Copyright © 2011-2022 走看看