程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> 進程間通信——命名管道和郵槽

進程間通信——命名管道和郵槽

編輯:關於C

 

前面介紹了進程間通信的兩種方法:剪貼板和匿名管道。這兩種進程間通信的方法只能在本地主機的進程之間通信。而匿名管道還限制通信的兩進程之間必須有父子關系。在開發網絡間不同進程之間相互通信的應用程序時,我們可以用命名管道和郵槽。這兩種方法不僅支持本地主機通信也支持網間進程通信。下面詳細介紹這兩種方法:

一、命名管道

    將命名管道作為網絡通信的方案時,他實際上建立了一個客戶機/服務器通信體系,並在其中可靠的傳輸數據。命名管道式圍繞Windows文件系統設計的一種機制,采用“命名管道文件系統借口”,因此客戶機和服務器可利用標准的Win32文件系統函數來進行數據的收發。在利用命名管道進行通信時,服務器是唯一一個有權建立命名管道的進程,可以接收客戶機的連接請求,客戶機只能同一個現有的命名管道客戶機建立連接。命名管道提供了兩種基本的通信模式:字節模式和消息模式。在實際編程過程中可以根據實際需要選擇不同的通信模式。利用命名管道進行通信可以按照下面的步驟進行:

(一)服務器端:

(1)創建命名管道

    在利用命名管道進行通信之前,肯定要先創建命名管道。創建命名管道需調用的函數為CreateNamedPipe(),該函數的創建一個命名管道的實例並返回該實例的句柄。該函數的第一個參數用指定的格式保存創建的管道的名字。該函數的餓第二個參數指定管道的進入模式、重疊模式、讀寫模式、安全屬性等信息,在下面的示例中我用的是雙向的進入模式(PIPE_ACCESS_DUPLEX),客戶機和服務器都可以從管道讀寫數據,重疊方式為可重疊的方式(FILE_FLAG_OVERLAPPED),指定重疊模式後,需要花費時間的線程會立即返回執行其他操作,耗費時間的線程會在後台完成。該函數的第四個參數指定該管道可創建的最大實例個數。指定該參數的原因是,一個命名管道的示例只能和一個客戶端通信,如果有多個客戶端要通過該命名管道和服務器通信,需要創建多個該命名管道的實例。創建命名管道的示例代碼如下:

 /***************************************************
                創建命名管道
 *******************************************************/
 hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
  0,1,1024,1024,0,NULL);
 if (INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox("創建命名管道失敗!");
  hPipe=NULL;
  //CloseHandle(hPipe);
  return;
 }

(2)等待客戶端連接請求的到來

    命名管道創建完成之後,服務器就可以等待客戶端的連接請求。等待客戶端的連接請求調用的函數為ConnectNamedPipe()該函數的的第二個參數是一個指向OVERLAPPED結構體的指針,該結構體包含用於異步執行I/O操作的信息。該結構體中我們感興趣的參數是一個指向事件對象的句柄,我們可以利用該事件對象來執行異步I/O操作。所以我們要先調用CreateEvent()創建一個人工重置的事件對象。當等待客戶端連接請求之後,我們就可以調用WaitForSingleObject()函數將該事件對象置為無信號狀態,以供下一次連接請求使用。實現代碼如下:

/*********************************************************
                創建人工重置的匿名事件對象
 *********************************************************/
 HANDLE hEvent;
 hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 if (!hEvent)
 {
  MessageBox(" 創建人工重置的匿名事件對象失敗!");
  CloseHandle(hEvent);
  hPipe=NULL;
  return;
 }
   /*********************************************************
                等待客戶端連接請求的到來
 *********************************************************/
 OVERLAPPED olp;
 ZeroMemory(&olp,sizeof(OVERLAPPED));
 olp.hEvent=hEvent;
 if(!ConnectNamedPipe(hPipe,&olp))
 {
  if (ERROR_IO_PENDING!=GetLastError())
  {
   MessageBox("等待客戶端連接請求失敗!");
   CloseHandle(hPipe);
   CloseHandle(hEvent);
   hPipe=NULL;
   
   return;
  }
 }
 if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
 {
  MessageBox("等待對象失敗!");
  CloseHandle(hPipe);
  CloseHandle(hEvent);
  hPipe=NULL;
  return;
 }
 CloseHandle(hEvent);

