(本章节中例子都是用 VS2005 编译调试的)
进程还可以通过套接字进行通信
通信流程:
注意:
CreateMailslot函数详解
函数原型:
HANDLE CreateMailslot( LPCTSTR lpName, // mailslot name DWORD nMaxMessageSize, // maximum message size DWORD lReadTimeout, // read time-out interval LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritance option );
参数说明:
代码样例:
服务器端源码:
#include<windows.h> #include<cstdlib> #include<iostream> using namespace std; void main() { HANDLE hMailslot; char buf[100]; DWORD dwRead; //创建邮槽 hMailslot=CreateMailslot("\\\\.\\mailslot\\Communication",0, MAILSLOT_WAIT_FOREVER,NULL); if(INVALID_HANDLE_VALUE==hMailslot) { cout<<"创建邮槽失败!"<<endl; system("pause"); return; } //等待用户写入数据然后读取出数据 if(!ReadFile(hMailslot,buf,100,&dwRead,NULL)) { cout<<"读取数据失败!"<<endl; CloseHandle(hMailslot); system("pause"); return; } cout<<buf<<endl; //关闭邮槽 CloseHandle(hMailslot); system("pause"); }
客户端源码:
#include<windows.h> #include<cstdlib> #include<iostream> using namespace std; void main() { HANDLE hMailslot; char buf[]="this is message"; //打开邮槽 hMailslot=CreateFile("\\\\.\\mailslot\\Communication",GENERIC_WRITE, FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(INVALID_HANDLE_VALUE==hMailslot) { cout<<"打开邮槽失败!"<<endl; system("pause"); return; } //向邮槽写数据 DWORD dwWrite; if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL)) { cout<<"写入数据失败!"<<endl; CloseHandle(hMailslot); system("pause"); return; } //关闭邮槽 CloseHandle(hMailslot); system("pause"); }
运行结果(先运行服务器端程序,然后在运行客户端程序):
说明:
匿名管道是一个未命名的,单向管道,通常用来在一个父进程和一个子进程之间传输数据,匿名管道只能实现在本机上的两个进程通信,而不能实现跨网络的通信
通信过程
相关函数
CreatePipe 管道创建
函数原型
BOOL CreatePipe( PHANDLE hReadPipe, // pointer to read handle PHANDLE hWritePipe, // pointer to write handle LPSECURITY_ATTRIBUTES lpPipeAttributes, // pointer to security attributes DWORD nSize // pipe size );
参数说明:
返回值
若函数成功返回非零值
若函数失败返回0,详细消息可以调用GetLastError函数获得
代码样例:
工程目录结构:
匿名管道
|-- child
| `-- debug
| `-- child.exe
` -- parent
`-- debug
`-- parent.exe
父进程源码:
#include<windows.h> #include<iostream> #include<cstdlib> using namespace std; void main() { SECURITY_ATTRIBUTES sa; STARTUPINFO sui; PROCESS_INFORMATION pi; HANDLE hRead,hWrite; char rebuf[100]; DWORD dwRead; //创建匿名管道 sa.bInheritHandle=TRUE; sa.lpSecurityDescriptor=NULL; sa.nLength=sizeof(SECURITY_ATTRIBUTES); if(!CreatePipe(&hRead,&hWrite,&sa,0)) { cout<<"创建匿名管道失败!"<<endl; system("pause"); return; } //创建子进程并对相关子进程相关数据进行初始化 (用匿名管道的读取写入句柄赋予子进程的输入输出句柄) ZeroMemory(&sui,sizeof(STARTUPINFO)); sui.cb=sizeof(STARTUPINFO); sui.dwFlags=STARTF_USESTDHANDLES; sui.hStdInput=hRead; sui.hStdOutput=hWrite; sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); if(!CreateProcess("..\\..\\child\\debug\\child.exe",NULL,NULL,NULL,true,CREATE_NEW_CONSOLE,NULL,NULL,&sui,&pi)) { cout<<"创建子进程失败!"<<endl; system("pause"); return; } else { //关闭子进程相关句柄(进行句柄,进程主线程句柄) CloseHandle(pi.hProcess); CloseHandle(pi.hThread); Sleep(2000); //读取数据 if(!ReadFile(hRead,rebuf,100,&dwRead,NULL)) { cout<<"读取数据失败!"<<endl; system("pause"); return; } cout<<rebuf<<endl; } system("pause"); }
子进程源码:
#include<windows.h> #include<iostream> #include<cstdlib> using namespace std; void main() { HANDLE hRead,hWrite; //获得匿名管道输入输出句柄 hRead=GetStdHandle(STD_INPUT_HANDLE); hWrite=GetStdHandle(STD_OUTPUT_HANDLE); char sebuf[]=" 子进程写入管道成功"; char rebuf[100]; DWORD dwWrite; //写入数据 if(!WriteFile(hWrite,sebuf,strlen(sebuf)+1,&dwWrite,NULL)) { cout<<"写入数据失败!"<<endl; system("pause"); return; } Sleep(500); system("pause"); }
作用
命名管道不仅可以实现在本机上两个进程间的通信,还可以跨网络实现两个进程间的通信
两种通信模式
通信流程
[CreateNamePine 创建命名管道][ConnectNamePipe 创建连接命名管道][WaitNamedPipe 进行命名管道连接]
函数原型:
HANDLE CreateNamedPipe( LPCTSTR lpName, // pipe name DWORD dwOpenMode, // pipe open mode DWORD dwPipeMode, // pipe-specific modes DWORD nMaxInstances, // maximum number of instances DWORD nOutBufferSize, // output buffer size DWORD nInBufferSize, // input buffer size DWORD nDefaultTimeOut, // time-out interval LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD );
参数说明:
一个指向空终止的字符串,该字符串的格式必须是:"\\.\pine\pinename"其中该字符串开始是两个连续的反斜杠,其后的原点表示是本地机器,如果想要与远程的服务器建立连接连接,那么在原点这个位置应该指定这个远程服务器的名称.接下来是"pine"这个固定的字符串,也就是说这个字符串的内容不能修改,但其大小写是无所谓的,最后是所创建的命名管道的名称
指定管道的访问方式,重叠方式.写直通方式,还有管道句柄的安全访问方式()
用来指定管道的访问方式的标志取值如下(下面这三个值只能够取其中一个),并且管道的每一个实例都必须具有同样的访问方式
用来指定写直通方式和重叠方式的标志,取值可以是一下一个或多个组合
用来指定管道安全访问方式的标志,取值可以是一下一个或多个组合
指定管道类型,,读取和等待方式可以是下面值的组合(0为字节写字节读阻塞方式)
用于指定管道句柄的写入的标志
用于指定管道句柄的读取方式
用于指定管道句柄的等待方式(同一管道的不同实例可以采取不同的等待方式)
为管道的的最大数量,在第一次建立服务器方管道时这个参数表明该管道可以同时存在的数量。PIPE_UNLIMITED_INSTANCES表明不对数量进行限制
表示输出缓冲区的大小
表示输入缓冲区的大小
表示在等待连接时最长的等待时间(以毫秒为单位),如果在创建时设置为NMPWAIT_USE_DEFAULT_WAIT表明无限制的等待,而以后服务器方的其他管道实例也需要设置相同的值
为安全属性,一般设置为NULL。如果创建或打开失败则返回INVALID_HANDLE_VALUE。可以通过GetLastError得到错误
返回值
函数原型
BOOL ConnectNamedPipe( HANDLE hNamedPipe, // handle to named pipe LPOVERLAPPED lpOverlapped // overlapped structure );
参数说明:
返回值
如果函数成功返回非零值如果失败返回0详细消息可以调用GetLastError函数获得
函数原型
BOOL WaitNamedPipe( LPCTSTR lpNamedPipeName, // pipe name DWORD nTimeOut // time-out interval );
参数说明:
用来指定管道的名称,这个名称必须包括创建该命名管道的服务器进程所在的机器的名称,该名称的格式必须是"\\.\pine\pinename".如果在同一台机器上编写的命名管道的服务器端程序和客户端程序,则应该指定这个名称时,在开始的两个反斜杆后可以设置一个圆点,表示服务器进程在本地机器上运行;如果是跨网络通信,则在这个圆点位置处应该指定服务器端程序所在的主机名
指定超时间隔.
也就是说,如果这个参数的值是NMPWAIT_USE_DEFAULT_WAIT,并且在服务器端调用CreateNamedPipe函数创建命名管道时,设置的超时间隔为1000ms,那么一个命名管道的所有实例来说,它们必须使用同样的超时间隔
返回值
如果函数成功返回非零值如果失败返回0详细消息可以调用GetLastError函数获得
服务器:
#include<windows.h> #include<cstdlib> #include<iostream> using namespace std; void main() { HANDLE hPipe,hEvent;; DWORD dwRead,dwWrite; OVERLAPPED ovlap; char sebuf[]="this is sever!"; char rebuf[100]; /*创建命名连接*****************************************************/ hPipe=CreateNamedPipe("\\\\.\\pipe\\Communication", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0,1,1024,1024,0,NULL); if(INVALID_HANDLE_VALUE==hPipe) { cout<<"创建命名管道失败!"<<endl; hPipe=NULL; system("pause"); return; } /*创建命名管道连接*************************************************/ hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); if(!hEvent) { cout<<"创建事件对象失败!"<<endl; CloseHandle(hPipe); hPipe=NULL; system("pause"); return; } ZeroMemory(&ovlap,sizeof(OVERLAPPED)); ovlap.hEvent=hEvent; //创建管道连接 if(!ConnectNamedPipe(hPipe,&ovlap)) { if(ERROR_IO_PENDING!=GetLastError()) { cout<<"等待客户端连接失败!"<<endl; CloseHandle(hPipe); CloseHandle(hEvent); hPipe=NULL; system("pause"); return; } } //等待客户端连接 if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE)) { cout<<"等待对象失败!"<<endl; CloseHandle(hPipe); CloseHandle(hEvent); hPipe=NULL; system("pause"); return; } CloseHandle(hEvent); /*读写管道数据*****************************************************/ //写入数据 if(!ReadFile(hPipe,rebuf,100,&dwRead,NULL)) { cout<<"读取数据失败!"<<endl; system("pause"); return; } cout<<rebuf<<endl; //写入数据 if(!WriteFile(hPipe,sebuf,strlen(sebuf)+1,&dwWrite,NULL)) { cout<<"写入数据失败!"<<endl; system("pause"); return; } system("pause"); }
客户端:
#include<windows.h> #include<cstdlib> #include<iostream> using namespace std; void main() { HANDLE hPipe; DWORD dwRead,dwWrite; char sebuf[]="this is client!"; char rebuf[100]; /*连接管道连接*****************************************************/ if(!WaitNamedPipe("\\\\.\\pipe\\Communication",NMPWAIT_WAIT_FOREVER)) { cout<<"当前没有可利用的命名管道实例!"<<endl; system("pause"); return; } /*打开管道连接*****************************************************/ hPipe=CreateFile("\\\\.\\pipe\\Communication",GENERIC_READ | GENERIC_WRITE, 0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if(INVALID_HANDLE_VALUE==hPipe) { cout<<"打开命名管道失败!"<<endl; hPipe=NULL; system("pause"); return; } /*读写管道数据*****************************************************/ //写入数据 if(!WriteFile(hPipe,sebuf,strlen(sebuf)+1,&dwWrite,NULL)) { cout<<"写入数据失败!"<<endl; system("pause"); return; } //读取数据 if(!ReadFile(hPipe,rebuf,100,&dwRead,NULL)) { cout<<"读取数据失败!"<<endl; system("pause"); return; } cout<<rebuf<<endl; system("pause"); }
通信流程
[打开/关闭剪贴板][清空剪贴板][向剪贴板写入数据][从剪贴板读取数据][判断剪贴板数据格式]
函数原型
//打开剪贴板 BOOL OpenClipboard(); //关闭剪贴板 BOOL CloseClipboard();
函数原型
BOOL EmptyClipboard();
说明
只有调用了EmptyClipboard函数后,打开剪贴板的当前窗口才拥有剪贴板.EmptyClipboard函数将清空剪贴板,并释放剪贴板中的句柄,然后剪贴板的所有权分配给当前窗口.
函数原型
HANDLE SetClipboardData(UINT uFormat,HANDLE hMem);
参数说明
返回值
如果函数成功返回的是数据句柄
如果函数失败返回的是NULL,详细消息可以调用GetLastError函数获得
说明 当前调用的SetClipboardData函数的窗口必须是剪贴板的拥有着,而且在这个之前,该程序已经调用了OpenClipboard函数打开剪贴板
当一个提供的进程创建了剪贴板数据之后,知道其他进程获取剪贴板数据前,这些数据都是要占据内存空间的,如果在剪贴板上放置的数据过大,就会浪费内存空间,降低资源利用率.为了避免这种浪费,就可以采用延迟提交技术,也就是有数据提供进程先提供一个指定格式的空剪贴板数据块,即把SetClipboardData函数的hMem参数设置为NULL.当需要获取数据的进程想要从剪贴板上得到数据时,操作系统会向数据提供进程发送WM_RENDERFORMAT消息,而数据提供进程可以响应这个消息,并在此消息的响应函数中,再一次调用SetClipboardData函数,将实际的数据放到剪贴板上,当再次调用SetClipboardData函数时就不需要调用OpenClipboard函数,也不需要调用EmptyClipboard函数.也就是说为了提高资源利用率,避免浪费内存空间,可以采用延迟提交技术.第一次调用SetClipboard函数时,将其hMem参数设置为NULL,在剪贴板上以指定的剪贴板放置一个空剪贴板数据块
函数原型
HANDLE GetClipboardData( UINT uFormat );
参数说明
返回值
若函数成功返回的是剪贴板数据内容的指定格式的句柄
若函数失败返回值是NULL,详细消息可以调用GetLastError函数获得
IsClipboardFormatAvailable 判断剪贴板的数据格式
函数原型
BOOL IsClipboardFormatAvailable(UINT format);
参数说明
返回值
若剪贴板中的数据格式句柄为uFormat格式返回非零值
若剪贴板中的数据格式句柄不为uFormat格式返回零
#include<windows.h> #include<cstdlib> #include<iostream> #include<string> using namespace std; void main() { HANDLE hClip; char *pBuf; string str="this is message"; /*向剪贴板写入数据************************************************************************/ //打开剪贴板 if(OpenClipboard(NULL)) { //清空剪贴板 EmptyClipboard(); //想剪贴板写入数据 hClip=GlobalAlloc(GMEM_MOVEABLE,str.size()+1); pBuf=(char*)GlobalLock(hClip); strcpy(pBuf,str.c_str()); GlobalUnlock(hClip); SetClipboardData(CF_TEXT,hClip); //释放剪贴板 CloseClipboard(); } /*从剪贴板读取数据************************************************************************/ //打开剪贴板 if(OpenClipboard(NULL)) { //检查剪贴板中的数据格式 if(IsClipboardFormatAvailable(CF_TEXT)) { //接收数据 hClip=GetClipboardData(CF_TEXT); pBuf=(char*)GlobalLock(hClip); GlobalUnlock(hClip); cout<<pBuf<<endl; //释放剪贴板 CloseClipboard(); } } system("pause"); }
运行结果: