进程间的通信

来源:互联网 发布:算法研发工程师路线 编辑:程序博客网 时间:2024/04/30 07:55

进程间的通信
     位于同一个进程中的多个线程共享同一个地址空间,因此线程之间的通信非常简单。然而,由于每个进程所拥有的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 hereif(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 hereif(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
原创粉丝点击