记录一些学习小事

Work hard

统计

留言簿

阅读排行榜

评论排行榜

进程通信——邮槽和命名管道

今天记录下自己学的邮槽和命名管道,学习过程中遇到点问题也拿出来分享下。哈  开整
先说一下大体的概念奥。

邮槽定义

邮槽(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);
}

下图程序运行效果图

哇卡卡阿卡














 

posted on 2011-08-01 22:14 陈晓 阅读(5240) 评论(6)  编辑 收藏 引用

评论

# re: 进程通信——邮槽和命名管道 2011-08-02 12:18 yotta123

没事还是不要用油槽!  回复  更多评论   

# re: 进程通信——邮槽和命名管道 2011-08-02 12:35 陈晓

恩,我上边也说了,没什么大用,就是供学习用用。书上都说什么可以广播什么的方便,但是我用这玩意两台机器通信都没搞上。。@yotta123
  回复  更多评论   

# re: 进程通信——邮槽和命名管道 2013-12-31 21:33 MeiJi

楼主好,刚才试了一下在管道的实例中,其他正常,但是服务器的读或者client的接受函数会出错,然后调试的时候发现readfile()第一个参数句柄无效(调试直接弹出中断或继续对话框),不晓得为什么。write();的句柄是有效地。忘答复,谢谢  回复  更多评论   

# re: 进程通信——邮槽和命名管道 2014-10-11 16:56 Mr zhang

最好还是封装一下。  回复  更多评论   

# re: 进程通信——邮槽和命名管道 2015-07-22 14:50 sad

MeiJi用户,我觉得是不是你的事件对象搞错了,这里是自动复位,初始为无信号,等待连接再激活这个信号,如果开始为有信号经过重叠对象可能会出错  回复  更多评论   

# re: 进程通信——邮槽和命名管道 2015-07-22 14:54 sad

楼主!你的命名通道试过两台电脑通讯过吗?成功没?我都没试过,全在自己电脑上调试的‘  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理