进程间的通信

进程间的通信
位于同一个进程中的多个线程共享同一个地址空间,因此线程之间的通信非常简单。然而,由于每个进程所拥有的4GB地址空间都是私有的,一个进程不能访问另一个进程地址空间中的数据,因此进程间的通信就比较困难。SOCKET也是进程间通信的一种方式(详见http://blog.csdn.net/walkerkalr/article/details/19443921)。本文将讲述进程间通信的其他方式(剪贴板,匿名管道,命名管道,油槽)。

1.剪贴板
剪贴板实际上是系统维护管理的一块内存区域,当在一个进程中复制数据时,是将这个数据放到该块内存区域中,当在另一个进程中黏贴数据时,是从该内存块区域中取出数据,然后显示在窗口上。

编写利用剪贴板通信的程序:
1)新建一个基于对话框的应用程序,工程名取为Clipboard
2)设计对话框界面
编辑框IDC_EDIT_SEND
编辑框IDC_EDIT_RECV
按钮IDC_BTN_SEND
按钮IDC_BTN_RECV
3)数据发送程序块设计
1>在把数据放置到剪贴板之前,首先需要打开剪贴板,这可以利用CWnd类的OpenClipBorad成员函数实现,该函数的原型声明如下:
BOOL OpenClipboard( );
如果某个程序打开了剪贴板,则其他程序将不能修改剪贴板,直到前者调用了BOOL CloseClipboar(),并且只有调用了BOOL EmptyClipboard(void)函数清空剪贴板,并释放剪贴板中数据的句柄,然后剪贴板的所有权分配给当前打开剪贴板的窗口。
2>放置数据
HANDLE SetClipboardData(
UINT uFormat,
HANDLE hMem);
3>数据发送

void CClipboardDlg::OnBtnSend() 
{
	// TODO: Add your control notification handler code here
	if(OpenClipboard())
	{
		CString str;
		HANDLE hClip;
		char *pBuf;
		EmptyClipboard();
		GetDlgItemText(IDC_EDIT_SEND,str);
		hClip=GlobalAlloc(GMEM_MOVEABLE,str.GetLength()+1);
		pBuf=(char*)GlobalLock(hClip);
		strcpy(pBuf,str);
		GlobalUnlock(hClip);
		SetClipboardData(CF_TEXT,hClip);
		CloseClipboard();
	}
}
4>数据接受
void CClipboardDlg::OnBnClickedBtnRecv()
{
	// TODO: Add your control notification handler code here
		if(OpenClipboard())
	{
		if(IsClipboardFormatAvailable(CF_TEXT))
		{
			HANDLE hClip;
			char *pBuf;
			hClip=GetClipboardData(CF_TEXT);
			pBuf=(char*)GlobalLock(hClip);
			GlobalUnlock(hClip);
			SetDlgItemText(IDC_EDIT_RECV,pBuf);
			CloseClipboard();
		}
	}
}

2.匿名管道
2.1创建管道和创建进程
匿名管道是一个未命名的、单向管道,通常用来在一个父进程和一个子进程之间传输数据。匿名管道只能实现本地机器上网络的通信,而不能实现跨网络的通信。
BOOL CreatePipe(//创建管道
PHANDLE hReadPipe, // read handle
PHANDLE hWritePipe, // write handle
LPSECURITY_ATTRIBUTES lpPipeAttributes, // security attributes
DWORD nSize // pipe size
);

CreateProcess( //创建进程
LPCWSTR lpszImageName,
LPCWSTR lpszCmdLine,
LPSECURITY_ATTRIBUTES lpsaProcess,
LPSECURITY_ATTRIBUTES lpsaThread,
BOOL fInheritHandles,
DWORD fdwCreate,
LPVOID lpvEnvironment,
LPWSTR lpszCurDir,
LPSTARTUPINFOW lpsiStartInfo,
LPPROCESS_INFORMATION lppiProcInfo);

2.2父进程的实现:

1)创建单文档类型MFC应用程序

2)创建菜单一个名为“匿名管道”子菜单,添加三个菜单项,并分别为他们添加命令函数,如下


命名相应函数:

void CParentView::OnPipeCreaate()

{

//TODO: Add your command handler code here

}

void CParentView::OnPipeRead()

{

//TODO: Add your command handler code here

}

void CParentView::OnPipeWrite()

{

//TODO: Add your command handler code here

}

3)为ParentView类:添加两个成员变量

private:

HANDLEhWrite;

HANDLEhRead;

并在构造和析构函数做如下操作:

CParentView::CParentView()
{
         //TODO: add construction code here
         hRead= NULL;
         hWrite= NULL;
}
 
CParentView::~CParentView()
{
         if(hRead)
                   CloseHandle(hRead);
         if(hWrite)
                   CloseHandle(hrite);
}

4)创建匿名管道

