咱們知道管道包括三種:ios
1):普通管道PIPE,一般有不少限制,一是半雙工,只能單向傳輸,二是隻能在父子進程間使用windows
2):流管道:這種能雙向傳輸,可是也是隻能父子進程間使用。安全
3):命名管道,去除了以上的第二種限制,能夠在許多不相關的進程間進行通信。也是半雙工的通訊方式。服務器
可是一般咱們把管道分爲匿名管道和命名管道。但對於匿名管道的話,只能在本機上進程之間通訊,並且只能實現本地的父子進程之間的通訊,侷限性太大了。而這裏介紹的命名管道,就和匿名管道有些不一樣了,在功能上也就顯得強大許多,至少其能夠實現跨網絡之間的進程的通訊,同時其客戶端既能夠接收數據也能夠發送數據,服務器端也是能夠接收數據,又能夠發送數據。網絡
匿名管道的概述app
對於匿名管道而言,命名管道使用了windows安全機制,於是命名管道的服務器端能夠控制哪些客戶有權與其創建鏈接。哪些客戶端是不可以與這個命名管道創建鏈接的。命名管道的通訊是以鏈接的方式進行的,服務器建立一個命名管道對象,而後在此對象上等待鏈接請求,一旦客戶鏈接過來,則二者均可以經過命名管道讀或者寫數據。異步
命名管道提供了兩種通訊模式:字節模式和消息模式。在字節模式下,數據以一個連續的字節流的形式在客戶機和服務器之間流動。而在消息模式下,客戶機和服務器則經過一系列不連續的數據單位,進行數據的收發,每次在管道上發出一個消息後,它必須做爲一個完整的消息讀入。函數
命名管道的使用步驟測試
服務器端:spa
1):服務器進程調用CreateNamedPipe函數來建立一個有名稱的命名管道在建立命名管道的時候必須指定一個本地的命名管道名稱。windows容許同一個本地的命名管道名稱右多個命名管道實例。因此,服務器進程在調用CreateNamedPipe函數時必須指定最大容許的實例數(0-255).若是CreateNamedPipe函數成功返回後,服務器進程獲得一個指向一個命名管道實例的句柄。
2):服務器進程就能夠調用ConnectNamedPipe來等待客戶的鏈接請求,這個ConnectNamedPipe既支持同步形式,又支持異步形式,若服務器進程以同步形式調用 ConnectNamedPipe函數,若是沒有獲得客戶端的鏈接請求,則會一直等到客戶端的鏈接請求。當該函數返回時,客戶端和服務器之間的命名管道鏈接已經創建起來了。
3):這個時候服務器端就能夠向客戶端讀(ReadFile)/寫(WriteFile)數據了。
4):在已經創建鏈接的命名管道實例中,服務器進程就會獲得一個指向該管道實例的句柄,這個句柄稱之爲服務器端句柄,同時服務端進程能夠調用DisconnectNamedPipe函數,將一個管道實例與當前創建鏈接的客戶端進程斷開,從而能夠從新鏈接到新的客戶端進程。固然,服務器也能夠調用CloseHandle來關閉一個已經創建鏈接的命名管道實例。
客戶端:
1):客戶端進程調用CreateFile函數鏈接到一個正在等待鏈接的命名管道上。在這裏客戶端須要指定將要鏈接的命名管道上。當CreateFile成功返回以後,客戶端就獲得了一個指向已經創建鏈接的命名管道實例的句柄。在這裏客戶端也能夠先調用WaitNamedPipe函數來測試指定名稱的管道實例是否可用。在已經創建的命名管道實例中,客戶端進程就會獲得一個指向該管道實例的句柄。這個句柄稱之爲客戶端句柄。
2):這個時候客戶端就能夠向服務器讀(ReadFile)/寫(WriteFile)數據了.
3):客戶端能夠調用CloseHandle來關閉一個已經創建鏈接的命名管道實例。
函數介紹
1:
HANDLE WINAPI CreateNamedPipe( _In_ LPCTSTR lpName, _In_ DWORD dwOpenMode, _In_ DWORD dwPipeMode, _In_ DWORD nMaxInstances, _In_ DWORD nOutBufferSize, _In_ DWORD nInBufferSize, _In_ DWORD nDefaultTimeOut, _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes );
該函數用來建立一個命名管道的實例,並返回這個命名管道的句柄。若是須要建立一個命名管道的多個實例,就須要屢次調用CreateNamedPipe函數,參數 lpName 爲一個字符串,其格式必須爲 \\.\pipe\pipeName,其中圓點 」.」 表示的是本地機器,若是想要與遠程的服務器創建鏈接,那麼這個圓點位置處應指定這個遠程服務器的名稱,而其中的 「pipe」 這個是個固定的字符串,也就是說不能進行改變的,最後的 「pipename」 則表明的是我將要建立的命名管道的名稱了,參數 dwOpenMode 用來指定管道的訪問方式,重疊方式,寫直通方式,還有管道句柄的安全訪問方式。
命名管道的訪問方式以下表:
命名管道的寫直通方式和重疊方式
命名管道的安全訪問方式
命名管道句柄的類型
命名管道句柄的讀取方式
命名管道句柄的等待方式
nMaxInstance 指定命名管道可以建立的實例的最大數目.該參數的取值能夠從 0 – 255 ,這裏說的最大實例數目是指對同一個命名管道最多能建立的實例數目。
nOutBufferSize 用來指定將要爲輸出緩衝區所保留的字節數
nInBufferSize 用來指定將要爲輸入緩衝區所保留的字節數
nDefaultTimeOut 用來指定默認的超時值,以毫秒爲單位,同一個管道的不一樣實例必須指定一樣的超時值
lpSecurityAttributes 用來設置該命名管道的安全性,通常設置爲 NULL ,也就是採用 Windows 提供的默認安全性
2:
BOOL WINAPI ConnectNamedPipe( _In_ HANDLE hNamedPipe, _Inout_opt_ LPOVERLAPPED lpOverlapped );
該函數的做用是讓服務器等待客戶端的鏈接請求的到來
hNamedPipe 指向一個命名管道實例的服務器的句柄
lpOverlapped 指向一個 OVERLAPPED結構的指針,
若是 hNamedPipe 所標識的命名管道是用 FILE_FLAG_OVERLAPPED,
(也就是重疊模式或者說異步方式)標記打開的,則這個參數不能爲 NULL ,
必須是一個有效的指向一個 OVERLAPPED 結構的指針,不然該函數可能會錯誤的執行
3:
BOOL WINAPI WaitNamedPipe( _In_ LPCTSTR lpNamedPipeName, _In_ DWORD nTimeOut );
經過該函數能夠判斷是否有可用的命名管道。直到等待的時間間隔已過,或者指定的命名管道的實例能夠用來鏈接了。
lpNamedPipeName 用來指定命名管道的名稱,格式同CreateNamedPipe函數的lpNamedPipeName參數。
參數 nTimeOut 用來指定超時間隔,參數能夠填寫系列的值::
NMPWAIT_USE_DEFAULT_WAIT::超時間隔即爲服務器端建立該命名管道時指定的超時間隔。
NMPWAIT_USE_DEFAULT_WAIT::一直等待,直到出現一個可用的命名管道的實例。
命名管道的通訊實例:
服務器端的實現
NamePipeServer.h
1 #ifndef NAME_PIPE_SERVER_H 2 #define NAME_PIPE_SERVER_H 3 4 #include<windows.h> 5 #include<iostream> 6 7 class NamePipeServer 8 { 9 public: 10 NamePipeServer() 11 { 12 pStr = "data from server"; 13 pPipeName = "\\\\.\\pipe\\testPipe"; 14 } 15 //建立命名管道 16 void CreateNamedPipeInServer(); 17 //從命名管道中讀取數據 18 void NamedPipeReadInServer(); 19 //往命名管道中寫入數據 20 void NamedPipeWriteInServer(); 21 private: 22 HANDLE hNamedPipe; 23 const char *pStr; 24 const char *pPipeName; 25 }; 26 27 #endif
NamePipeServer.cpp
1 #include "stdafx.h" 2 #include "NamePipeServer.h" 3 4 using namespace std; 5 void NamePipeServer::CreateNamedPipeInServer() 6 { 7 HANDLE hEvent; 8 OVERLAPPED ovlpd; 9 10 BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH]; 11 SECURITY_ATTRIBUTES sa; 12 13 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 14 sa.bInheritHandle = TRUE; 15 sa.lpSecurityDescriptor = &sd; 16 17 InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); 18 SetSecurityDescriptorDacl(&sd, TRUE, (PACL) 0, FALSE); 19 //建立命名管道 20 //這裏建立的是雙向模式且使用重疊模式(異步操做)的命名管道 21 hNamedPipe = CreateNamedPipe( L"\\\\.\\pipe\\testspipe",PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,0, 1, 1024, 1024, 0, &sa); 22 if( INVALID_HANDLE_VALUE == hNamedPipe ) 23 { 24 cout << GetLastError() << endl; 25 hNamedPipe = NULL; 26 cout << "建立命名管道失敗!!!" << endl << endl; 27 return; 28 } 29 hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 30 if( !hEvent ) 31 { 32 cout<<"建立事件失敗 ..."<< endl<< endl; 33 return; 34 } 35 memset(&ovlpd, 0, sizeof(OVERLAPPED)); 36 ovlpd.hEvent = hEvent; 37 38 cout << "等待客戶端的鏈接" << endl; 39 if( !ConnectNamedPipe(hNamedPipe, &ovlpd) ) 40 { 41 if( ERROR_IO_PENDING != GetLastError() ) 42 { 43 CloseHandle(hNamedPipe); 44 CloseHandle(hEvent); 45 cout<<"等待客戶端鏈接失敗 ..."<< endl << endl; 46 return; 47 } 48 } 49 //等待事件 hEvent 失敗 50 if( WAIT_FAILED == WaitForSingleObject(hEvent, INFINITE) ) 51 { 52 CloseHandle(hNamedPipe); 53 CloseHandle(hEvent); 54 cout<<"等待對象失敗 ..."<<endl<<endl; 55 return; 56 } 57 CloseHandle(hEvent); 58 } 59 60 void NamePipeServer::NamedPipeReadInServer() 61 { 62 char * pReadBuf; 63 DWORD dwRead; 64 pReadBuf = new char[strlen(pStr) + 1]; 65 memset(pReadBuf, 0, strlen(pStr) + 1); 66 //從命名管道中讀取數據 67 if( !ReadFile(hNamedPipe, pReadBuf, strlen(pStr), &dwRead, NULL) ) 68 { 69 delete []pReadBuf; 70 cout<<"讀取數據失敗 ..."<< endl<< endl; 71 return; 72 } 73 cout << "讀取數據成功::"<< pReadBuf << endl<< endl; 74 } 75 76 void NamePipeServer::NamedPipeWriteInServer() 77 { 78 DWORD dwWrite; 79 //向命名管道中寫入數據 80 if( !WriteFile(hNamedPipe, pStr, strlen(pStr), &dwWrite, NULL) ) 81 { 82 cout << "寫入數據失敗 ..." << endl<< endl; 83 return; 84 } 85 cout << "寫入數據成功:: "<< pStr<< endl<< endl; 86 } 87 88 int main() 89 { 90 NamePipeServer pipeserver; 91 //建立命名管道 92 pipeserver.CreateNamedPipeInServer(); 93 //從命名管道讀數據 94 pipeserver.NamedPipeReadInServer(); 95 //向匿名管道中寫入數據 96 pipeserver.NamedPipeWriteInServer(); 97 98 system("pause"); 99 return 0; 100 }
客戶端代碼實現:
NamePipeClient.h
1 #ifndef _NAME_PIPE_CLIENT_H 2 #define _NAME_PIPE_CLIENT_H 3 4 #include<windows.h> 5 #include<iostream> 6 7 class NamePipeClient 8 { 9 public: 10 NamePipeClient() 11 { 12 pStr = "data from client"; 13 pPipeName = "\\\\.\\pipe\\testPipe"; 14 } 15 //打開命名管道 16 void OpenNamedPipeInClient(); 17 //客戶端從命名管道中讀取數據 18 void NamedPipeReadInClient(); 19 //客戶端往命名管道中寫入數據 20 void NamedPipeWriteInClient(); 21 22 private: 23 //用來保存在客戶端經過 CreateFile 打開的命名管道句柄HANDLE 24 HANDLE hNamedPipe; 25 const char * pStr; 26 const char * pPipeName; 27 }; 28 29 #endif
NamePipeClient.cpp
1 #include "stdafx.h" 2 #include "NamePipeClient.h" 3 4 using namespace std; 5 6 void NamePipeClient::OpenNamedPipeInClient() 7 { 8 //等待鏈接命名管道 9 if( !WaitNamedPipe(L"\\\\.\\pipe\\testspipe", NMPWAIT_WAIT_FOREVER) ) 10 { 11 cout<<"命名管道實例不存在 ..."<< endl<< endl; 12 return; 13 } 14 cout << "成功鏈接到服務器" << endl; 15 //打開命名管道 16 hNamedPipe = CreateFile( L"\\\\.\\pipe\\testspipe", GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 17 if( INVALID_HANDLE_VALUE == hNamedPipe ) 18 { 19 cout << "打開命名管道失敗!!!" << endl << endl; 20 return; 21 } 22 } 23 24 void NamePipeClient::NamedPipeReadInClient() 25 { 26 char * pReadBuf; 27 DWORD dwRead; 28 pReadBuf = new char[strlen(pStr) + 1]; 29 memset(pReadBuf, 0, strlen(pStr) + 1); 30 //從命名管道中讀取數據 31 if( !ReadFile(hNamedPipe, pReadBuf, strlen(pStr), &dwRead, NULL) ) 32 { 33 delete []pReadBuf; 34 cout << "讀取數據失敗 ..."<< endl << endl; 35 return; 36 } 37 cout<<"讀取數據成功:: "<< pReadBuf << endl << endl; 38 } 39 40 void NamePipeClient::NamedPipeWriteInClient() 41 { 42 DWORD dwWrite; 43 //向命名管道中寫入數據 44 if( !WriteFile(hNamedPipe, pStr, strlen(pStr), &dwWrite, NULL) ) 45 { 46 cout<<"寫入數據失敗 ..." << endl << endl; 47 return; 48 } 49 cout<< "寫入數據成功:: "<< pStr << endl << endl; 50 } 51 52 int main() 53 { 54 NamePipeClient pipeclient; 55 pipeclient.OpenNamedPipeInClient(); 56 //往命名管道中寫入數據 57 pipeclient.NamedPipeWriteInClient(); 58 //接收從服務器發來的數據 59 pipeclient.NamedPipeReadInClient(); 60 system("pause"); 61 return 0; 62 }
首先啓動服務器,服務器在等待客戶端的鏈接
而後啓動客戶端
能夠看到服務器成功從客戶端讀取到數據,而且寫入數據成功。
而客戶端也寫入服務器數據成功,而且成功讀取到服務器的數據。