?? 17.3.2 服務器端程序.txt
字號:
17.3.2 服務器端程序
下面,我們就利用命名管道實現進程間的通信。首先實現服務器端程序,新建一個單文檔類型的 MFC
應用程序,工程取名為: NamedPipeSrv。然后,為該工程增加一個子菜單,名稱為"命名管道"。接
著,為該子菜單添加三個菜單項,并分別為它們添加相應的命令響應函數,本例選擇
CNamedPipeSrvView類接收這些命令響應函數。各菜單項的 E、名稱,以及響應函數如表 17.13所示。
ID 菜單名稱 響應函數
IDM_PIPE_CREATE 創建管道 OnPipeCreate
IDM_PIPE_READ 讀取數據 OnPipeRead
IDM_PIPE_WRITE 寫入數據 OnPipeWrite
接下來,為 CNamedPipeSrvView類增加一個句柄變量,用來保存創建的命名管道實例的句柄。
private:
HANDLE hPipe;
在 CNamedPipeSrvView類的構造函數中將其初始化為 NULL:
CNamedPipeSrvView :: CNamedPipeSrvView ()
// TODO: add construction code here
hPipe =NULL ;
然后在 CNamedPipeSrvView類的析構函數中,如果判斷該句柄有值,則調用 CloseHandle函數關閉
該句柄 :
CNarnedPipeSrvView:: -CNamedPipeSrvView ()
if(hPipe)
CloseHandle(hPipe) ;
1.創建命名管道
接下來,在 OnPipeCreate函數中就可以調用 CreateNamedPipe函數創建命名管道了。具體代碼如例
17-9所示。
例17-9 ,
void CNamedPipeSrvView : :OnPipeCreate()
// TODO: Add your cornrnand handler code here
//創建命名管道
hPipe=CreateNamedPipe("\\\\ .\\pipe\\MyPipe" , PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
0, 1 , 1024 , 1024 , 0,NULL) ;
if(INVALID_HANDLE_VALUE==hPipe)
MessageBox ( "創建命名管道失敗 ! ") ;
hPipe=NULL ;
return;
//創建匿名的人工重置事件對象
HANDLE hEvent;
hEvent=CreateEvent(NULL, TRUE , FALSE ,NULL) ;
if ( !hEvent)
MessageBox("創建事件對象失敗!");
CloseHandle(hPipe);
hPipe=NULL;
return;
}
OVERLAPPED ovlap;
ZeroMemory(&ovlap , sizeof(OVERLAPPED));
ovlap.hEvent=hEvent;
//等待客戶端請求的到來
if(!ConnectNamedPipe(hPipe, &ovlap))
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);
,如果想要指定兩個反斜杠,那么在代碼中就需要輸入四個反斜杠。
所以,在如例 17-9所示代碼中調用 CreateNamedPipe函數時,將管道的名稱指定為:
"\\.\pipe\hPipe";管道訪問的模式設定為 PIPE_ACCESS_DUPLEX,即雙向模式,服
務器進程和客戶端進程都可以從管道讀取數據和向管道中寫入數據,同時指定
FILE_FLAG_OVERLAPPED標志,允許重疊方式:第三個參數用來指定管道類型、讀取
和等待方式,本例將其值設為 0,即默認為字節類型和字節讀方式:第四個參數用來指定
管道實例的最大數目,本例設置為1,因為本程序是一個測試程序,只需要一個客戶端連
接就可以了:第五個和第六個參數分別用來指定輸出緩沖區大小和輸入緩沖區大小,本例
都設置為 1024;第七個參數指定超時值,本例設為 0;最后一個參數指定安全屬性,本例
設置為 NULL,讓管道句柄使用默認的安全性。
如果 CreateNamedPipe函數調用成功,它將返回一個有效的管道句柄;否則返回
INVALID_HANDLE_VALUE,可以調用 GetLas tError函數獲得更多的錯誤信息。因此在程序中可以對
CreateNamedPipe函數的返回值進行判斷,如果失敗,則提示用戶:"創建命名管道失敗!",接著,將
管道句柄變量 ChPipe)設置為 NULL,這樣是為了避免程序失敗時在 CNamedPipeSrvView對象的析構
函數中再次調用 CloseHandle函數關閉這個句柄,然后讓 OnPipeCreate函數直接返回:如果成功創
建了命名管道的實例,就可以調用 ConnectNamedPipe函數,等待客戶端請求的到來。這個函數允許
一個服務端進程等待一個客戶端進程連接到一個命名管道的一個實例上。這個函數的命名不太好,
給人的直覺好像去連接服務器端的命名管道,實際上這個函數的作用是讓服務器等待客戶端的連接
請求的到來。該函數聲明如下所示:
BOOL ConnectNamedPipe(HANDLE hNamedPipe , LPOVERLAPPED lpOverlapped);
ConnectNamedPipe函數有兩個參數,其含義分別如下所述。
. hNamedPipe
指向一個命名管道實例的服務器的句柄,該句柄由 CreateNamedPipe函數返回。
. lpOverlapped
指向一個 OVERLAPPED結構的指針,如果 hNamedPipe參數所標識的管道是用 FILE-FLAG-OVERLAPPED
標記打開的,則這個參數不能是 NULL,必須是一個有效的指向一個 OVERLAPPED結構的指針;否則該
函數可能會錯誤地執行。如果hNamedPipe參數所標識的管道是用 FILE-FLAG-OVERLAPPED標記打開
的,并且這個參數不是 NULL,則這個參數所指向的 OVERLAPPED結構體中必須包含人工重置事件對
象句柄。
于是,上述 OnPipeCreate函數調用 CreateEvent的數創建了一個匿名的人工重置事件對象句柄(注
意:第二個參數一定要指定為 TRUE) o CreateEvent函數如果調用失敗,將返回 NULL,所以對該函
數的返回值進行判斷,如果調用失敗,則提示用戶:"創建事件對象失敗!",并在調用 return語句讓
OnPipeCreate函數返回之前,調用 CloseHandle函數關閉命名管道的句柄,然后將其設置為 NULL,
原因前面已經提過了,主要是為了避免程序關閉時,在 CNamedPipeSrvView對象的析構函數中再次
調用 CloseHandle函數關閉這個句柄。
如果成功創建了匿名的人工重置事件對象,那么接下來就定義一個 OVERLAPPED結構體類型的變量:
ovlap,雖然程序中片需要使用到該變量的事件對象句柄成員 C hEvent ),但是首先應該將 ovlap
變量中所有成員都設置為 0,以免它們影響函數運行的結果,然后將 hEvent成員設置為剛剛創建的
一個有效的人工重置事件對象句柄。
接著就可以調用 Connec tN amedPipe函數等待客戶端請求的到來,該函數的第一個參數就是前面調
用 CreateNamedPipe函數返回的一個有效的命名管道句柄,第二個參數就是指向 OVERLAPPED結構體
變量的指針,即 ovlap變量的地址。
如果 ConnectNamedPipe函數調用失敗,它將返回 O值,但其中有一種特殊情況并不表明等待連接事
件失敗了,也就是說,如果這時調用 GetLastError函數返回 ERROR_
IO_PENDING.那么并不表示 ConnectNamedPipe函數失敗了,只是表明這個操作是一個未決的操作,
在隨后的某個時間這個操作可能能夠完成。因此在程序中,當 ConnectNamedPipe函數返回 O時,還
應調用 GetLastError函數,并對其返回值進行判斷,如果不是 ERROR_IO_PENDING.才說明
ConnectNamedPipe函數調用失敗,這時提示用戶:"等待客戶端連接失敗!",然后調用 CloseHandle
函數分別關閉管道句柄和事件對象句柄,井將管道句柄設置為 NULL.之后調用 retum語句返回。
如果上述操作都成功了,那么這時調用 WaitForSingleObject函數等待事件對象 (hEvent)變為有信
號狀態。讀者應注意,前面我們己將該事件對象句柄賦給了。vlap變量的hEvent成員,也就是說,
這兩個變量: hEvent和 ovlap.hEvent.現在標識的是同一個對象,因此在調用
WaitForSingleObject函數時,采用這兩個對象中的任一個都是可以的。本例將
WaitForSingleObject函數的第二個參數設置為 INFINITE,即讓線程永遠等待,直到所等待的事件
對象變為有信號狀態。
同樣的,應該對 WaitForSingleObject函數的返回值進行判斷,如果調用失敗,則提示用戶"等待事
件對象失敗!",然后關閉相關的句柄,并將管道旬柄設置為 NULL.之后調用 retum語句返回。
最后,當請求到所等待的事件對象后,也就是當該事件對象變成有信號狀態時,說明已經有一個客
戶端連接到命名管道的實例上了。這時,不再需要該事件對象句柄了,可以調用 CloseHandle函數
將它關閉。
2.讀
對于命名管道的數據讀取操作,與上面匿名管道的讀取操作是一樣的,因此可以直接復制己實現的
代碼,然后將 ReadFile函數的第一個參數修改為本例創建的命名管道的句柄即可,結果如例 17-10
所示。
例 17-10
void CNamedPipeSrvView: :OnPipeRead()
(
// TODO: Add your command handler code here
char buf [1 00] ;
DWORD dwRead;
if(!ReadFile(hPipe, buf , 100 , &dwRead,NULL))
MessageBox ( "讀取數據失敗!");
return;
MessageBox(buf);
3.寫入數據
對于命名管道的數據寫入操作,與上面匿名管道的寫入操作是一樣的,所以可以直接復制己實現的
代碼,然后將 WriteFi1e函數的第一個參數修改為本例創建的命名管道的句柄
即可,結果如例 17-11所示。
1?IJ 17-11
void CNarnedPipeSrvView: :OnPipeWrite()
11 TODO : Add your cornrnand handler code here
char buf [1 = ''http ://www . sunxin. org" ;
DWORD dwWrite;
if(!WriteFile(hPipe, buf, strlen(buf ) +1, &dwWrite, NULL) )
MessageBox ( "寫入數據失敗!");
return;
至此我們就完成了利用命名管道實現進程間通信的服務器端程序,利用 Build命令生成
NamedPipeSrv程序。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -