今天记录下自己学的邮槽和命名管道,学习过程中遇到点问题也拿出来分享下。哈 开整
先说一下大体的概念奥。
邮槽定义
邮槽(Mailslot)也称为邮件槽,是Windows 提供的进程间通信的手段,
其提供的是基于不可靠的,邮件槽只支持单向数据传输,也就是服务器只能接收数据,而客户端只能发送数据,
何为服务端?创建邮槽的那一端就是服务端。
还有需要提及的一点是,客户端在使用邮槽发送数据的时候只有当数据的长度 < 425 字节时,
才可以被广播给多个服务器,如果消息的长度 > 425 字节的话,那么在这种情形下,邮槽是不支持广播通信的。
这是我看到的邮槽的简要说明吧。
先说下邮槽的使用过程吧。然后再分析函数,在贴代码。非常简单哦
服务端: 客户端:
首先创建邮槽CreateMailslot 打开油槽CreateFile
读取数据 ReadFile 写入数据WriteFile
完事了,只有这四个函数。也很容易理解。 客户端写入数据 服务端读取数据。
CreateMailslot(_T("\\\\.\\mailslot\\chenxiao"),0, MAILSLOT_WAIT_FOREVER,NULL);
第一个参数是个固定格式\\.\\mailslot\\name 点代表本机。mailslot是硬编码 不能变,name可以自己起个邮槽的名字。‘\’放入字符串中要用转义字符\
所以就写成了"\\\\.\\mailslot\\chenxiao"
第二个参数To specify that the message can be of any size, set this value to zero. 设置成0
第三个参数为了下面的读取操作应该等待的时间 MAILSLOT_WAIT_FOREVER 传这个代表参数代表永久等待。
最后一个参数安全属性 嘎嘎 null
ReadFile(hMailSlot,pData,sizeof(TCHAR)*80,&dByteRead,NULL);
这几个参数很简单了。第一个参数就是创建邮槽返回来的句柄 第二个参数一个[out]buffer用来接收从邮槽中读出来的东东。第三个参数就是读取多少个字节。
第四个参数基本没用,是一个[out]的LPDWord 很蛋疼只能DWORD dByteRead; 然后传个他的地址。
因为msdn上说了If lpOverlapped is NULL, lpNumberOfBytesRead cannot be NULL;
lpoverlapped就是我们的最后一个参数,这个参数可以设置同步和异步,如果文件打开模式是FILE_FLAG_OVERLAPPED这个的话,我们这个就不可以是NULL
这个同步异步问题我在下面的命名管道中在说。这里就先过去。这个参数设成NULL。
客户端函数
CreateFile(_T("\\\\.\\mailslot\\chenxiao"),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
这几个参数也很容易理解了。第一个参数要和创建邮槽的时候的参数一样。如果要远程通信的话可以把‘.’设置成服务器 主机名 或者在一个区域内广播‘*’
但是我用两个机器实验了,没有成功,目前我只能用邮槽在本地一个机器上通信。。。尴尬。。有知道怎么在两个机器上通信的,要给我留言教教我哦。
后几个参数根据参数名大家就可以猜个差不多了,我就不说了。吼吼。
WriteFile(hMailSlot,str,sizeof(TCHAR)*80,&dByteWrite,NULL);这个函数 跟 readfile差不多 就是向邮槽中写入数据用的。
第二个参数是要写入的内容,第三个是大小(以字节为单位).
好了这几个函数都说完了。贴上小代码,就清晰了。
//服务器端 我用的c++写的。
#include <iostream>
#include <Windows.h>
#include <tchar.h>
using namespace std;
int main()
{
HANDLE hMailSlot=CreateMailslot(_T("\\\\.\\mailslot\\chenxiao"),0,
MAILSLOT_WAIT_FOREVER,NULL);
TCHAR pData[80];
ZeroMemory(pData,sizeof(TCHAR)*80);
DWORD dByteRead;
while(1)
{
BOOL b=ReadFile(hMailSlot,pData,sizeof(TCHAR)*80,&dByteRead,NULL);
wprintf_s(_T("%s\n"),pData);
}
system("pause");
return 0;
}
//客户端我在mfc中写的。
void CclientDlg::OnBnClickedButtonSend()
{
TCHAR str[80];
ZeroMemory(str,sizeof(TCHAR)*80);
GetDlgItem(IDC_EDIT_INPUT)->GetWindowText(str,70);
DWORD dByteWrite;
HANDLE hMailSlot=CreateFile(_T("\\\\.\\mailslot\\chenxiao"),GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
if (hMailSlot==INVALID_HANDLE_VALUE)
{
MessageBox(_T("createfile失败,请打开服务器"));
return ;
}
BOOL b=WriteFile(hMailSlot,str,sizeof(TCHAR)*80,&dByteWrite,NULL);
GetDlgItem(IDC_EDIT_INPUT)->SetWindowText(_T(""));
CloseHandle(hMailSlot);
}
这就是运行结果啦。这个东西没啥大用。就是学习一下而已。以后万一用到也能弄弄。
下面我说下命名管道。这个东西坑了我一下午。。。
邮槽建立的是无连接的通信。。那么命名管道 就是有链接的可靠的通信了。他跟邮槽挺相似的。但是比邮槽好很多。
同上面。我粘一些概念性的东西。
命名管道是通过网络来完成进程之间的通信的,命名管道依赖于底层网络接口,
其中包括有 DNS 服务,TCP/IP 协议等等机制,但是其屏蔽了底层的网络协议细节,
对于匿名管道而言,其只能实现在父进程和子进程之间进行通信,而对于命名管道而言,
其不仅可以在本地机器上实现两个进程之间的通信,还可以跨越网络实现两个进程之间的通信。
命名管道使用了 Windows 安全机制,因而命名管道的服务端可以控制哪些客户有权与其建立连接,
而哪些客户端是不能够与这个命名管道建立连接的。
利用命名管道机制实现不同机器上的进程之间相互进行通信时,
可以将命名管道作为一种网络编程方案时,也就是看做是 Socket 就可以了,
它实际上是建立了一个客户机/服务器通信体系,并在其中可靠的传输数据。
命名管道的通信是以连接的方式来进行的,
服务器创建一个命名管道对象,然后在此对象上等待连接请求,
一旦客户连接过来,则两者都可以通过命名管道读或者写数据。
命名管道提供了两种通信模式:字节模式和消息模式。
在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动,
而在消息模式下,客户机和服务器则通过一系列的不连续的数据单位,进行数据的收发,
每次在管道上发出一个消息后,它必须作为一个完整的消息读入。
我相信很多人看了几句就跳到这里来了。。概念性的东西 确实太不好玩了。我也不爱看。。哈哈
介绍命名管道需要的函数。
服务器端
CreateNamedPipe 创建命名管道
ConnectNamedPip 连接
ReadFile 读
WriteFile 写
客户端
WaitNamedPipe 查看命名管道
CreateFile 打开命名管道
WriteFile ReadFile 写 读
就这些东西,今天由于不仔细看msdn 写程序写蒙了。。。等会我在说啊。大家要注意哦。
CreateNamedPipe(_T("\\\\.\\pipe\\chenxiao"),PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,PIPE_TYPE_BYTE,1,1024,1024,2000,NULL);
很多参数啊!不怕不怕 慢慢来
第一个我略过了哦。第二个大家在msdn上可以看到有
PIPE_ACCESS_DUPLEX 读写双向
PIPE_ACCESS_INBOUND 数据只能从客户端到服务端
PIPE_ACCESS_OUTBOUND 和上面那个相反
这个参数我们设置成 第一个。然而通过msdn大家可以看到这个参数可以附加 flag 我们就附加FILE_FLAG_OVERLAPPED 这个了
MSDN那一大堆英文我也瞅不太明白,大至意思就是这个呢 用了这个参数 程序操作读,写,连接等操作,可以立马返回。比如说读一个大文件吧
你把这个文件从头读到伟 需要很长时间。这样的话你的readfile函数就不会反回 就会阻塞在那里一直读。这样很不好,所以有了这个参数。这个参数就是使你的读 写 等待函数立马返回,这个就属于程序的异步,这个读函数和主程序一起执行。
下一个参数就是以字节流还是消息方式发送文件 读取文件。我们采用字节流方式PIPE_TYPE_BYTE。
下一个参数是最多可以创建几个命名管道 比如我们设置成3,就是可以创建3个这样的管道。我们这里设置成1,我们只用一个管道做演示就行。然后是分配的输入 输出 缓冲区大小 ,就类似创建线程时分配栈空间大小一样。然后是一个超时时间设置 这个设置成0就可以。最后一个NULL安全属性
ConnectNamedPipe服务端的连接管道函数这个函数两个参数第一个参数句柄,第二个参数一个结构体对象
这个结构体呢 里面有一个事件句柄。刚才上边由于设置了异步,所以你要有一个标志着读结束的标志,这个标志就用的这个事件。创建这个事件要设置成手动的,初始为无信号。
这样服务端的就写完了。
然后再说一下客户端的函数
WaitNamedPipe(_T("\\\\.\\pipe\\chenxiao"),0);
这个函数呢就属于一个查看函数,看看有没有叫chenxiao的命名管道
大家不要认为这个函数可以打开命名管道 或者连接管道
大家从msdn上可以看到这句话If the function succeeds,the process should use the CreateFile function to open a handle to the named pipe
今天我由于没看到这句话苦苦弄了一个下午也没连上管道5555555555
在客户端可以用waitnamedpipe检查下有没有这个管道 然后再createfile打开它。
哦了 搞定了。搞上我的代码瞅瞅效果。
//服务器端的代码 MFC写的
void CPipeServerDlg::OnBnClickedButtonCreate()
{
m_hNP=CreateNamedPipe(_T("\\\\.\\pipe\\chenxiao"),
PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE,1,1024,1024,0,NULL);
if (m_hNP==INVALID_HANDLE_VALUE)
{
MessageBox(_T("创建管道失败"));
}
else
{
MessageBox(_T("创建管道成功"));
}
//连接-----------------------------
OVERLAPPED op;
ZeroMemory(&op,sizeof(OVERLAPPED));
op.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
BOOL b=ConnectNamedPipe(m_hNP,&op);
if (WaitForSingleObject(op.hEvent,INFINITE)==0)
{
MessageBox(_T("connect成功 haha"));
}
else
{
MessageBox(_T("create fail"));
}
}
void CPipeServerDlg::OnBnClickedButtonWrite()
{
TCHAR buff[100]=_T("来自服务器的信息");
DWORD d;
WriteFile(m_hNP,buff,200,&d,NULL);
}
void CPipeServerDlg::OnBnClickedButtonRead()
{
TCHAR buff[100];
ZeroMemory(buff,200);
DWORD d;
ReadFile(m_hNP,buff,200,&d,NULL);
MessageBox(buff);
}
//客户端的代码 MFC写的
void CPipeClientDlg::OnBnClickedButtonOpenpipe()
{
BOOL b=WaitNamedPipe(_T("\\\\.\\pipe\\chenxiao"),0);
//BOOL b=1;
m_hFile = CreateFile(_T("\\\\.\\pipe\\chenxiao"),
GENERIC_READ | GENERIC_WRITE,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!b||m_hFile==INVALID_HANDLE_VALUE)
{
MessageBox(_T("连接失败"));
}
else
{
MessageBox(_T("连接成功"));
}
}
void CPipeClientDlg::OnBnClickedButtonRecieve()
{
TCHAR buff[100];
ZeroMemory(buff,200);
DWORD d;
ReadFile(m_hFile,buff,200,&d,NULL);
MessageBox(buff);
}
void CPipeClientDlg::OnBnClickedButtonSend()
{
TCHAR buff[100]=_T("client's message");
DWORD d;
WriteFile(m_hFile,buff,200,&d,NULL);
}
下图程序运行效果图
哇卡卡阿卡