第十七课

详细讲解进程间通讯的四种方式:剪贴板、匿名管道、命名管道和邮槽。并比较分析这几种进程间通信的优点和缺点。

进程间通信的四种方式

剪贴板

匿名管道

命名管道

邮槽

命名管道 

命名管道是通过网络来完成进程间的通信,它屏蔽了底层的网络协议细节。我们在不了解网络协议的情况下,也可以利用命名管道来实现进程间的通信。

命名管道充分利用了Windows NTWindows 2000内建的安全机制。

将命名管道作为一种网络编程方案时,它实际上建立了一个客户机/服务器通信体系,并在其中可靠地传输数据。

命名管道是围绕Windows文件系统设计的一种机制,采用“命名管道文件系统(Named Pipe File SystemNPFS)”接口,因此,客户机和服务器可利用标准的Win32文件系统函数(例如:ReadFileWriteFile)来进行数据的收发。

命名管道服务器和客户机的区别在于:服务器是唯一一个有权创建命名管道的进程,也只有它才能接受管道客户机的连接请求。而客户机只能同一个现成的命名管道服务器建立连接。

命名管道服务器只能在Windows NTWindows 2000上创建,所以,我们无法在两台Windows 95Windows 98计算机之间利用管道进行通信。不过,客户机可以是Windows 95Windows 98计算机,与Windows NTWindows 2000计算机进行连接通信。

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

邮槽

邮槽是基于广播通信体系设计出来的,它采用无连接的不可靠的数据传输。

邮槽是一种单向通信机制,创建邮槽的服务器进程读取数据,打开邮槽的客户机进程写入数据。

