[zz]VC++下具名管道編程的原理及實現

來源:互聯網
上載者:User
管道(Pipe)實際是用於處理序間通訊的一段共用記憶體,建立管道的進程稱為管道伺服器,串連到一個管道的進程為管道客戶機。具名管道(Named Pipes)是在管道伺服器和一台或多台管道客戶機之間進行單向或雙向通訊的一種命名的管道。一個具名管道的所有執行個體共用同一個管道名,但是每一個執行個體均擁有獨立的緩衝與控制代碼,並且為客戶——服務通訊提供有一個分離的管道。執行個體的使用保證了多個管道客戶能夠在同一時間使用同一個具名管道。

  Microsoft Windows NT、Windows 2000、Windows 95以及Windows 98均提供對具名管道的支援(不包括Windows CE),但只有Windows NT和Windows 2000才支援伺服器端的具名管道技術。具名管道可以在同一台電腦的不同進程之間,或在跨越一個網路的不同電腦的不同進程之間進行有串連的可靠資料通訊,如果串連中斷,串連雙方都能立即收到串連斷開的資訊。命令管道是圍繞Windows檔案系統而設計的一種機制,採用的是具名管道檔案系統(Named Pipe File System, NPFS)介面。對資料的收發也採用檔案讀寫函數ReadFile()和WriteFile()來完成。在設計上,由於具名管道也利用了微軟網路提供者(MSNP)重新導向器,因此無需涉及底層的通訊協定細節。具名管道還充分利用了Windows NT及Windows 2000內建的安全特性,通訊的安全性相對較好。

  命名規範及通訊模式

  每一個具名管道都有一個唯一的名字以區分於存在於系統的命名物件列表中的其他具名管道。管道伺服器在調用CreateNamedPipe()函數建立具名管道的一個或多個執行個體時為其指定了名稱。對於管道客戶機,則是在調用CreateFile()或CallNamedPipe()函數以串連一個具名管道執行個體時對管道名進行指定。具名管道的命名規範與郵槽有些類似,對其標識也是採用的UNC格式:

//Server/Pipe/[Path]Name

  其中,第一部分//Server指定了伺服器的名字,具名管道服務即在此伺服器建立,其字串部分可表示為一個小數點(表示本機)、星號(當前網路欄位)、網域名稱或是一個真正的服務;第二部分/Pipe與郵槽的/Mailslot一樣是一個不可變化的寫入程式碼字串,以指出該檔案是從屬於NPFS;第三部分/[Path]Name則使應用程式可以唯一定義及標識一個具名管道的名字,而且可以設定多級目錄。

  具名管道提供了兩種基本的通訊模式:位元組模式和訊息模式。可在CreateNamePipe()建立具名管道時分別用PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE標誌進行設定。在位元組模式中,資訊以連續位元組流的形式在客戶與伺服器之間流動。這也就意味著,對於客戶機應用和伺服器應用,在任何一個特定的時間段內,都無法準確知道有多少位元組從管道中讀出或寫入。在這種通訊模式中,一方在向管道寫入某個數量的位元組後,並不能保證管道另一方能讀出等量的位元組。對於訊息模式,客戶機和伺服器則是通過一系列不連續的資料包進行資料的收發。從管道發出的每一條訊息都必須作為一條完整的訊息讀入。
 

使用具名管道

  管道伺服器首次調用CreateNamedPipe()函數時,使用nMaxInstance參數指定了能同時存在的管道執行個體的最大數目。伺服器可以重複調用CreateNamedPipe()函數去建立管道新的執行個體,直至達到設定的最大執行個體數。下面給出CreateNamedPipe()的函數原型:

