咱们知道管道包括三种: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 }
首先启动服务器,服务器在等待客户端的链接
而后启动客户端
能够看到服务器成功从客户端读取到数据,而且写入数据成功。
而客户端也写入服务器数据成功,而且成功读取到服务器的数据。