void CParentView::OnPipeCreaate()
{
         //TODO: Add your command handler code here
         SECURITY_ATTRIBUTESsa;
         sa.bInheritHandle=TRUE;
         sa.lpSecurityDescriptor=NULL;
         sa.nLength=sizeof(SECURITY_ATTRIBUTES);
         if(!CreatePipe(&hRead,&hWrite,&sa,0))
         {
                   MessageBox("创建匿名管道失败!");
                   return;
         }
         STARTUPINFOsui;
         PROCESS_INFORMATIONpi;
         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,0,NULL,NULL,&sui,&pi))
         {
                   CloseHandle(hRead);
                   CloseHandle(hWrite);
                   hRead=NULL;
                   hWrite=NULL;
                   MessageBox("创建子进程失败!");
                   return;
         }
         else
         {
                   CloseHandle(pi.hProcess);
                   CloseHandle(pi.hThread);
         }
}
 
void CParentView::OnPipeRead()
{
         //TODO: Add your command handler code here
         charbuf[100];
         DWORDdwRead;
         if(!ReadFile(hRead,buf,100,&dwRead,NULL))
         {
                   MessageBox("读取数据失败!");
                   return;
         }
         MessageBox(buf);
}
 
void CParentView::OnPipeWrite()
{
         //TODO: Add your command handler code here
         charbuf[]="http://www.sunxin.org";
         DWORDdwWrite;
         if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
         {
                   MessageBox("写入数据失败!");
                   return;
         }
}
以上就是利用匿名管道实现父子进程间通信的父进程程序的实现,在父进程中创建匿名管道,返回该管道的读、写句柄,然后调用CreateProcess函数启动子进程,并且将子进程的标准输入和标准输出句柄设置为匿名管道的读、写句柄,相当于将该管道的读写句柄作了一个标记,传递给子进程,然后在子进程中得到自己的标准输入和标准输出句柄时,相当于得到了匿名管道的读,写句柄。

2.3子进程实现

1)创建一个名为Child的单文档MFC应用程序

2)添加菜单项(不添加创建进程函数)和响应函数

3)添加成员变量,并在构造函数和析构函数中做响应处理

4)获得管道的读取和写入句柄

void CChildView::OnInitialUpdate()

{

// TODO: Addyour specialized code here and/or call the base class

CView::OnInitialUpdate();

hRead= GetStdHandle(STD_INPUT_HANDLE);

hWrite= GetStdHandle(STD_OUTPUT_HANDLE);

}

5)读取数据

void CChildView::OnPipeRead()
{
         //TODO: Add your command handler code here
         charbuf[100];
         DWORDdwRead;
         if(!ReadFile(hRead,buf,100,&dwRead,NULL))
         {
                   MessageBox("读取数据失败!");
                   return;
         }
         MessageBox(buf);
}
6)写入数据

void CChildView::OnPipeWrite()
{
         //TODO: Add your command handler code here
         charbuf[]="匿名管道测试程序";
         DWORDdwWrite;
         if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
         {
                   MessageBox("写入数据失败!");
                   return;
         }
}
2.4测试

运行父进程,单击创建进程菜单项,在出现的子进程窗口中单击写入数据,再在父窗口中单击读取数据,运行结果如下


以上就是对匿名管道的使用,它主要是用来在父子进程之间进行通信。利用匿名管道实现父子进程间通信时,需要注意一点:因为匿名管道没有名称,所以只能在父进程中调用CreateProcess函数创建子进程时,将管道的读、写句柄传递给子进程。

3.命名管道

命名管道通过网络完成进程间的通信,它屏蔽了底层的网络协议细节。相对于匿名官大,命名管道不仅可以在本机实现两个进程间的通信,还可以跨网络试下两个进程间的通信。相对于Sockets,命名管道不需要编写身份验证的代码。命名管道的服务器和客户机之间的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个线程的命名管道服务器建立连接。

命名管道提供两种基本通信模式:字节模式和消息模式。在字节模式下,数据以一个连续的字节流的形式在客户机和服务器之间流动。而在消息模式下,客户机和服务器则通过一些列不连续的数据单位,进行数据收发,每次在管道上发出一条消息后,它必须作为一条完整的消息读入。

相关函数:

HANDLE CreateNamedPipe( //创建命名管道

LPCTSTR lpName, // pipe name

DWORD dwOpenMode, // pipe open mode

DWORD dwPipeMode, // pipe-specificmodes

DWORD nMaxInstances, // maximum number ofinstances

DWORD nOutBufferSize, // output buffer size

DWORD nInBufferSize, // input buffer size

DWORD nDefaultTimeOut, // time-out interval

LPSECURITY_ATTRIBUTES lpSecurityAttributes // SD

);

3.1服务器端程序

1)创建单文档MFC应用程序

2)添加菜单项

3)增加成员变量及其在构造函数和析构函数中相应的处理

4)创建命名管道