为保证邮槽在各种Windows平台下都能够正常工作,我们传输消息的时候,应将消息的长度限制在424字节以下。

  1. 剪切板数据交换 
  2. void CClipboardDlg::OnBtnSend()  
  3.     if(OpenClipboard())//打开剪贴板查看,并防止其他应用程序修改剪贴板的内容.  
  4.     { 
  5.         CString str; 
  6.         HANDLE hClip;    
  7.         char *pBuf; 
  8.         EmptyClipboard();//清空剪切板拥有剪切板所有权 
  9.         GetDlgItemText(IDC_EDIT_SEND,str); 
  10.         //分配一个内存对象 
  11.         hClip = GlobalAlloc(GMEM_MOVEABLE,//返回值指向进程句柄表条目值 
  12.                             str.GetLength()+1); 
  13.          
  14.         pBuf = (char *) GlobalLock(hClip); //锁定内存 
  15.         //将数据拷贝到内存中 
  16.         strcpy(pBuf,str); 
  17.         GlobalUnlock(hClip); 
  18.         SetClipboardData(CF_TEXT,hClip);//放置剪切板数据 
  19.         CloseClipboard();//记住要关闭剪贴板 
  20.     } 
  21.  
  22. void CClipboardDlg::OnBtnRecv()  
  23.     if(OpenClipboard()) 
  24.     { 
  25.         if(IsClipboardFormatAvailable(CF_TEXT))//判断剪切板的数据是否可用 
  26.         { 
  27.             HANDLE hClip; 
  28.             hClip = GetClipboardData(CF_TEXT); 
  29.             char *pBuf; 
  30.             pBuf = (char*)GlobalLock(hClip); 
  31.             GlobalUnlock(hClip);//将内存解锁 
  32.             SetDlgItemText(IDC_EDIT_RECV,pBuf); 
  33.             CloseClipboard(); 
  34.         } 
  35.     } 
  36.  
  37.  
  38. 匿名管道只能在父子之间通信 
  39. 添加dll库文件 
  40. #pragma comment( lib, "gdiplus.lib" ) 
  41. 在VC6.0时边要加入以下三句到stdafx.h里边
  42. #define ULONG_PTR ULONG#include <gdiplus.h>using namespace Gdiplus;#pragma comment( lib, "gdiplus.lib" ) 
  43.  
  44.  
  45.  
  46. 创建匿名管道 
  47. void CParentView::OnPipeCreate()  
  48.     // TODO: Add your command handler code here 
  49.     SECURITY_ATTRIBUTES sa; 
  50.     sa.bInheritHandle=TRUE; 
  51.     sa.lpSecurityDescriptor=NULL; 
  52.     sa.nLength=sizeof(SECURITY_ATTRIBUTES); 
  53.     if(!CreatePipe(&hRead,&hWrite,&sa,0)) 
  54.     { 
  55.         MessageBox("创建匿名管道失败!"); 
  56.         return
  57.     } 
  58.     STARTUPINFO sui; 
  59.     PROCESS_INFORMATION pi; 
  60.     ZeroMemory(&sui,sizeof(STARTUPINFO)); 
  61.     sui.cb=sizeof(STARTUPINFO); 
  62.     sui.dwFlags=STARTF_USESTDHANDLES; 
  63.     sui.hStdInput=hRead; 
  64.     sui.hStdOutput=hWrite; 
  65.     sui.hStdError=GetStdHandle(STD_ERROR_HANDLE); 
  66.     //创建子进程 
  67.     if(!CreateProcess("..\\Child\\Debug\\Child.exe",NULL,NULL,NULL, 
  68.             TRUE,//让子进程继承读写句柄 
  69.             0,NULL,NULL,&sui,&pi)) 
  70.     { 
  71.         CloseHandle(hRead); 
  72.         CloseHandle(hWrite); 
  73.         hRead=NULL; 
  74.         hWrite=NULL; 
  75.         MessageBox("创建子进程失败!"); 
  76.         return
  77.     } 
  78.     else 
  79.     { 
  80.         CloseHandle(pi.hProcess); 
  81.         CloseHandle(pi.hThread); 
  82.     } 
  83.  
  84. void CParentView::OnPipeRead()  
  85.     // TODO: Add your command handler code here 
  86.     char buf[100]; 
  87.     DWORD dwRead; 
  88.     if(!ReadFile(hRead,buf,100,&dwRead,NULL)) 
  89.     { 
  90.         MessageBox("读取数据失败!"); 
  91.         return
  92.     } 
  93.     MessageBox(buf); 
  94.  
  95. void CParentView::OnPipeWrite()  
  96.     // TODO: Add your command handler code here 
  97.     char buf[]="http://www.sunxin.org"
  98.     DWORD dwWrite; 
  99.     if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL)) 
  100.     { 
  101.         MessageBox("写入数据失败!"); 
  102.         return
  103.     } 
  104.  
  105.  
  106.  
  107.  
  108. void CChildView::OnInitialUpdate() //获取标准输入输出句柄 
  109.     CView::OnInitialUpdate(); 
  110.      
  111.     // TODO: Add your specialized code here and/or call the base class 
  112.     hRead=GetStdHandle(STD_INPUT_HANDLE); 
  113.     hWrite=GetStdHandle(STD_OUTPUT_HANDLE); 
  114.  
  115. void CChildView::OnPipeRead()  
  116.     // TODO: Add your command handler code here 
  117.     char buf[100]; 
  118.     DWORD dwRead; 
  119.     if(!ReadFile(hRead,buf,100,&dwRead,NULL)) 
  120.     { 
  121.         MessageBox("读取数据失败!"); 
  122.         return
  123.     } 
  124.     MessageBox(buf); 
  125.  
  126. void CChildView::OnPipeWrite()  
  127.     // TODO: Add your command handler code here 
  128.     char buf[]="匿名管道测试程序"
  129.     DWORD dwWrite; 
  130.     if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL)) 
  131.     { 
  132.         MessageBox("写入数据失败!"); 
  133.         return
  134.     } 
  135.  
  136.  
  137.  
  138. 命名管道 
  139. CNamedPipeSrvView::CNamedPipeSrvView() 
  140.     // TODO: add construction code here 
  141.     hPipe=NULL; 
  142.  
  143. CNamedPipeSrvView::~CNamedPipeSrvView() 
  144.     if(hPipe) 
  145.         CloseHandle(hPipe); 
  146.  
  147. void CNamedPipeSrvView::OnPipeCreate()  
  148.     // TODO: Add your command handler code here 
  149.     //创建命名管道 
  150.     hPipe=CreateNamedPipe("\\\\.\\pipe\\MyPipe"
  151.         PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 
  152.         0,1,1024,1024,0,NULL); 
  153.     if(INVALID_HANDLE_VALUE==hPipe) 
  154.     { 
  155.         MessageBox("创建命名管道失败!"); 
  156.         hPipe=NULL; 
  157.         return
  158.     } 
  159.     HANDLE hEvent; 
  160.     hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); 
  161.     if(!hEvent) 
  162.     { 
  163.         MessageBox("创建事件对象失败!"); 
  164.         CloseHandle(hPipe); 
  165.         hPipe=NULL; 
  166.         return
  167.     } 
  168.     OVERLAPPED ovlap; 
  169.     ZeroMemory(&ovlap,sizeof(OVERLAPPED)); 
  170.     ovlap.hEvent=hEvent; 
  171.     if(!ConnectNamedPipe(hPipe,&ovlap)) 
  172.     { 
  173.         if(ERROR_IO_PENDING!=GetLastError()) 
  174.         { 
  175.             MessageBox("等待客户端连接失败!"); 
  176.             CloseHandle(hPipe); 
  177.             CloseHandle(hEvent); 
  178.             hPipe=NULL; 
  179.             return
  180.         } 
  181.     } 
  182.     if(WAIT_FAILED==WaitForSingleObject(hEvent,INFINITE)) 
  183.     { 
  184.         MessageBox("等待对象失败!"); 
  185.         CloseHandle(hPipe); 
  186.         CloseHandle(hEvent); 
  187.         hPipe=NULL; 
  188.         return
  189.     } 
  190.     CloseHandle(hEvent); 
  191.  
  192. void CNamedPipeSrvView::OnPipeRead()  
  193.     // TODO: Add your command handler code here 
  194.     char buf[100]; 
  195.     DWORD dwRead; 
  196.     if(!ReadFile(hPipe,buf,100,&dwRead,NULL)) 
  197.     { 
  198.         MessageBox("读取数据失败!"); 
  199.         return
  200.     } 
  201.     MessageBox(buf); 
  202.  
  203. void CNamedPipeSrvView::OnPipeWrite()  
  204.     // TODO: Add your command handler code here 
  205.     char buf[]="http://www.sunxin.org"
  206.     DWORD dwWrite; 
  207.     if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL)) 
  208.     { 
  209.         MessageBox("写入数据失败!"); 
  210.         return
  211.     } 
  212.  
  213.  
  214. 客户端 
  215. CNamedPipeCltView::CNamedPipeCltView() 
  216.     // TODO: add construction code here 
  217.     hPipe=NULL; 
  218.  
  219. CNamedPipeCltView::~CNamedPipeCltView() 
  220.     if(hPipe) 
  221.         CloseHandle(hPipe); 
  222.  
  223. void CNamedPipeCltView::OnPipeConnect()  
  224.     // TODO: Add your command handler code here 
  225.     if(!WaitNamedPipe("\\\\.\\pipe\\MyPipe",NMPWAIT_WAIT_FOREVER)) 
  226.     { 
  227.         MessageBox("当前没有可利用的命名管道实例!"); 
  228.         return
  229.     } 
  230.     hPipe=CreateFile("\\\\.\\pipe\\MyPipe",GENERIC_READ | GENERIC_WRITE, 
  231.         0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
  232.     if(INVALID_HANDLE_VALUE==hPipe) 
  233.     { 
  234.         MessageBox("打开命名管道失败!"); 
  235.         hPipe=NULL; 
  236.         return
  237.     } 
  238.  
  239. void CNamedPipeCltView::OnPipeRead()  
  240.     // TODO: Add your command handler code here 
  241.     char buf[100]; 
  242.     DWORD dwRead; 
  243.     if(!ReadFile(hPipe,buf,100,&dwRead,NULL)) 
  244.     { 
  245.         MessageBox("读取数据失败!"); 
  246.         return
  247.     } 
  248.     MessageBox(buf); 
  249.  
  250. void CNamedPipeCltView::OnPipeWrite()  
  251.     // TODO: Add your command handler code here 
  252.     char buf[]="命名管道测试程序"
  253.     DWORD dwWrite; 
  254.     if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL)) 
  255.     { 
  256.         MessageBox("写入数据失败!"); 
  257.         return
  258.     } 
  259.  
  260.  
  261. 邮槽通信 
  262.  
  263. void CMailslotSrvView::OnMailslotRecv()  
  264.     // TODO: Add your command handler code here 
  265.     HANDLE hMailslot; 
  266.     hMailslot=CreateMailslot("\\\\.\\mailslot\\MyMailslot",0, 
  267.         MAILSLOT_WAIT_FOREVER,NULL); 
  268.     if(INVALID_HANDLE_VALUE==hMailslot) 
  269.     { 
  270.         MessageBox("创建油槽失败!"); 
  271.         return
  272.     } 
  273.     char buf[100]; 
  274.     DWORD dwRead; 
  275.     if(!ReadFile(hMailslot,buf,100,&dwRead,NULL)) 
  276.     { 
  277.         MessageBox("读取数据失败!"); 
  278.         CloseHandle(hMailslot); 
  279.         return
  280.     } 
  281.     MessageBox(buf); 
  282.     CloseHandle(hMailslot); 
  283. 客户端 
  284. void CMailslotCltView::OnMailslotSend()  
  285.     // TODO: Add your command handler code here 
  286.     HANDLE hMailslot; 
  287.     hMailslot=CreateFile("\\\\.\\mailslot\\MyMailslot",GENERIC_WRITE, 
  288.         FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
  289.     if(INVALID_HANDLE_VALUE==hMailslot) 
  290.     { 
  291.         MessageBox("打开油槽失败!"); 
  292.         return
  293.     } 
  294.     char buf[]="http://www.sunxin.org"
  295.     DWORD dwWrite; 
  296.     if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL)) 
  297.     { 
  298.         MessageBox("写入数据失败!"); 
  299.         CloseHandle(hMailslot); 
  300.         return
  301.     } 
  302.     CloseHandle(hMailslot);