一种网络进程间通信的方式—— 管道
?
一種網(wǎng)絡(luò)進(jìn)程間通信的方式—— 管道
摘要: 文章主要介紹了計(jì)算機(jī)網(wǎng)絡(luò)進(jìn)程間通信的必要性以及進(jìn)程間通信所采用的幾種方式,重點(diǎn)說明了管道通信的原理及命名管道的實(shí)現(xiàn)方法。
關(guān)鍵詞:管道 命名管道 進(jìn)程
?
一、概述
進(jìn)程間通信的主要目的是實(shí)現(xiàn)同一計(jì)算機(jī)系統(tǒng)內(nèi)部相互協(xié)作的進(jìn)程之間的數(shù)據(jù)共享與信息交換。由于這些進(jìn)程處于同一應(yīng)用軟件和硬件環(huán)境下,因而利用操作系統(tǒng)提供的編程接口,用戶可以方便地在程序中實(shí)現(xiàn)這種通信。而應(yīng)用程序間通信的主要目的是實(shí)現(xiàn)不同計(jì)算機(jī)系統(tǒng)中相互協(xié)作的應(yīng)用程序之間的數(shù)據(jù)共享與信息交換。由于應(yīng)用程序分別運(yùn)行在不同計(jì)算機(jī)系統(tǒng)中,所以它們之間要通過網(wǎng)絡(luò)間的協(xié)議才能實(shí)現(xiàn)數(shù)據(jù)共享與信息交換。進(jìn)程間的通信和應(yīng)用程序間的通信,以及它們相應(yīng)的實(shí)現(xiàn)技術(shù)既有許多相同之處,也有各自的特點(diǎn),因?yàn)榧词故峭活愋偷耐ㄐ乓矔卸喾N實(shí)現(xiàn)的方法,以適應(yīng)不同情況的需要。
二、 進(jìn)程間通信的必要性
用戶提交給計(jì)算機(jī)的任務(wù)最終都是通過一個(gè)個(gè)進(jìn)程來完成的。在一組并發(fā)進(jìn)程中的任何兩個(gè)進(jìn)程之間,如果都不存在公共變量,則稱該組進(jìn)程為不相交的。在不相交的進(jìn)程組中,每個(gè)進(jìn)程都獨(dú)立于其它進(jìn)程,它的運(yùn)行環(huán)境與順序程序一樣,而且也不為別的進(jìn)程所改變,其運(yùn)行的結(jié)果是確定的,不會發(fā)生與時(shí)間相關(guān)的錯誤。
但是,在實(shí)際中,并發(fā)進(jìn)程的各個(gè)進(jìn)程之間并不是完全互相獨(dú)立的,它們之間往往存在著相互制約的關(guān)系。進(jìn)程之間的相互制約關(guān)系表現(xiàn)為以下兩種方式:
● 間接相互制約 指共享CPU方式;
● 直接相互制約 指競爭和協(xié)作方式。
其中,競爭指進(jìn)程對共享資源的競爭。為保證進(jìn)程互斥地訪問共享資源,各進(jìn)程必須互斥地進(jìn)入各自的臨界段。
協(xié)作指進(jìn)程之間交換數(shù)據(jù)。為完成一個(gè)共同任務(wù)而同時(shí)運(yùn)行的一組進(jìn)程稱為同組進(jìn)程,它們之間必須交換數(shù)據(jù),以達(dá)到協(xié)作完成任務(wù)的目的。
共享CPU問題由操作系統(tǒng)的進(jìn)程調(diào)度來解決,進(jìn)程間的競爭和協(xié)作由進(jìn)程間的通信來完成。
Win32下提供的進(jìn)程間通信方式有:剪貼板、COM/DCOM、DDE、文件映射、郵件槽、管道、遠(yuǎn)地過程調(diào)用、網(wǎng)絡(luò)套接字和消息。
三、無名管道和命名管道
無名管道實(shí)際上是內(nèi)存中的一個(gè)臨時(shí)存儲區(qū),它由系統(tǒng)安全控制,并且獨(dú)立于創(chuàng)建它的進(jìn)程的內(nèi)存區(qū)。管道對數(shù)據(jù)采用先進(jìn)先出方式管理,并嚴(yán)格按順序操作,如不能對管道進(jìn)行搜索,管道中的信息只能讀一次。
無名管道只能用于兩個(gè)相互協(xié)作的進(jìn)程之間的通信,并且訪問無名管道的進(jìn)程必須有共同的祖先。
命名管道的操作與無名管道類似,不同的地方在于使用有名管道的進(jìn)程不需要具有共同的祖先,其它進(jìn)程,只要知道該管道的名字,就可以訪問它。管道非常適合進(jìn)程之間快速交換信息。
四、命名管道的連接和通訊方式
在服務(wù)器端第一次創(chuàng)建命名管道后等待連接,當(dāng)客戶端連接成功后服務(wù)器端的命名管道就用作通訊用途。如果需要再次等待連接,服務(wù)器端就需要再次打開命名管道(創(chuàng)建一個(gè)命名管道的實(shí)例)并等待連接。
對于客戶端每次打開命名管道后可建立與服務(wù)器間的連接,然后就可以利用命名管道進(jìn)行通信,如果需要建立第二個(gè)連接,則需要再次打開管道并再次建立連接。
創(chuàng)建命名管道時(shí)需要指定一個(gè)主機(jī)名和管道名,客戶端可以采用以下格式完成:
\\[host_name]\pipe\[pipe_name]\;也可以是:
\\.\pipe\pipe_name\(其中:. 表示本機(jī))。
而服務(wù)器端只能夠以指定本機(jī)作為主機(jī)名,即只能使用 \\.\ pipe_name\格式。此外,需要注意的是:在同一主機(jī)上的管道名稱是唯一的,一個(gè)命名管道一旦被創(chuàng)建,就不允許再創(chuàng)建相同名稱的管道。
服務(wù)器方通過下列方式創(chuàng)建命名管道和打開已經(jīng)存在的命名管道:
HANDLE CreateNamedPipe(
LPCTSTR lpName, //管道
DWORD dwOpenMode, // 打開方式
DWORD dwPipeMode, // 管道類型
DWORD nMaxInstances, // 管道的最大數(shù)量
DWORD nOutBufferSize, // 寫緩沖區(qū)大小
DWORD nInBufferSize, // 讀緩沖區(qū)大小
DWORD nDefaultTimeOut, // 最長的等待時(shí)間
LPSECURITY_ATTRIBUTES lpSecurityAttributes // 安全屬性
);
其中,lpName為管道名稱,dwOpenMode為創(chuàng)建方式,可以是下面值的組合:
PIPE_ACCESS_INBOUND:管道只能用作接收數(shù)據(jù)。
PIPE-ACCESS-OUTBOUND:管道只能用作發(fā)送數(shù)據(jù)。
PIPE-ACCESS-DUPLEX:管道既可以發(fā)送也可以接收數(shù)據(jù)(只能取三個(gè)中的一個(gè))。
FILE-FLAG-WRITE-THROUGH:管道用于同步發(fā)送和接收數(shù)據(jù),只有當(dāng)數(shù)據(jù)被發(fā)送到目標(biāo)地址時(shí),發(fā)送函數(shù)才會返回。如果不設(shè)置這個(gè)參數(shù),那么在系統(tǒng)內(nèi)部對于命名管道的處理上可能會因?yàn)闇p少網(wǎng)絡(luò)負(fù)荷而在數(shù)據(jù)積累到一定量時(shí)才發(fā)送,并且對于發(fā)送函數(shù)的調(diào)用會馬上返回。
FILE-FLAG-OVERLAPPED:管道可以用于異步輸入和輸出,異步讀寫的有關(guān)方法和文件異步讀寫是相同的。
dwPipeMode指定管道類型,可以是下面值的組合:
PIPE-TYPE-BYTE:數(shù)據(jù)在通過管道發(fā)送時(shí)作為字節(jié)流發(fā)送,不能與PIPE-READMODE-MESSAGE共用。
PIPE-TYPE-MESSAGE:數(shù)據(jù)在通過管道發(fā)送時(shí)作為消息發(fā)送,不能與PIPE-READMODE-BYTE共用。
PIPE-READMODE-BYTE:在接收數(shù)據(jù)時(shí)接收字節(jié)流。
PIPE-READMODE-MESSAGE:在接收數(shù)據(jù)時(shí)接收消息。
PIPE-WAIT:使用等待模式,在讀、寫和建立連接時(shí)都需要管道的另一方完成相應(yīng)動作后才會返回。
PIPE-NOWAIT:使用非等待模式,在讀、寫和建立連接時(shí)不需要管道的另一方完成相應(yīng)動作后就可立即返回。
nMaxInstances為管道的最大數(shù)量,在第一次建立服務(wù)器方管道時(shí),這個(gè)參數(shù)表明該管道可以同時(shí)存在的數(shù)量。PIPE- UNLIMITED-INSTANCES表明不對數(shù)量進(jìn)行限制。nOutBufferSize和nInBufferSize表示緩沖區(qū)的大小。nDefaultTimeOut表示在等待連接時(shí)最長的等待時(shí)間(以毫秒為單位)。如果在創(chuàng)建時(shí)設(shè)置為NMPWAIT-USE-DEFAULT-WAIT,表明無限制的等待,而以后服務(wù)器方的其他管道實(shí)例也需要設(shè)置相同的值。lpSecurityAttributes為安全屬性,一般設(shè)置為NULL。如果創(chuàng)建或打開失敗,則返回INVALID-HANDLE-VALUE??梢酝ㄟ^GetLastError查到錯誤。
客戶方通過下列方式創(chuàng)建客戶端命名管道:
HANDLE CreateFile(
LPCTSTR lpFileName, // 文件名
DWORD dwDesiredAccess, // 存取方式
DWORD dwShareMode, // 共享方式
LPSECURITY-ATTRIBUTES lpSecurityAttributes, // 安全屬性
DWORD dwCreationDisposition, // 創(chuàng)建方式
DWORD dwFlagsAndAttributes, // 文件屬性
HANDLE hTemplateFile // 臨時(shí)文件據(jù)柄
);
其中,CreateFile可以有很多用途,可以用來創(chuàng)建文件、管道、郵件槽、目錄等,這里介紹用CreateFile打開客戶端命名管道;lpFileName用于指明管道名稱;dwDesiredAccess用于表明使用方式,可以使用下面的值:
GENERIC-READ:打開一個(gè)只用于讀的管道。
GENERIC-WRITE:打開一個(gè)只用于寫的管道。
GENERIC-READ | GENERIC-WRITE:打開一個(gè)用于讀和寫的管道。
dwShareMode指定共享方式,一般指定為0;lpSecurityAttributes為安全屬性,一般設(shè)置為NULL; dwCreationDisposition設(shè)置為OPEN_EXISTING; dwFlagsAndAttributes設(shè)置為FILE-ATTRIBUTE-NORMAL, 還可以設(shè)置為FILE-FLAG-OVERLAPPED來進(jìn)行異步通訊;hTemplateFile設(shè)置為NULL。如果打開失敗,則返回INVALID-HANDLE-VALUE,并 可以通過GetLastError找到存在的錯誤。
此外客戶方可以利用下列方式創(chuàng)建一個(gè)發(fā)送消息的管道:
BOOL CallNamedPipe(
LPCTSTR lpNamedPipeName, // 管道名
LPVOID lpInBuffer, // 寫緩沖區(qū)
DWORD nInBufferSize, // 寫緩沖區(qū)大小
LPVOID lpOutBuffer, // 讀緩沖區(qū)大小
DWORD nOutBufferSize, // 讀緩沖區(qū)大小
LPDWORD lpBytesRead, // 可讀字節(jié)數(shù)
DWORD nTimeOut // 最長的等待時(shí)間
);
管道的連接管理,客戶方在調(diào)用CreateFile后可立即建立與服務(wù)器的連接,而服務(wù)器方一旦管道打開或創(chuàng)建后,就可以用下列方式等待客戶端的連接建立 :
BOOLConnectNamedPipe(HANDLE hNamedPipe,
LPOVERLAPPED lpOverlapped);
如果希望在服務(wù)器方檢測是否有連接到達(dá),可以調(diào)用:
BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName,
DWORD nTimeOut );
這里的lpNamePipeName直接使用創(chuàng)建管道時(shí)的名稱。如果在服務(wù)器方希望關(guān)閉連接,則調(diào)用BOOL DisconnectNamedPipe( HANDLE hNamedPipe ),一旦連接被關(guān)閉,服務(wù)器方可以再次調(diào)用ConnectNamedPipe來建立連接;如果要關(guān)閉管道,則可直接調(diào)用CloseHandle。請注意:這里提到的關(guān)閉管道和關(guān)閉連接具有不同的意思,在同一個(gè)管道上可以依次反復(fù)建立連接,而且可以減小系統(tǒng)的負(fù)荷。如果指定了管道最大數(shù)量限制,那么在打開的管道達(dá)到最大限制后如果不關(guān)閉舊管道,就無法打開新管道。
客戶方則無法關(guān)閉連接,而只能直接調(diào)用CloseHandle關(guān)閉管道。
數(shù)據(jù)的發(fā)送,不論是服務(wù)器還是客戶方都可以通過ReadFile和WriteFile進(jìn)行管道讀寫來達(dá)到通訊的目的。
下面是一個(gè)例子,服務(wù)器方創(chuàng)建或打開一個(gè)管道并讀入對方發(fā)送的數(shù)據(jù),將小寫字母轉(zhuǎn)換成大寫字母后返回;而客戶方創(chuàng)建一個(gè)到服務(wù)器的連接并發(fā)送一個(gè)字符串,同時(shí)讀回經(jīng)過轉(zhuǎn)換的數(shù)據(jù)。
在使用這個(gè)例子時(shí),只可運(yùn)行三個(gè)服務(wù)端進(jìn)程,如果運(yùn)行第四個(gè)進(jìn)程,就會因達(dá)到管道最大數(shù)量限制而導(dǎo)致打開管道失敗。
服務(wù)端程序:
void CNamed-pipeDlg::OnCreateP()
{
DWORD dwTO = NMPWAIT-USE-DEFAULT-WAIT;//設(shè)置連接等待時(shí)間
HANDLEhSvr=
CreateNamedPipe("\\\\.\\pipe\\test-pipe\\",PIPE-ACCESS-DUPLEX,
PIPE-TYPE-BYTE,3,256,256,dwTO,NULL);
if( INVALID-HANDLE-VALUE == hSvr)
AfxMessageBox("創(chuàng)建管道失敗!");
else
{
if (ConnectNamedPipe(hSvr,NULL))
{
BYTE bRead;
DWORD dwRead,dwWritten;
while (ReadFile(hSvr,&bRead,1,&dwRead,NULL))
{
if(bRead >= 'a' && bRead $lt;='z')
bRead = 'A'+ (bRead-'a');
WriteFile(hSvr,&bRead,1,&dwWritten,NULL);
}
}
else
AfxMessageBox("連接失敗!");
CloseHandle(hSvr);
}
}
客戶端程序:
void CNamed-pipe-cDlg::OnConn()
{
HANDLE hClient=CreateFile("\\\\.\\pipe\\test-pipe\\",GENERIC-WRITE
|GENERIC-READ,0,NULL,OPEN-EXISTING,
FILE-ATTRIBUTE-NORMAL,NULL);
if(hClient == INVALID-HANDLE-VALUE)
AfxMessageBox("打開管道失敗!");
else
{
DWORD dwRead,dwWritten;
char szSend[10]="send...";
char szRecv[10];
for(int i=0;i<strlen(szSend)+1;i++)
{
WriteFile(hClient,szSend+i,1,&dwWritten,NULL);
ReadFile(hClient,szRecv+i,1,&dwRead,NULL);
}
CloseHandle(hClient); // 關(guān)閉管道
AfxMessageBox(szRecv);
}
}
該程序在VC5.0、Win98下調(diào)試通過。▲
總結(jié)
以上是生活随笔為你收集整理的一种网络进程间通信的方式—— 管道的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WZ132源代码行侠仗义
- 下一篇: bcb中使用pas文件