void CNamedPipeSrvView::OnPipeCreate()
{
                    // TODO:Add your command handler code here
                    hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe",
                                         PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED,
                                         0,1,1024,1024,0,NULL);
                    if(INVALID_HANDLE_VALUE==hPipe)
                    {
                                         MessageBox("创建命名管道失败!");
                                         hPipe=NULL;
                                         return;
                    }
                    HANDLEhEvent;
                    hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
                    if(!hEvent)
                    {
                                         MessageBox("创建事件对象失败!");
                                         CloseHandle(hPipe);
                                         hPipe=NULL;
                                         return;
                    }
                    OVERLAPPEDovlap;
                    ZeroMemory(&ovlap,sizeof(OVERLAPPED));
                    ovlap.hEvent=hEvent;
                    if(!ConnectNamedPipe(hPipe,&ovlap))
                    {
                                         if(ERROR_IO_PENDING!=GetLastError())
                                         {
                                                             MessageBox("等待客户端连接失败!");
                                                             CloseHandle(hPipe);
                                                             CloseHandle(hEvent);
                                                             hPipe=NULL;
                                                             return;
                                         }
                    }
                    if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE))
                    {
                                         MessageBox("等待对象失败!");
                                         CloseHandle(hPipe);
                                         CloseHandle(hEvent);
                                         hPipe=NULL;
                                         return;
                    }
                    CloseHandle(hEvent);
}

5)读取数据

void CNamedPipeSrvView::OnPipeRead()
{
                    // TODO:Add your command handler code here
                    charbuf[100];
                    DWORDdwRead;
                    if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
                    {
                                         MessageBox("读取数据失败!");
                                         return;
                    }
                    MessageBox(buf);
}
6)写入数据

void CNamedPipeSrvView::OnPipeWrite()
{
                    // TODO:Add your command handler code here
                    char buf[]="http://www.sunxin.org";
                    DWORDdwWrite;
                    if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
                    {
                                         MessageBox("写入数据失败!");
                                         return;
                    }
}
3.2客户端程序
1)创建单文档MFC应用程序
2)添加菜单项
3)增加成员变量及其在构造函数和析构函数中相应的处理
4)连接命名管道
void CNamedPipeCltView::OnPipeConnect()
{
                    // TODO:Add your command handler code here
                    if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER))
                    {
                                         MessageBox("当前没有可利用的命名管道实例!");
                                         return;
                    }
                    hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ| GENERIC_WRITE,
                                         0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
                    if(INVALID_HANDLE_VALUE==hPipe)
                    {
                                         MessageBox("打开命名管道失败!");
                                         hPipe=NULL;
                                         return;
                    }
}
5)读取数据

void CNamedPipeCltView::OnPipeRead()
{
                    // TODO:Add your command handler code here
                    charbuf[100];
                    DWORDdwRead;
                    if(!ReadFile(hPipe,buf,100,&dwRead,NULL))
                    {
                                         MessageBox("读取数据失败!");
                                         return;
                    }
                    MessageBox(buf);
}
6)写入数据

void CNamedPipeCltView::OnPipeWrite()
{
                    // TODO:Add your command handler code here
                    charbuf[]="命名管道测试程序";
                    DWORDdwWrite;
                    if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
                    {
                                         MessageBox("写入数据失败!");
                                         return;
                    }
}
7)测试如下


以上就是采用命名管道完成进程通信的实现,具体的过程是:在服务器端调用CreateNamePipe创建命名管道之后,调用ConnectNamedPipe函数让服务器端进程等待客户端进程连接到该命名管道的实例上。在客户端,首先调用WaitNamedPipe函数判断当前是否有可以利用的命名管道,如果有,就调用Create函数打开该命名管道的实例,并建立一个连接。

4.邮槽

邮槽是基于广播体系设计出来的,它采用无连接的不可靠的数据连接。邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据。邮槽是基于广播通信的。

HANDLE CreateMailslot(

LPCTSTR lpName, // mailslot name

DWORD nMaxMessageSize, // maximum message size

DWORD lReadTimeout, // read time-outinterval

LPSECURITY_ATTRIBUTES lpSecurityAttributes // inheritanceoption

);

服务器端程序:

   voidCMailsotSrv1View::OnMailslotrecv()
{
                    // TODO:Add your command handler code here
                                         HANDLEhMailslot;
                    hMailslot=CreateMailslot("\\\\.\\mailslot\\MyMailslot",0,
                                         MAILSLOT_WAIT_FOREVER,NULL);
                    if(INVALID_HANDLE_VALUE==hMailslot)
                    {
                                         MessageBox("创建油槽失败!");
                                         return;
                    }
                    charbuf[100];
                    DWORDdwRead;
                    if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
                    {
                                         MessageBox("读取数据失败!");
                                         CloseHandle(hMailslot);
                                         return;
                    }
                    MessageBox(buf);
                    CloseHandle(hMailslot);
}

客户端程序:

void CMailslotClt1View::OnMailslotSend()
{
                    // TODO:Add your command handler code here\
                                         HANDLEhMailslot;
                    hMailslot=CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE,
                                         FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
                    if(INVALID_HANDLE_VALUE==hMailslot)
                    {
                                         MessageBox("打开油槽失败!");
                                         return;
                    }
                    charbuf[]="http://www.sunxin.org";
                    DWORDdwWrite;
                    if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
                    {
                                         MessageBox("写入数据失败!");
                                         CloseHandle(hMailslot);
                                         return;
                    }
                    CloseHandle(hMailslot);
}
测试:





==参考 VC++深入详解

==转载请注明出处,谢谢!



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值