邮槽
通信流程:
- 服务器
- 客户端
-
注意:
- 邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输
- 邮槽可以实现一对多的单向通信,我们可以利用这个特点编写一个网络会议通知系统,而且实现这一的系统所需要编写的代码非常少.如果读者是项目经理,就可以给你手下每一位员工的机器上安装上这个系统中的邮槽服务器端程序,在你自己的机器上安装油槽的客户端程序,这样,当你想通知员工开会,就可以通过自己安装的邮槽客户端程序.将开会这个消息发送出去,因为机器上都安装了邮槽服务器端的程序,所以他们都能同时收到你发出的会议通知.采用邮槽实现这一的程序非常简单的,如果采用Sockets来实现这一的通信,代码会比较复杂
- 邮槽是一种单向通信机制,创建邮槽的服务器进程只能读取数据,打开邮槽的客户机进程只能写入数据
- 为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下
CreateMailslot函数详解
函数原型:
HANDLE CreateMailslot(
LPCTSTR lpName, // mailslot name
DWORD nMaxMessageSize, // maximum message size
DWORD lReadTimeout, // read time-out interval
LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritance option
);参数说明:
- lpName
指向一个空终止字符串的指针,该字符串指定了油槽的名称,该名称的格式必须是:"\\.\mailslot\[path]name ",其中前两个反斜杠之后的字符表示服务器所在机器的名称,圆点表示是主机;接着是硬编码的字符串:"mailslot",这个字符串不能改变,但大小写无所谓;最后是油槽的名称([path]name)由程序员起名 - nMaxMessageSize
用来指定可以被写入到油槽的单一消息的最大尺寸,为了可以发送任意大小的消息,卡伊将该参数设置为0 - lReadTimeout
指定读写操作的超时间间隔,以ms为单位,读取操作在超时之前可以等待一个消息被写入到这个油槽之中.- 如果这个值设置为0,那么若没有消息可用,该函数立即返回;
- 如果这个值设置为MAILSOT_WAIT_FOREVER,则函数一直等待,直到有消息可用
- lpSecurityAttributes
指向一个SECURITY_ATTRIBUTES结构的指针,可以简单地给这个参数传递NULL值,让系统为所创建的油槽赋予默认的安全描述符
代码样例:
服务器端源码:
#include<windows.h>
#include<cstdlib>
#include<iostream>
using namespace std; void main()
{
HANDLE hMailslot;
char buf[];
DWORD dwRead; //创建邮槽
hMailslot=CreateMailslot("\\\\.\\mailslot\\Communication",,
MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
cout<<"创建邮槽失败!"<<endl;
system("pause");
return;
} //等待用户写入数据然后读取出数据
if(!ReadFile(hMailslot,buf,,&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)+,&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
);参数说明:
- hReadPipe 作为返回类型使用,返回管道读取句柄
- hWritePipe 作为返回类型使用,返回管道写入句柄
- lpPipeAttributes 指向SECURITY_ATTRIBUTES结构体的指针,检测返回句柄是否能被子进程继承,如果此参数为NULL,则句柄不能被继承
- nSize 指定管道的缓冲区大小,改大小只是个建议值,系统将用这个值来计算一个适当的缓存区大小,如果此参数是0,系统会使用默认的缓冲区大小
返回值
若函数成功返回非零值
若函数失败返回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[];
DWORD dwRead; //创建匿名管道
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,))
{
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(); //读取数据
if(!ReadFile(hRead,rebuf,,&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[];
DWORD dwWrite; //写入数据
if(!WriteFile(hWrite,sebuf,strlen(sebuf)+,&dwWrite,NULL))
{
cout<<"写入数据失败!"<<endl;
system("pause");
return;
}
Sleep(); system("pause");
}-
命名管道
作用
命名管道不仅可以实现在本机上两个进程间的通信,还可以跨网络实现两个进程间的通信
两种通信模式
- 字节模式: 在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动
- 消息模式: 在消息模式下,客户机和服务器则通过一系列不连续的数据单位,进行数据收发,每次在管道上发一条消息后,它必须作为一条完整的消息读入
通信流程
- 服务器
- 客户端
相关函数
-
代码样例:
服务器:
#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[]; /*创建命名连接*****************************************************/
hPipe=CreateNamedPipe("\\\\.\\pipe\\Communication",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
,,,,,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,,&dwRead,NULL))
{
cout<<"读取数据失败!"<<endl;
system("pause");
return;
}
cout<<rebuf<<endl; //写入数据
if(!WriteFile(hPipe,sebuf,strlen(sebuf)+,&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[]; /*连接管道连接*****************************************************/
if(!WaitNamedPipe("\\\\.\\pipe\\Communication",NMPWAIT_WAIT_FOREVER))
{
cout<<"当前没有可利用的命名管道实例!"<<endl;
system("pause");
return;
} /*打开管道连接*****************************************************/
hPipe=CreateFile("\\\\.\\pipe\\Communication",GENERIC_READ | GENERIC_WRITE,
,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)+,&dwWrite,NULL))
{
cout<<"写入数据失败!"<<endl;
system("pause");
return;
} //读取数据
if(!ReadFile(hPipe,rebuf,,&dwRead,NULL))
{
cout<<"读取数据失败!"<<endl;
system("pause");
return;
}
cout<<rebuf<<endl; system("pause");
}-
剪贴板
通信流程
- 接收数据
- 发送数据
相关函数
程序样例:
-
#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()+);
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");
} -
运行结果: