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