HANDLE CreateNamedPipe(
 LPCTSTR lpName, // 指向管道名稱的指標
 DWORD dwOpenMode, // 管道開啟模式
 DWORD dwPipeMode, // 管道模式
 DWORD nMaxInstances, // 最大執行個體數
 DWORD nOutBufferSize, // 輸出緩衝大小
 DWORD nInBufferSize, // 輸入緩衝大小
 DWORD nDefaultTimeOut, // 逾時設定
 LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全屬性指標
);

  如果在已定義逾時值變為零以前,有一個執行個體管道可以使用,則建立成功並返回管道控制代碼,以此偵聽來自客戶機的串連請求。另一方面,客戶機通過函數WaitNamedPipe()使伺服器處理序等待來自客戶的執行個體串連。如果在逾時值變為零以前,有一個管道可供串連使用,則函數將成功返回,並通過調用CreateFile()或CallNamedPipe()來呼叫對伺服器的串連。此時伺服器將接受客戶的串連請求,成功建立串連,伺服器調用的等待客戶機建立串連的ConnectNamedPipe()函數也將成功返回。

  從調用時序上看,首先是客戶機通過WaitNamedPipe()使伺服器的CreateFile()在限時時間內建立執行個體成功,然後雙方通過ConnectNamedPipe()和CreateFile()成功串連,在返回用以通訊的檔案控制代碼後,客戶、服務雙方即可進行通訊。

  在建立了串連後,客戶機與伺服器即可通過ReadFile()和WriteFile()並利用得到的管道控制代碼,以檔案讀寫的形式彼此間進行資訊交換。 當客戶與伺服器的通訊結束,或是由於某種原因一方需要斷開時,由客戶機調用CloseFile()函數關閉開啟的管道控制代碼,伺服器隨即調用DisconnectNamedPipe()函數。當然,伺服器也可以通過單方面調用DisconnectNamedPipe()來終止串連。在終止串連後調用函數CloseHandle()來關閉此管道。下面給出的程式清單即是按照上述方法實現的具名管道伺服器和客戶機進行通訊的簡單程式實現代碼:
伺服器端:

m_hPipe = CreateNamedPipe("////.//Pipe//Test", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 1000, NULL); // 建立具名管道
if (m_hPipe == INVALID_HANDLE_VALUE)
 m_sMessage = "建立具名管道失敗!";
else{
 m_sMessage = "成功建立具名管道!";
 AfxBeginThread(ReadProc, this); // 開啟線程
}

  由於ConnectNamedPipe()函數在沒有客戶機串連到伺服器時會無限等待下去,因此為避免由此引起主線程的阻塞,為其開闢了一個子線程ReadProc:

UINT ReadProc(LPVOID lpVoid)
{
 char buffer[1024]; // 資料緩衝
 DWORD ReadNum;
 CServerView* pView = (CServerView*)lpVoid; // 擷取視控制代碼
 if (ConnectNamedPipe(pView->m_hPipe, NULL) == FALSE) // 等待客戶機的串連
 {
  CloseHandle(pView->m_hPipe); // 關閉管道控制代碼
  pView->m_sMessage = "與客戶機建立串連失敗!"; // 顯示資訊
  pView->Invalidate();
  return 0;
 }else{
  pView->m_sMessage = "與客戶機建立串連!"; // 顯示資訊
  pView->Invalidate();
 }
 // 從管道讀取資料
 if (ReadFile(pView->m_hPipe, buffer, sizeof(buffer), &ReadNum, NULL) == FALSE)
 {
  CloseHandle(pView->m_hPipe); // 關閉管道控制代碼
  pView->m_sMessage = "從管道讀取資料失敗!"; // 顯示資訊
  pView->Invalidate();
 } else {
  buffer[ReadNum] = '/0'; // 顯示接收到的資訊
  pView->m_sMessage = CString(buffer);
  pView->Invalidate();
 }
 return 1;
}

  在客戶同伺服器建立串連後,ConnectNamedPipe()才會返回,其下語句才得以執行。隨後的ReadFile()將負責把客戶寫入管道的資料讀取出來。在全部操作完成後,伺服器可以通過調用函數DisconnectNamedPipe()而終止串連:

if (DisconnectNamedPipe(m_hPipe) == FALSE) // 終止串連
 m_sMessage = "終止串連失敗!";
else
{
 CloseHandle(m_hPipe); // 關閉管道控制代碼
 m_sMessage = "成功終止串連!";
}

  客戶機端:

CString Message = "[測試資料,由客戶機發出]"; // 要發送的資料
DWORD WriteNum; // 發送的是資料長度
// 等待與伺服器的串連
if (WaitNamedPipe("////.//Pipe//Test", NMPWAIT_WAIT_FOREVER) == FALSE)
{
 m_sMessage = "等待串連失敗!"; // 顯示資訊
 Invalidate();
 return;
}
// 開啟已建立的管道控制代碼
HANDLE hPipe = CreateFile("////.//Pipe//Test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
 m_sMessage = "管道開啟失敗!"; // 顯示資訊
 Invalidate();
 return;
} else {
 m_sMessage = "成功開啟管道!"; // 顯示資訊
 Invalidate();
}
// 向管道寫入資料
if (WriteFile(hPipe, Message, Message.GetLength(), &WriteNum, NULL) == FALSE)
{
 m_sMessage = "資料寫入管道失敗!"; // 顯示資訊
 Invalidate();
} else {
 m_sMessage = "資料成功寫入管道!"; // 顯示資訊
 Invalidate();
}
CloseHandle(hPipe); // 關閉管道控制代碼

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.