VC++下命名管道编程的原理及实现
概述
管道(Pipe)實(shí)際是用于進(jìn)程間通信的一段共享內(nèi)存,創(chuàng)建管道的進(jìn)程稱(chēng)為管道服務(wù)器,連接到一個(gè)管道的進(jìn)程為管道客戶(hù)機(jī)。命名管道(Named Pipes)是在管道服務(wù)器和一臺(tái)或多臺(tái)管道客戶(hù)機(jī)之間進(jìn)行單向或雙向通信的一種命名的管道。一個(gè)命名管道的所有實(shí)例共享同一個(gè)管道名,但是每一個(gè)實(shí)例均擁有獨(dú)立的緩存與句柄,并且為客戶(hù)——服務(wù)通信提供有一個(gè)分離的管道。實(shí)例的使用保證了多個(gè)管道客戶(hù)能夠在同一時(shí)間使用同一個(gè)命名管道。
Microsoft Windows NT、Windows 2000、Windows 95以及Windows 98均提供對(duì)命名管道的支持(不包括Windows CE),但只有Windows NT和Windows 2000才支持服務(wù)器端的命名管道技術(shù)。命名管道可以在同一臺(tái)計(jì)算機(jī)的不同進(jìn)程之間,或在跨越一個(gè)網(wǎng)絡(luò)的不同計(jì)算機(jī)的不同進(jìn)程之間進(jìn)行有連接的可靠數(shù)據(jù)通信,如果連接中斷,連接雙方都能立即收到連接斷開(kāi)的信息。命令管道是圍繞Windows文件系統(tǒng)而設(shè)計(jì)的一種機(jī)制,采用的是命名管道文件系統(tǒng)(Named Pipe File System, NPFS)接口。對(duì)數(shù)據(jù)的收發(fā)也采用文件讀寫(xiě)函數(shù)ReadFile()和WriteFile()來(lái)完成。在設(shè)計(jì)上,由于命名管道也利用了微軟網(wǎng)絡(luò)提供者(MSNP)重定向器,因此無(wú)需涉及底層的通信協(xié)議細(xì)節(jié)。命名管道還充分利用了Windows NT及Windows 2000內(nèi)建的安全特性,通信的安全性相對(duì)較好。
命名規(guī)范及通信模式
每一個(gè)命名管道都有一個(gè)唯一的名字以區(qū)分于存在于系統(tǒng)的命名對(duì)象列表中的其他命名管道。管道服務(wù)器在調(diào)用CreateNamedPipe()函數(shù)創(chuàng)建命名管道的一個(gè)或多個(gè)實(shí)例時(shí)為其指定了名稱(chēng)。對(duì)于管道客戶(hù)機(jī),則是在調(diào)用CreateFile()或CallNamedPipe()函數(shù)以連接一個(gè)命名管道實(shí)例時(shí)對(duì)管道名進(jìn)行指定。命名管道的命名規(guī)范與郵槽有些類(lèi)似,對(duì)其標(biāo)識(shí)也是采用的UNC格式:
| \\Server\Pipe\[Path]Name |
其中,第一部分\\Server指定了服務(wù)器的名字,命名管道服務(wù)即在此服務(wù)器創(chuàng)建,其字串部分可表示為一個(gè)小數(shù)點(diǎn)(表示本機(jī))、星號(hào)(當(dāng)前網(wǎng)絡(luò)字段)、域名或是一個(gè)真正的服務(wù);第二部分\Pipe與郵槽的\Mailslot一樣是一個(gè)不可變化的硬編碼字串,以指出該文件是從屬于NPFS;第三部分\[Path]Name則使應(yīng)用程序可以唯一定義及標(biāo)識(shí)一個(gè)命名管道的名字,而且可以設(shè)置多級(jí)目錄。
命名管道提供了兩種基本的通信模式:字節(jié)模式和消息模式。可在CreateNamePipe()創(chuàng)建命名管道時(shí)分別用PIPE_TYPE_BYTE和PIPE_TYPE_MESSAGE標(biāo)志進(jìn)行設(shè)定。在字節(jié)模式中,信息以連續(xù)字節(jié)流的形式在客戶(hù)與服務(wù)器之間流動(dòng)。這也就意味著,對(duì)于客戶(hù)機(jī)應(yīng)用和服務(wù)器應(yīng)用,在任何一個(gè)特定的時(shí)間段內(nèi),都無(wú)法準(zhǔn)確知道有多少字節(jié)從管道中讀出或?qū)懭搿T谶@種通信模式中,一方在向管道寫(xiě)入某個(gè)數(shù)量的字節(jié)后,并不能保證管道另一方能讀出等量的字節(jié)。對(duì)于消息模式,客戶(hù)機(jī)和服務(wù)器則是通過(guò)一系列不連續(xù)的數(shù)據(jù)包進(jìn)行數(shù)據(jù)的收發(fā)。從管道發(fā)出的每一條消息都必須作為一條完整的消息讀入。
使用命名管道
管道服務(wù)器首次調(diào)用CreateNamedPipe()函數(shù)時(shí),使用nMaxInstance參數(shù)指定了能同時(shí)存在的管道實(shí)例的最大數(shù)目。服務(wù)器可以重復(fù)調(diào)用CreateNamedPipe()函數(shù)去創(chuàng)建管道新的實(shí)例,直至達(dá)到設(shè)定的最大實(shí)例數(shù)。下面給出CreateNamedPipe()的函數(shù)原型:
| HANDLE CreateNamedPipe(LPCTSTR lpName, // 指向管道名稱(chēng)的指針DWORD dwOpenMode, // 管道打開(kāi)模式DWORD dwPipeMode, // 管道模式DWORD nMaxInstances, // 最大實(shí)例數(shù)DWORD nOutBufferSize, // 輸出緩存大小DWORD nInBufferSize, // 輸入緩存大小DWORD nDefaultTimeOut, // 超時(shí)設(shè)置LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全屬性指針 ); |
如果在已定義超時(shí)值變?yōu)榱阋郧?#xff0c;有一個(gè)實(shí)例管道可以使用,則創(chuàng)建成功并返回管道句柄,以此偵聽(tīng)來(lái)自客戶(hù)機(jī)的連接請(qǐng)求。另一方面,客戶(hù)機(jī)通過(guò)函數(shù)WaitNamedPipe()使服務(wù)器進(jìn)程等待來(lái)自客戶(hù)的實(shí)例連接。如果在超時(shí)值變?yōu)榱阋郧?#xff0c;有一個(gè)管道可供連接使用,則函數(shù)將成功返回,并通過(guò)調(diào)用CreateFile()或CallNamedPipe()來(lái)呼叫對(duì)服務(wù)器的連接。此時(shí)服務(wù)器將接受客戶(hù)的連接請(qǐng)求,成功建立連接,服務(wù)器調(diào)用的等待客戶(hù)機(jī)建立連接的ConnectNamedPipe()函數(shù)也將成功返回。
從調(diào)用時(shí)序上看,首先是客戶(hù)機(jī)通過(guò)WaitNamedPipe()使服務(wù)器的CreateFile()在限時(shí)時(shí)間內(nèi)創(chuàng)建實(shí)例成功,然后雙方通過(guò)ConnectNamedPipe()和CreateFile()成功連接,在返回用以通信的文件句柄后,客戶(hù)、服務(wù)雙方即可進(jìn)行通信。
在建立了連接后,客戶(hù)機(jī)與服務(wù)器即可通過(guò)ReadFile()和WriteFile()并利用得到的管道句柄,以文件讀寫(xiě)的形式彼此間進(jìn)行信息交換。 當(dāng)客戶(hù)與服務(wù)器的通信結(jié)束,或是由于某種原因一方需要斷開(kāi)時(shí),由客戶(hù)機(jī)調(diào)用CloseFile()函數(shù)關(guān)閉打開(kāi)的管道句柄,服務(wù)器隨即調(diào)用DisconnectNamedPipe()函數(shù)。當(dāng)然,服務(wù)器也可以通過(guò)單方面調(diào)用DisconnectNamedPipe()來(lái)終止連接。在終止連接后調(diào)用函數(shù)CloseHandle()來(lái)關(guān)閉此管道。下面給出的程序清單即是按照上述方法實(shí)現(xiàn)的命名管道服務(wù)器和客戶(hù)機(jī)進(jìn)行通信的簡(jiǎn)單程序?qū)崿F(xiàn)代碼:
服務(wù)器端:
| m_hPipe = CreateNamedPipe("\\\\.\\Pipe\\Test", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 0, 0, 1000, NULL); // 創(chuàng)建命名管道 if (m_hPipe == INVALID_HANDLE_VALUE)m_sMessage = "創(chuàng)建命名管道失敗!"; else{m_sMessage = "成功創(chuàng)建命名管道!";AfxBeginThread(ReadProc, this); // 開(kāi)啟線程 } |
由于ConnectNamedPipe()函數(shù)在沒(méi)有客戶(hù)機(jī)連接到服務(wù)器時(shí)會(huì)無(wú)限等待下去,因此為避免由此引起主線程的阻塞,為其開(kāi)辟了一個(gè)子線程ReadProc:
| UINT ReadProc(LPVOID lpVoid) {char buffer[1024]; // 數(shù)據(jù)緩存DWORD ReadNum;CServerView* pView = (CServerView*)lpVoid; // 獲取視句柄if (ConnectNamedPipe(pView->m_hPipe, NULL) == FALSE) // 等待客戶(hù)機(jī)的連接{ CloseHandle(pView->m_hPipe); // 關(guān)閉管道句柄 pView->m_sMessage = "與客戶(hù)機(jī)建立連接失敗!"; // 顯示信息 pView->Invalidate(); return 0;}else{ pView->m_sMessage = "與客戶(hù)機(jī)建立連接!"; // 顯示信息 pView->Invalidate();}// 從管道讀取數(shù)據(jù)if (ReadFile(pView->m_hPipe, buffer, sizeof(buffer), &ReadNum, NULL) == FALSE){ CloseHandle(pView->m_hPipe); // 關(guān)閉管道句柄 pView->m_sMessage = "從管道讀取數(shù)據(jù)失敗!"; // 顯示信息 pView->Invalidate();} else { buffer[ReadNum] = '\0'; // 顯示接收到的信息 pView->m_sMessage = CString(buffer); pView->Invalidate();}return 1; } |
在客戶(hù)同服務(wù)器建立連接后,ConnectNamedPipe()才會(huì)返回,其下語(yǔ)句才得以執(zhí)行。隨后的ReadFile()將負(fù)責(zé)把客戶(hù)寫(xiě)入管道的數(shù)據(jù)讀取出來(lái)。在全部操作完成后,服務(wù)器可以通過(guò)調(diào)用函數(shù)DisconnectNamedPipe()而終止連接:
| if (DisconnectNamedPipe(m_hPipe) == FALSE) // 終止連接m_sMessage = "終止連接失敗!"; else {CloseHandle(m_hPipe); // 關(guān)閉管道句柄m_sMessage = "成功終止連接!"; } |
客戶(hù)機(jī)端:
| CString Message = "[測(cè)試數(shù)據(jù),由客戶(hù)機(jī)發(fā)出]"; // 要發(fā)送的數(shù)據(jù) DWORD WriteNum; // 發(fā)送的是數(shù)據(jù)長(zhǎng)度 // 等待與服務(wù)器的連接 if (WaitNamedPipe("\\\\.\\Pipe\\Test", NMPWAIT_WAIT_FOREVER) == FALSE) {m_sMessage = "等待連接失敗!"; // 顯示信息Invalidate();return; } // 打開(kāi)已創(chuàng)建的管道句柄 HANDLE hPipe = CreateFile("\\\\.\\Pipe\\Test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hPipe == INVALID_HANDLE_VALUE) {m_sMessage = "管道打開(kāi)失敗!"; // 顯示信息Invalidate();return; } else {m_sMessage = "成功打開(kāi)管道!"; // 顯示信息Invalidate(); } // 向管道寫(xiě)入數(shù)據(jù) if (WriteFile(hPipe, Message, Message.GetLength(), &WriteNum, NULL) == FALSE) {m_sMessage = "數(shù)據(jù)寫(xiě)入管道失敗!"; // 顯示信息Invalidate(); } else {m_sMessage = "數(shù)據(jù)成功寫(xiě)入管道!"; // 顯示信息Invalidate(); } CloseHandle(hPipe); // 關(guān)閉管道句柄 |
原文:VC++下命名管道編程的原理及實(shí)現(xiàn)?
總結(jié)
以上是生活随笔為你收集整理的VC++下命名管道编程的原理及实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: UNICODE转多字节
- 下一篇: s3c2440移植MQTT