無論是SQL Server的用戶,還是PB的用戶,作為C/S結(jié)構(gòu)開發(fā)環(huán)境,他們?cè)诰W(wǎng)絡(luò)通信的實(shí)現(xiàn)上,都有一種共同的方法——命名管道。由于當(dāng)前操作系統(tǒng)的不惟一性,各個(gè)系統(tǒng)都有其獨(dú)自的通信協(xié)議,導(dǎo)致了不同系統(tǒng)間通信的困難。盡管TCP/IP協(xié)議目前已發(fā)展成為Internet的標(biāo)準(zhǔn),但仍不能保證C/S應(yīng)用程序的順利進(jìn)行。命名管道作為一種通信方法,有其獨(dú)特的優(yōu)越性,這主要表現(xiàn)在它不完全依賴于某一種協(xié)議,而是適用于任何協(xié)議——只要能夠?qū)崿F(xiàn)通信。
命名管道具有很好的使用靈活性,表現(xiàn)在:
1) 既可用于本地,又可用于網(wǎng)絡(luò)。
2) 可以通過它的名稱而被引用。
3) 支持多客戶機(jī)連接。
4) 支持雙向通信。
5) 支持異步重疊I/O操作。
不過,當(dāng)前只有Windows NT支持服務(wù)端的命名管道技術(shù)。
一、命名管道程序設(shè)計(jì)的實(shí)現(xiàn)
1.命名管道Server和Client間通信的實(shí)現(xiàn)流程
(1)建立連接:服務(wù)端通過函數(shù)CreateNamedPipe創(chuàng)建一個(gè)命名管道的實(shí)例并返回用于今后操作的句柄,或?yàn)橐汛嬖诘墓艿绖?chuàng)建新的實(shí)例。如果在已定義超時(shí)值變?yōu)榱阋郧?,有一個(gè)實(shí)例管道可以使用,則創(chuàng)建成功并返回管道句柄,并用以偵聽來自客戶端的連接請(qǐng)求,該功能通過ConnectNamedPipe函數(shù)實(shí)現(xiàn)。
另一方面,客戶端通過函數(shù)WaitNamedPipe使服務(wù)進(jìn)程等待來自客戶的實(shí)例連接,如果在超時(shí)值變?yōu)榱阋郧埃幸粋€(gè)管道可以為連接使用,則WaitNamedPipe將返回True,并通過調(diào)用CreateFile或CallNamedPipe來呼叫對(duì)服務(wù)端的連接。此時(shí)服務(wù)端將接受客戶端的連接請(qǐng)求,成功建立連接,服務(wù)端ConnectNamedPipe返回True,客戶端CreateFile將返回一指向管道文件的句柄。
從時(shí)序上講,首先是客戶端通過WaitNamedPipe使服務(wù)端的CreateFile在時(shí)間內(nèi)創(chuàng)建實(shí)例成功,然后雙方通過ConnectNamedPipe和CreateFile成功連接,并返回用以通信的文件句柄,此時(shí)雙方即可進(jìn)行通信。
(2)通信實(shí)現(xiàn):建立連接之后,客戶端與服務(wù)器端即可通過ReadFile和WriteFile,利用得到的管道文件句柄,彼此間進(jìn)行信息交換。
(3)連接終止:當(dāng)客戶端與服務(wù)端的通信結(jié)束,或由于某種原因一方需要斷開時(shí),客戶端應(yīng)調(diào)用CloseFile,而服務(wù)端應(yīng)接著調(diào)用DisconnectNamedPipe。當(dāng)然服務(wù)端亦可通過單方面調(diào)用DisconnectNamedPipe終止連接。最后應(yīng)調(diào)用函數(shù)CloseHandle來關(guān)閉該管道。
2.命名管道服務(wù)器端和客戶端代碼實(shí)現(xiàn)
(1)客戶端:
HANDLE CltHandle;
char pipenamestr[30];
sprintf(pipenamestr,″\\\\servername\\pipe\\pipename″)
if (WaitNamedPipe( pipenamestr, NMPWAIT—WAIT—FOREVER)==FALSE
// 管道名要遵循UNC,格式為\ \.\pipe\pipname,名字不分大小寫。
AfxMessageBox(″操作失敗,請(qǐng)確定服務(wù)端正確建立管道實(shí)例!″);
Else
CltHandle=CreateFile(pipenamestr, GENERIC—READ|GENERIC—WRITE, FILE—SHARE—READ| FILE—SHARE—WRITE,NULL, OPEN—EXISTING,
//為了與命名管道連接,此參數(shù)應(yīng)一直為OPEN—EXISTING
FILE—ATTRIBUTE—ARCHIVE|FILE—FLAG—WRITE—THROUGH,
// FILE—FLAG—WRITE—THROUGH會(huì)使管道WriteFile調(diào)用處于阻塞狀態(tài),直到數(shù)據(jù)傳送成功。
NULL);
If (CltHandle== INVALID—HANDLE—VALUE)
AfxMessageBox(″管道連接失敗″);
Else
DoUsertTransactInfo();
//執(zhí)行用戶自定義信息交換函數(shù)——從管道讀、寫信息。
……
(2)服務(wù)端:
HANDLE SvrHandle;
char pipenamestr[30];
sprintf(pipenamestr,″\\\\.\\pipe\\pipename″)
SvrHandle=CreateNamedPipe(pipenamestr,
PIPE—ACCESS—DUPLEX|FILE—FLAG—WRITE—THROUGH,
//阻塞模式,這種模式僅對(duì)″字節(jié)傳輸管道″操作有效。
FILE—WAIT|PIPE—TYPE—BYTE,
//字節(jié)模式
PIPE—UNLIMITED—INSTANCES,
128,128,
NULL,NULL);
// SECURITY—ATTRIBUTES結(jié)構(gòu)指針,描述一個(gè)新管道,確定子進(jìn)程的繼承權(quán),如果為NULL則該命名管道不能被繼承。
If (SvrHandle==INVALID—HANDLE—VALUE)
AfxMessageBox(″管道創(chuàng)建失敗,請(qǐng)確定客戶端提供連接可能!″);
Else
If (ConnectNamedPipe(SvrHandle,NULL)==FALSE)
AfxMessageBox(″建立連接失?。 ?;
Else
DoUsertTransactInfo();
//用戶自定義信息交換函數(shù)
……
二、程序設(shè)計(jì)的注意事項(xiàng)
1.如果命名管道客戶端已打開,函數(shù)將會(huì)強(qiáng)迫關(guān)閉管道,用DisconnectNamedPipe關(guān)閉的管道,其客戶端還必須用CloseHandle來關(guān)閉最后的管道。
2. ReadFile和WriteFile的hFile句柄是由CreateFile及ConnectNamedPipe返回得到。
3.一個(gè)已被某客戶端連接的管道句柄在被另一客戶通過ConnectNamedPipe建立連接之前,服務(wù)端必須用DisconnectNamedPipe函數(shù)對(duì)已存在的連接進(jìn)行強(qiáng)行拆離。服務(wù)端拆離管道會(huì)造成管道中數(shù)據(jù)的丟失,用FlushFileBuffers函數(shù)可以保證數(shù)據(jù)不被丟失。
4.命名管道服務(wù)端可以通過新創(chuàng)建的管道句柄或已被連接過其他客戶的管道句柄來使用ConnectNamedPipe函數(shù),但在連接新的客戶端之前,服務(wù)端必須用函數(shù)DisconnectNamedPipe切斷之前的客戶句柄,否則ConnectNamedPipe 將會(huì)返回False。
5.阻塞模式,這種模式僅對(duì)“字節(jié)傳輸管道"操作有效,并且要求客戶端與服務(wù)端不在同一機(jī)器上。如果用這種模式,則只有當(dāng)函數(shù)通過網(wǎng)絡(luò)向遠(yuǎn)端計(jì)算機(jī)管道緩沖器寫數(shù)據(jù)成功時(shí),才能有效返回。如果不用這種模式,系統(tǒng)會(huì)運(yùn)行缺省方式以提高網(wǎng)絡(luò)的工作效率。
6.用戶必須用FILE—CREATE—PIPE—INSTANCE 來訪問命名管道對(duì)象。新的命名管道建立后,來自安全參數(shù)的訪問控制列表定義了訪問該命名管道的權(quán)限。所有命名管道實(shí)例必須使用統(tǒng)一的管道傳輸方式、管道模式等參數(shù)??蛻舳宋磫?dòng),管道服務(wù)端不能執(zhí)行阻塞讀操作,否則會(huì)發(fā)生空等的阻塞狀態(tài)。當(dāng)最后的命名管道實(shí)例的最后一個(gè)句柄被關(guān)閉時(shí),就應(yīng)該刪除該命名管道。
命名管道具有很好的使用靈活性,表現(xiàn)在:
1) 既可用于本地,又可用于網(wǎng)絡(luò)。
2) 可以通過它的名稱而被引用。
3) 支持多客戶機(jī)連接。
4) 支持雙向通信。
5) 支持異步重疊I/O操作。
不過,當(dāng)前只有Windows NT支持服務(wù)端的命名管道技術(shù)。
一、命名管道程序設(shè)計(jì)的實(shí)現(xiàn)
1.命名管道Server和Client間通信的實(shí)現(xiàn)流程
(1)建立連接:服務(wù)端通過函數(shù)CreateNamedPipe創(chuàng)建一個(gè)命名管道的實(shí)例并返回用于今后操作的句柄,或?yàn)橐汛嬖诘墓艿绖?chuàng)建新的實(shí)例。如果在已定義超時(shí)值變?yōu)榱阋郧?,有一個(gè)實(shí)例管道可以使用,則創(chuàng)建成功并返回管道句柄,并用以偵聽來自客戶端的連接請(qǐng)求,該功能通過ConnectNamedPipe函數(shù)實(shí)現(xiàn)。
另一方面,客戶端通過函數(shù)WaitNamedPipe使服務(wù)進(jìn)程等待來自客戶的實(shí)例連接,如果在超時(shí)值變?yōu)榱阋郧埃幸粋€(gè)管道可以為連接使用,則WaitNamedPipe將返回True,并通過調(diào)用CreateFile或CallNamedPipe來呼叫對(duì)服務(wù)端的連接。此時(shí)服務(wù)端將接受客戶端的連接請(qǐng)求,成功建立連接,服務(wù)端ConnectNamedPipe返回True,客戶端CreateFile將返回一指向管道文件的句柄。
從時(shí)序上講,首先是客戶端通過WaitNamedPipe使服務(wù)端的CreateFile在時(shí)間內(nèi)創(chuàng)建實(shí)例成功,然后雙方通過ConnectNamedPipe和CreateFile成功連接,并返回用以通信的文件句柄,此時(shí)雙方即可進(jìn)行通信。
(2)通信實(shí)現(xiàn):建立連接之后,客戶端與服務(wù)器端即可通過ReadFile和WriteFile,利用得到的管道文件句柄,彼此間進(jìn)行信息交換。
(3)連接終止:當(dāng)客戶端與服務(wù)端的通信結(jié)束,或由于某種原因一方需要斷開時(shí),客戶端應(yīng)調(diào)用CloseFile,而服務(wù)端應(yīng)接著調(diào)用DisconnectNamedPipe。當(dāng)然服務(wù)端亦可通過單方面調(diào)用DisconnectNamedPipe終止連接。最后應(yīng)調(diào)用函數(shù)CloseHandle來關(guān)閉該管道。
2.命名管道服務(wù)器端和客戶端代碼實(shí)現(xiàn)
(1)客戶端:
HANDLE CltHandle;
char pipenamestr[30];
sprintf(pipenamestr,″\\\\servername\\pipe\\pipename″)
if (WaitNamedPipe( pipenamestr, NMPWAIT—WAIT—FOREVER)==FALSE
// 管道名要遵循UNC,格式為\ \.\pipe\pipname,名字不分大小寫。
AfxMessageBox(″操作失敗,請(qǐng)確定服務(wù)端正確建立管道實(shí)例!″);
Else
CltHandle=CreateFile(pipenamestr, GENERIC—READ|GENERIC—WRITE, FILE—SHARE—READ| FILE—SHARE—WRITE,NULL, OPEN—EXISTING,
//為了與命名管道連接,此參數(shù)應(yīng)一直為OPEN—EXISTING
FILE—ATTRIBUTE—ARCHIVE|FILE—FLAG—WRITE—THROUGH,
// FILE—FLAG—WRITE—THROUGH會(huì)使管道WriteFile調(diào)用處于阻塞狀態(tài),直到數(shù)據(jù)傳送成功。
NULL);
If (CltHandle== INVALID—HANDLE—VALUE)
AfxMessageBox(″管道連接失敗″);
Else
DoUsertTransactInfo();
//執(zhí)行用戶自定義信息交換函數(shù)——從管道讀、寫信息。
……
(2)服務(wù)端:
HANDLE SvrHandle;
char pipenamestr[30];
sprintf(pipenamestr,″\\\\.\\pipe\\pipename″)
SvrHandle=CreateNamedPipe(pipenamestr,
PIPE—ACCESS—DUPLEX|FILE—FLAG—WRITE—THROUGH,
//阻塞模式,這種模式僅對(duì)″字節(jié)傳輸管道″操作有效。
FILE—WAIT|PIPE—TYPE—BYTE,
//字節(jié)模式
PIPE—UNLIMITED—INSTANCES,
128,128,
NULL,NULL);
// SECURITY—ATTRIBUTES結(jié)構(gòu)指針,描述一個(gè)新管道,確定子進(jìn)程的繼承權(quán),如果為NULL則該命名管道不能被繼承。
If (SvrHandle==INVALID—HANDLE—VALUE)
AfxMessageBox(″管道創(chuàng)建失敗,請(qǐng)確定客戶端提供連接可能!″);
Else
If (ConnectNamedPipe(SvrHandle,NULL)==FALSE)
AfxMessageBox(″建立連接失?。 ?;
Else
DoUsertTransactInfo();
//用戶自定義信息交換函數(shù)
……
二、程序設(shè)計(jì)的注意事項(xiàng)
1.如果命名管道客戶端已打開,函數(shù)將會(huì)強(qiáng)迫關(guān)閉管道,用DisconnectNamedPipe關(guān)閉的管道,其客戶端還必須用CloseHandle來關(guān)閉最后的管道。
2. ReadFile和WriteFile的hFile句柄是由CreateFile及ConnectNamedPipe返回得到。
3.一個(gè)已被某客戶端連接的管道句柄在被另一客戶通過ConnectNamedPipe建立連接之前,服務(wù)端必須用DisconnectNamedPipe函數(shù)對(duì)已存在的連接進(jìn)行強(qiáng)行拆離。服務(wù)端拆離管道會(huì)造成管道中數(shù)據(jù)的丟失,用FlushFileBuffers函數(shù)可以保證數(shù)據(jù)不被丟失。
4.命名管道服務(wù)端可以通過新創(chuàng)建的管道句柄或已被連接過其他客戶的管道句柄來使用ConnectNamedPipe函數(shù),但在連接新的客戶端之前,服務(wù)端必須用函數(shù)DisconnectNamedPipe切斷之前的客戶句柄,否則ConnectNamedPipe 將會(huì)返回False。
5.阻塞模式,這種模式僅對(duì)“字節(jié)傳輸管道"操作有效,并且要求客戶端與服務(wù)端不在同一機(jī)器上。如果用這種模式,則只有當(dāng)函數(shù)通過網(wǎng)絡(luò)向遠(yuǎn)端計(jì)算機(jī)管道緩沖器寫數(shù)據(jù)成功時(shí),才能有效返回。如果不用這種模式,系統(tǒng)會(huì)運(yùn)行缺省方式以提高網(wǎng)絡(luò)的工作效率。
6.用戶必須用FILE—CREATE—PIPE—INSTANCE 來訪問命名管道對(duì)象。新的命名管道建立后,來自安全參數(shù)的訪問控制列表定義了訪問該命名管道的權(quán)限。所有命名管道實(shí)例必須使用統(tǒng)一的管道傳輸方式、管道模式等參數(shù)??蛻舳宋磫?dòng),管道服務(wù)端不能執(zhí)行阻塞讀操作,否則會(huì)發(fā)生空等的阻塞狀態(tài)。當(dāng)最后的命名管道實(shí)例的最后一個(gè)句柄被關(guān)閉時(shí),就應(yīng)該刪除該命名管道。