(3)往管道中執行讀寫操作

    由於命名管道式圍繞文件系統設計的,所以我們可以利用標准的Win32文件操作函數執行讀寫操作。示例代碼如下:

void CNamedPipeSrvView::OnPipRead()
{
 // TODO: Add your command handler code here
 char buf[200];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,200,&dwRead,NULL))
 {
  MessageBox("讀取匿名管道失敗!");
  return;
 }
 MessageBox(buf);
}


void CNamedPipeSrvView::OnPipWrite()
{
 // TODO: Add your command handler code here
 char buf[]="匿名管道測試程序!";
 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("寫匿名管道失敗!");
  return;
 }
}
(二)客戶端

    命名管道的實現比較簡單了,客戶端首先判斷是否有可利用的命名管道實例,如果有,然後打開管道就可以進行讀寫操作了。

(1)連接管道實例並打開管道

    WaitNamedPipe()函數可用來連接一個命名管實例,連接成功後,就可以調用CreateFile()函數打開管道,該函數可以指定管道進入的模式、文件屬性等。示例代碼如下:

**********************************************************
                判斷是否有可利用的命名管道實例
 ************************************************************/
 if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER))
 {
  MessageBox("當前沒有可利用的命名管道實例");
  return;
 }
 /**********************************************************
                打開命名管道
 ************************************************************/
 hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ|GENERIC_WRITE,
      0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if (INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox("打開管道失敗!");
  hPipe=NULL;
  return;
 }

(2)往管道進行讀寫操作

void CNamedPipeCltView::OnPipRead()
{
 // TODO: Add your command handler code here
 char buf[200];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,200,&dwRead,NULL))
 {
  MessageBox("讀取匿名管道失敗!");
  return;
 }
 MessageBox(buf);
}


void CNamedPipeCltView::OnPipWrite()
{
 // TODO: Add your command handler code here
 char buf[]="乘風736博客園 http://www.cnblogs.com/chengfeng736";

 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("寫匿名管道失敗!");
  return;
 }
}

運行結果如下:

\

這樣,一個通過命名管道進行通信的服務器和客戶端程序就設計好了。兩個進程就可以實現通信了。

二 、郵槽

    郵槽是基於廣播模式的單向通信方式,服務器只能從郵槽讀取數據,客戶端只能往郵槽寫入數據,而且利用郵槽通信的信息量不能太大。下面介紹利用郵槽進行進程通信的過程:

(一)服務器

(1)創建油槽

    創建油槽可調用CreateMailslot()函數實現,該函數的第一個參數按照指定格式指定油槽的名字,第三個參數指定讀操作等待的時間。下面的示例程序我設定讀操作一直等待(MAILSLOT_WAIT_FOREVER),只到接收到數據為止。示例代碼如下:

HANDLE hMailslot;
 hMailslot=CreateMailslot("\\\\.\\mailslot\\MyMailslot",0,MAILSLOT_WAIT_FOREVER,NULL);
 if (INVALID_HANDLE_VALUE==hMailslot)
 {
  MessageBox("創建郵槽失敗!");
  CloseHandle(hMailslot);
  return;
 }

(2)讀取數據

DWORD dwRead;
 char buf[200];
 if(!ReadFile(hMailslot,buf,200,&dwRead,NULL))
 {
  MessageBox("讀取數據失敗!");
  CloseHandle(hMailslot);
  return;
 }
 MessageBox(buf);
 CloseHandle(hMailslot);

(二)客戶端

(1)打開油槽

CreateFile()函數不僅可以打開文件、管道還可以打開油槽。示例代碼如下:

HANDLE hMailslot;
 hMailslot=CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE,FILE_SHARE_READ,
  NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if (INVALID_HANDLE_VALUE==hMailslot)
 {
  MessageBox("打開郵槽失敗!");
  CloseHandle(hMailslot);
  return;
 }

(2)寫入數據

char buf[]="使用郵槽進行進程間通信\r\n乘風736博客園 http://www.cnblogs.com/chengfeng736";

  DWORD dwWrite;
 if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,0))
 {
  MessageBox("寫入數據失敗!");
  CloseHandle(hMailslot);
  return;
 }
 CloseHandle(hMailslot);

運行結果如下:

\

使用油槽通信的實現是很簡單的。油槽只能單向通信,如果要實現雙向通信,可以在客戶端和服務器分別實現讀寫操作就可以了

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved