葡京网投哪个正规 > 首页 > 命名管道和邮槽,Windows系统编程之进程间通信

原标题:命名管道和邮槽,Windows系统编程之进程间通信

浏览次数:161 时间:2020-03-25

第17课 进程间通信
有四种方法
1.剪贴板
   a.创建个ClipBoard的对话框应用程序,加两EditBox和两个Button发送接收。
   b.具体代码:
     发送端代码:
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();
}
      接收端代码:
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.匿名管道:只能在父子进程之间进行通信
   a.先建一个Parent的单文档应用程序,增加“创建管道”“读取数据”“写入数据”三个菜单
   b.增加成员变量HANDLE类型的hRead,hWrite,初始化变量,并在析构函数中释放句柄
   c.响应菜单代码:
void CParentView::OnPipeCreate() 菜单“创建管道”代码
{
// TOD Add your command handler code here
SECURITY_ATTRIBUTES sa;
sa.bInheritHandle=TRUE;
sa.lpSecurityDescriptor=NULL;
sa.nLength=sizeof(SECURITY_ATTRIBUTES);
if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
   MessageBox("创建匿名管道失败!");
   return;
}
STARTUPINFO sui;
PROCESS_INFORMATION pi;
ZeroMemory(&sui,sizeof(STARTUPINFO));将数据清0!
sui.cb=sizeof(STARTUPINFO);
sui.dwFlags=STARTF_USESTDHANDLES;
sui.hStdInput=hRead;
sui.hStdOutput=hWrite;
sui.hStdError=GetStdHandle(STD_ERROR_HANDLE);

 

匿名管道子进程创建失败
我是跟着孙鑫VC教程学的,跟着他的步骤敲得代码,但是子进程总是创建失败,
if(!CreateProcess(("..ChildDebugChild1.exe"),NULL,NULL,NULL,
TRUE,0,NULL,NULL,&sui,&pi))
{
CloseHandle;
CloseHandle;
hRead=NULL;
hWrite=NULL;
MessageBox("创建子进程失败!");
return;
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}

Windows系统编程之进程间通信
作者:北极星2003
来源:看雪论坛(www.pediy.com)
Windows 的IPC(进程间通信)机制主要是异步管道和命名管道。(至于其他的IPC方式,例如内存映射、邮槽等这里就不介绍了)
管道(pipe)是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接到这个管道的进程称为管道客户端。一个进程向管道写入信息,而另外一个进程从管道读取信息。
异步管道是基于字符和半双工的(即单向),一般用于程序输入输出的重定向;命名管道则强大地多,它们是面向消息和全双工的,同时还允许网络通信,用于创建客户端/服务器系统。
一、异步管道(实现比较简单,直接通过实例来讲解)
实验目标:当前有sample.cpp, sample.exe, sample.in这三个文件,sample.exe为sample.cpp的执行程序,sample.cpp只是一个简单的程序示例(简单求和),如下:

if(!CreateProcess("..\Child\Debug\Child.exe",NULL,NULL,NULL,
    TRUE,0,NULL,NULL,&sui,&pi))创建子进程
{
   CloseHandle(hRead);
   CloseHandle(hWrite);关闭句柄,将内核对象的使用计数减少1,这样当操作系统发现内核对象的使用计数为0时,将清除内核对象。
   hRead=NULL;
   hWrite=NULL;
   MessageBox("创建子进程失败!");
   return;
}
else
{
   CloseHandle(pi.hProcess);
   CloseHandle(pi.hThread);
}
}

前面介绍了进程间通信的两种方法:剪贴板和匿名管道。这两种进程间通信的方法只能在本地主机的进程之间通信。而匿名管道还限制通信的两进程之间必须有父子关系。在开发网络间不同进程之间相互通信的应用程序时,我们可以用命名管道和邮槽。这两种方法不仅支持本地主机通信也支持网间进程通信。下面详细介绍这两种方法:

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

代码:

void CParentView::OnPipeRead() 菜单“读取数据”代码
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
   MessageBox("读取数据失败!");
   return;
}
MessageBox(buf);
}

一、命名管道

[cpp] view plain copy

void CParentView::OnPipeWrite() 菜单“写入数据”代码
{
// TOD Add your command handler code here
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hWrite,buf,strlen(buf)+1,&dwWrite,NULL))
{
   MessageBox("写入数据失败!");
   return;
}
}
      d.再建一个Child的单文档,在View中增加两个成员hRead和hWrite.在OnInitialUpdate()中得到句柄的值。
void CChildView::OnInitialUpdate()
{
CView::OnInitialUpdate();

    将命名管道作为网络通信的方案时,他实际上建立了一个客户机/服务器通信体系,并在其中可靠的传输数据。命名管道式围绕Windows文件系统设计的一种机制,采用“命名管道文件系统借口”,因此客户机和服务器可利用标准的Win32文件系统函数来进行数据的收发。在利用命名管道进行通信时,服务器是唯一一个有权建立命名管道的进程,可以接收客户机的连接请求,客户机只能同一个现有的命名管道客户机建立连接。命名管道提供了两种基本的通信模式:字节模式和消息模式。在实际编程过程中可以根据实际需要选择不同的通信模式。利用命名管道进行通信可以按照下面的步骤进行:

 

// TOD Add your specialized code here and/or call the base class
hRead=GetStdHandle(STD_INPUT_HANDLE);注意这句代码!
hWrite=GetStdHandle(STD_OUTPUT_HANDLE);
}

(一)服务器端:

  1. #include <iostream.h>  
  2. int main()  
  3. {  
  4.   int a, b ;  
  5.   while ( cin >> a >> b && ( a || b ) )  
  6.     cout << a + b << endl ;  
  7.   return 0;  
  8. }  

      e.加菜单“读取数据”“写入数据”其代码如下:
void CChildView::OnPipeRead()
{
// TOD Add your command handler code here
char buf[100];
DWORD dwRead;
if(!ReadFile(hRead,buf,100,&dwRead,NULL))
{
   MessageBox("读取数据失败!");
   return;
}
MessageBox(buf);
}

(1)创建命名管道

Sample.in文件是输入文件,内容:
32 433
542 657
0 0
要求根据sample.exe和它的输入数据,把输出数据重定向到sample.out
流程分析:实际这个实验中包含两个部分,把输入数据重定向到sample.exe 和把输出数据重定向到sample.out。在命令行下可以很简单的实现这个功能“sample <sample.in >sample.out”,这个命令也是利用管道特性实现的,现在我们就根据异步管道的实现原理自己来实现这个功能。
管道是基于半双工(单向)的,这里有两个重定向的过程,显然需要创建两个管道,下面给出流程图:
葡京正网网投 1

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

    在利用命名管道进行通信之前,肯定要先创建命名管道。创建命名管道需调用的函数为CreateNamedPipe(),该函数的创建一个命名管道的实例并返回该实例的句柄。该函数的第一个参数用指定的格式保存创建的管道的名字。该函数的饿第二个参数指定管道的进入模式、重叠模式、读写模式、安全属性等信息,在下面的示例中我用的是双向的进入模式(PIPE_ACCESS_DUPLEX),客户机和服务器都可以从管道读写数据,重叠方式为可重叠的方式(FILE_FLAG_OVERLAPPED),指定重叠模式后,需要花费时间的线程会立即返回执行其他操作,耗费时间的线程会在后台完成。该函数的第四个参数指定该管道可创建的最大实例个数。指定该参数的原因是,一个命名管道的示例只能和一个客户端通信,如果有多个客户端要通过该命名管道和服务器通信,需要创建多个该命名管道的实例。创建命名管道的示例代码如下:

异步管道实现的流程图说明:
1)。父进程是我们需要实现的,其中需要创建管道A,管道B,和子进程,整个实现流程分为4个操作。
2)。管道A:输入管道
3)。管道B:输出管道
4)。操作A:把输入文件sample.in的数据写入输入管道(管道A)
5)。操作B:子进程从输入管道中读取数据,作为该进程的加工原料。通常,程序的输入数据由标准的输入设备输入,这里实现输入重定向,即把输入管道作为输入设备。
6)。操作C:子进程把加工后的成品(输出数据)输出到输出管道。通常,程序的输出数据会输出到标准的输出设备,一般为屏幕,这里实现输出重定向,即把输出管道作为输出设备。
7)。操作D:把输出管道的数据写入输出文件
需要注意的是,管道的本质只是一个共享的内存区域。这个实验中,管道区域处于父进程的地址空间中,父进程的作用是提供环境和资源,并协调子进程进行加工。
程序源码:

3.命名管道:还可以跨网络通信,服务器只能在win2000和NT下运行!而客户端可以在95下运行。关键函数CreateNamedPipe
   a.先建一个NamedPipeSRV单文档应用程序,加菜单“创建管道”“读取数据”“写入数据”
   b.在View中增加Handle变量hPipe,注意在析构函数中释放它!
   c.响应菜单,创建命名管道
void CNamedPipeSrvView::OnPipeCreate()
{
// TOD 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;
}
HANDLE hEvent;
hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
if(!hEvent)
{
   MessageBox("创建事件对象失败!");
   CloseHandle(hPipe);
   hPipe=NULL;
   return;
}
OVERLAPPED ovlap;
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);
}

 /***************************************************
                创建命名管道
 *******************************************************/
 hPipe=CreateNamedPipe("\\.\pipe\MyPipe",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
  0,1,1024,1024,0,NULL);
 if (INVALID_HANDLE_VALUE==hPipe)
 {
  MessageBox("创建命名管道失败!");
  hPipe=NULL;
  //CloseHandle(hPipe);
  return;
 }

代码:

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

(2)等待客户端连接请求的到来

[cpp] view plain copy

void CNamedPipeSrvView::OnPipeWrite()
{
// TOD Add your command handler code here
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
{
   MessageBox("写入数据失败!");
   return;
}
}

    命名管道创建完成之后,服务器就可以等待客户端的连接请求。等待客户端的连接请求调用的函数为ConnectNamedPipe()该函数的的第二个参数是一个指向OVERLAPPED结构体的指针,该结构体包含用于异步执行I/O操作的信息。该结构体中我们感兴趣的参数是一个指向事件对象的句柄,我们可以利用该事件对象来执行异步I/O操作。所以我们要先调用CreateEvent()创建一个人工重置的事件对象。当等待客户端连接请求之后,我们就可以调用WaitForSingleObject()函数将该事件对象置为无信号状态,以供下一次连接请求使用。实现代码如下:

 

       d.再建一个NamedPipeCLT单文档工程,加菜单“连接管道”“读取数据”“写入数据”,当然别忘记成员变量hPipe的定义和初始化
       e.响应菜单代码
void CNamedPipeCltView::OnPipeConnect() 连接管道
{
// TOD 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;
}
}

/*********************************************************
                创建人工重置的匿名事件对象
 *********************************************************/
 HANDLE hEvent;
 hEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
 if (!hEvent)
 {
  MessageBox(" 创建人工重置的匿名事件对象失败!");
  CloseHandle(hEvent);
  hPipe=NULL;
  return;
 }
   /*********************************************************
                等待客户端连接请求的到来
 *********************************************************/
 OVERLAPPED olp;
 ZeroMemory(&olp,sizeof(OVERLAPPED));
 olp.hEvent=hEvent;
 if(!ConnectNamedPipe(hPipe,&olp))
 {
  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);

  1. #include <windows.h>   
  2. #include <iostream.h>  
  3.   
  4. const int BUFSIZE = 4096 ;   
  5. HANDLE  hChildStdinRd, hChildStdinWr, hChildStdinWrDup,   
  6.        hChildStdoutRd,hChildStdoutWr,hChildStdoutRdDup,   
  7.     hSaveStdin,    hSaveStdout;   
  8.   
  9. BOOL CreateChildProcess(LPTSTR);   
  10. VOID WriteToPipe(LPTSTR);   
  11. VOID ReadFromPipe(LPTSTR);   
  12. VOID ErrorExit(LPTSTR);   
  13. VOID ErrMsg(LPTSTR, BOOL);   
  14. void main( int argc, char *argv[] )   
  15. {    
  16.   // 处理输入参数  
  17.   if ( argc != 4 )  
  18.     return ;  
  19.   
  20.   // 分别用来保存命令行,输入文件名(CPP/C),输出文件名(保存编译信息)  
  21.   LPTSTR lpProgram = new char[ strlen(argv[1]) ] ;  
  22.   strcpy ( lpProgram, argv[1] ) ;  
  23.   LPTSTR lpInputFile = new char[ strlen(argv[2]) ];  
  24.   strcpy ( lpInputFile, argv[2] ) ;  
  25.   LPTSTR lpOutputFile = new char[ strlen(argv[3]) ] ;  
  26.   strcpy ( lpOutputFile, argv[3] ) ;      
  27.     
  28.   SECURITY_ATTRIBUTES saAttr;   
  29.   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);   
  30.   saAttr.bInheritHandle = TRUE;   
  31.   saAttr.lpSecurityDescriptor = NULL;   
  32.      
  33.   /************************************************ 
  34.    *    redirecting child process's STDOUT  * 
  35.    ************************************************/  
  36.   hSaveStdout = GetStdHandle(STD_OUTPUT_HANDLE);   
  37.     
  38.   if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))   
  39.     ErrorExit("Stdout pipe creation failed/n");   
  40.       
  41.   if (! SetStdHandle(STD_OUTPUT_HANDLE, hChildStdoutWr))   
  42.     ErrorExit("Redirecting STDOUT failed");   
  43.     
  44.   BOOL fSuccess = DuplicateHandle(  
  45.     GetCurrentProcess(),   
  46.     hChildStdoutRd,  
  47.         GetCurrentProcess(),   
  48.     &hChildStdoutRdDup ,  
  49.     0,  
  50.         FALSE,  
  51.         DUPLICATE_SAME_ACCESS);  
  52.     if( !fSuccess )  
  53.         ErrorExit("DuplicateHandle failed");  
  54.     CloseHandle(hChildStdoutRd);  
  55.     
  56.   /************************************************ 
  57.    *    redirecting child process's STDIN    * 
  58.    ************************************************/  
  59.   hSaveStdin = GetStdHandle(STD_INPUT_HANDLE);   
  60.   
  61.   if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))   
  62.     ErrorExit("Stdin pipe creation failed/n");   
  63.     
  64.   if (! SetStdHandle(STD_INPUT_HANDLE, hChildStdinRd))   
  65.     ErrorExit("Redirecting Stdin failed");   
  66.     
  67.   fSuccess = DuplicateHandle(  
  68.     GetCurrentProcess(),   
  69.     hChildStdinWr,   
  70.     GetCurrentProcess(),  
  71.     &hChildStdinWrDup,   
  72.     0,   
  73.     FALSE,                   
  74.     DUPLICATE_SAME_ACCESS);   
  75.   if (! fSuccess)   
  76.     ErrorExit("DuplicateHandle failed");   
  77.   CloseHandle(hChildStdinWr);     
  78.   
  79.   /************************************************ 
  80.    *      创建子进程(即启动SAMPLE.EXE)    * 
  81.    ************************************************/  
  82.   fSuccess = CreateChildProcess( lpProgram );  
  83.   if ( !fSuccess )   
  84.     ErrorExit("Create process failed");   
  85.     
  86.   // 父进程输入输出流的还原设置  
  87.   if (! SetStdHandle(STD_INPUT_HANDLE, hSaveStdin))   
  88.     ErrorExit("Re-redirecting Stdin failed/n");   
  89.   if (! SetStdHandle(STD_OUTPUT_HANDLE, hSaveStdout))   
  90.     ErrorExit("Re-redirecting Stdout failed/n");   
  91.   
  92.   WriteToPipe( lpInputFile ) ;  
  93.   ReadFromPipe( lpOutputFile );   
  94.           delete lpProgram ;  
  95.           delete lpInputFile ;  
  96.           delete lpOutputFile ;  
  97. }   
  98.   
  99. BOOL CreateChildProcess( LPTSTR lpProgram )   
  100. {   
  101.   PROCESS_INFORMATION piProcInfo;   
  102.   STARTUPINFO siStartInfo;  
  103.   BOOL bFuncRetn = FALSE;   
  104.     
  105.   ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );  
  106.   ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );  
  107.   siStartInfo.cb = sizeof(STARTUPINFO);   
  108.     
  109.   bFuncRetn = CreateProcess ( NULL, lpProgram, NULL, NULL, TRUE, /  
  110.                 0, NULL, NULL, &siStartInfo, &piProcInfo);  
  111.   if (bFuncRetn == 0)   
  112.   {  
  113.     ErrorExit("CreateProcess failed/n");  
  114.     return 0;  
  115.   }   
  116.   else   
  117.   {  
  118.     CloseHandle(piProcInfo.hProcess);  
  119.     CloseHandle(piProcInfo.hThread);  
  120.     return bFuncRetn;  
  121.   }  
  122. }  
  123.   
  124. VOID WriteToPipe( LPTSTR lpInputFile )   
  125. {   
  126.   HANDLE hInputFile = CreateFile(lpInputFile, GENERIC_READ, 0, NULL,   
  127.     OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);   
  128.   if (hInputFile == INVALID_HANDLE_VALUE)   
  129.     return ;  
  130.   
  131.   BOOL fSuccess ;  
  132.   DWORD dwRead, dwWritten;   
  133.   CHAR chBuf[BUFSIZE] = {0} ;   
  134.     
  135.   for (;;)   
  136.   {   
  137.     fSuccess = ReadFile( hInputFile, chBuf, BUFSIZE, &dwRead, NULL) ;  
  138.     if ( !fSuccess || dwRead == 0)  
  139.       break;   
  140.   
  141.     fSuccess = WriteFile( hChildStdinWrDup, chBuf, dwRead, &dwWritten, NULL) ;  
  142.     if ( !fSuccess )   
  143.       break;   
  144.   }   
  145.       
  146.   if (! CloseHandle(hChildStdinWrDup))   
  147.     ErrorExit("Close pipe failed/n");   
  148.   
  149.   CloseHandle ( hInputFile ) ;  
  150. }   
  151.   
  152. VOID ReadFromPipe( LPTSTR lpOutputFile )   
  153. {   
  154.   HANDLE hOutputFile = CreateFile( lpOutputFile, GENERIC_READ|GENERIC_WRITE,   
  155.     FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   
  156.   if (hOutputFile == INVALID_HANDLE_VALUE)   
  157.     return ;  
  158.   
  159.   BOOL fSuccess ;  
  160.   DWORD dwRead, dwWritten;   
  161.   CHAR chBuf[BUFSIZE] = { 0 };   
  162.     
  163.   if (!CloseHandle(hChildStdoutWr))   
  164.     ErrorExit("Closing handle failed");   
  165.     
  166.   for (;;)   
  167.   {   
  168.     fSuccess = ReadFile( hChildStdoutRdDup, chBuf, BUFSIZE, &dwRead, NULL) ;  
  169.     if( !fSuccess || dwRead == 0)   
  170.     {  
  171.       break;   
  172.     }  
  173.     fSuccess = WriteFile( hOutputFile, chBuf, dwRead, &dwWritten, NULL) ;  
  174.     if ( !fSuccess )   
  175.       break;   
  176.   }   
  177.   
  178.   CloseHandle ( hOutputFile ) ;  
  179. }   
  180. VOID ErrorExit (LPTSTR lpszMessage)   
  181. {   
  182.   MessageBox( 0, lpszMessage, 0, 0 );   
  183. }  

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

(3)往管道中执行读写操作

二、命名管道
命名管道具有以下几个特征:
(1)命名管道是双向的,所以两个进程可以通过同一管道进行交互。
(2)命名管道不但可以面向字节流,还可以面向消息,所以读取进程可以读取写进程发送的不同长度的消息。
(3)多个独立的管道实例可以用一个名称来命名。例如几个客户端可以使用名称相同的管道与同一个服务器进行并发通信。
(4)命名管道可以用于网络间两个进程的通信,而其实现的过程与本地进程通信完全一致。
实验目标:在客户端输入数据a和b,然后发送到服务器并计算a+b,然后把计算结果发送到客户端。可以多个客户端与同一个服务器并行通信。
界面设计:
葡京正网网投 2

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

    由于命名管道式围绕文件系统设计的,所以我们可以利用标准的Win32文件操作函数执行读写操作。示例代码如下:

难点所在:
实现的过程比较简单,但有一个难点。原本当服务端使用ConnectNamedPipe函数后,如果有客户端连接,就可以直接进行交互。原来我在实现过程中,当管道空闲时,管道的线程函数会无限(INFINITE)阻塞。若现在需要停止服务,就必须结束所有的线程,TernimateThread可以作为一个结束线程的方法,但我基本不用这个函数。一旦使用这个函数之后,目标线程就会立即结束,但如果此时的目标线程正在操作互斥资源、内核调用、或者是操作共享DLL的全局变量,可能会出现互斥资源无法释放、内核异常等现象。这里我用重叠I/0来解决这个问题,在创建PIPE时使用FILE_FLAG_OVERLAPPED标志,这样使用ConnectNamedPipe后会立即返回,但线程的阻塞由等待函数WaitForSingleObject来实现,等待OVERLAPPED结构的事件对象被设置。
客户端主要代码:

4.邮槽,使用时应将消息长度限制在424字节以下,关键函数CreateMailSlot()
   a.先建一个MailSlotSRV工程,加菜单“接收数据”
   b.消息响应代码:
void CMailslotSrvView::OnMailslotRecv() 菜单“接收数据”的代码
{
// TOD Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateMailslot("\\.\mailslot\MyMailslot",0,
   MAILSLOT_WAIT_FOREVER,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
   MessageBox("创建油槽失败!");
   return;
}
char buf[100];
DWORD dwRead;
if(!ReadFile(hMailslot,buf,100,&dwRead,NULL))
{
   MessageBox("读取数据失败!");
   CloseHandle(hMailslot);
   return;
}
MessageBox(buf);
CloseHandle(hMailslot);
}
     c.加工程MailSlotCLT,加菜单“发送数据”
     d.加消息响应,添加代码,客户端也比较简单。
void CMailslotCltView::OnMailslotSend() 菜单“发送数据”的代码
{
// TOD Add your command handler code here
HANDLE hMailslot;
hMailslot=CreateFile("\\.\mailslot\MyMailslot",GENERIC_WRITE,
   FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
if(INVALID_HANDLE_VALUE==hMailslot)
{
   MessageBox("打开油槽失败!");
   return;
}
char buf[]="http://www.sunxin.org";
DWORD dwWrite;
if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,NULL))
{
   MessageBox("写入数据失败!");
   CloseHandle(hMailslot);
   return;
}
CloseHandle(hMailslot);
}

void CNamedPipeSrvView::OnPipRead()
{
 // TODO: Add your command handler code here
 char buf[200];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,200,&dwRead,NULL))
 {
  MessageBox("读取匿名管道失败!");
  return;
 }
 MessageBox(buf);
}

代码:

5.以上4种方法各有优缺点:剪贴板比较简单。邮槽是基于广播的,可以一对多发送。但只能一个发送,一个接收,要想同时发送接收,须写两次代码。
命名管道和邮槽可以进行网络通信。

void CNamedPipeSrvView::OnPipWrite()
{
 // TODO: Add your command handler code here
 char buf[]="匿名管道测试程序!";
 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("写匿名管道失败!");
  return;
 }
}
(二)客户端

[cpp] view plain copy

    命名管道的实现比较简单了,客户端首先判断是否有可利用的命名管道实例,如果有,然后打开管道就可以进行读写操作了。

 

(1)连接管道实例并打开管道

  1. void CMyDlg::OnSubmit()   
  2. {  
  3.   // 打开管道  
  4.   HANDLE hPipe = CreateFile("////.//Pipe//NamedPipe", GENERIC_READ | GENERIC_WRITE, /  
  5.     0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL) ;  
  6.   if ( hPipe == INVALID_HANDLE_VALUE )  
  7.   {  
  8.     this->MessageBox ( "打开管道失败,服务器尚未启动,或者客户端数量过多" ) ;  
  9.     return ;  
  10.   }  
  11.   
  12.   DWORD nReadByte, nWriteByte ;  
  13.   char szBuf[1024] = {0} ;  
  14.   // 把两个整数(a,b)格式化为字符串  
  15.   sprintf ( szBuf, "%d %d", this->nFirst, this->nSecond ) ;  
  16.   // 把数据写入管道  
  17.   WriteFile ( hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;  
  18.   
  19.   memset ( szBuf, 0, sizeof(szBuf) ) ;  
  20.   // 读取服务器的反馈信息  
  21.   ReadFile ( hPipe, szBuf, 1024, &nReadByte, NULL ) ;  
  22.   // 把返回信息格式化为整数  
  23.   sscanf ( szBuf, "%d", &(this->nResValue) ) ;  
  24.   this->UpdateData ( false ) ;  
  25.   CloseHandle ( hPipe ) ;  
  26. }  

    WaitNamedPipe()函数可用来连接一个命名管实例,连接成功后,就可以调用CreateFile()函数打开管道,该函数可以指定管道进入的模式、文件属性等。示例代码如下:

服务端主要代码:

**********************************************************
                判断是否有可利用的命名管道实例
 ************************************************************/
葡京网投哪个正规, 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;
 }

代码:

(2)往管道进行读写操作

[cpp] view plain copy

void CNamedPipeCltView::OnPipRead()
{
 // TODO: Add your command handler code here
 char buf[200];
 DWORD dwRead;
 if(!ReadFile(hPipe,buf,200,&dwRead,NULL))
 {
  MessageBox("读取匿名管道失败!");
  return;
 }
 MessageBox(buf);
}

 

void CNamedPipeCltView::OnPipWrite()
{
 // TODO: Add your command handler code here
 char buf[]="乘风736博客园 ";

  1. // 启动服务  
  2. void CMyDlg::OnStart()   
  3. {  
  4.   CString lpPipeName = "////.//Pipe//NamedPipe" ;  
  5.   for ( UINT i = 0; i < nMaxConn; i++ )  
  6.   {  
  7.     // 创建管道实例  
  8.     PipeInst[i].hPipe =  CreateNamedPipe ( lpPipeName, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, /  
  9.           PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, nMaxConn, 0, 0, 1000, NULL ) ;  
  10.     if ( PipeInst[i].hPipe == INVALID_HANDLE_VALUE )  
  11.     {  
  12.       DWORD dwErrorCode = GetLastError () ;  
  13.       this->MessageBox ( "创建管道错误!" ) ;  
  14.       return ;  
  15.     }  
  16.     // 为每个管道实例创建一个事件对象,用于实现重叠IO  
  17.     PipeInst[i].hEvent  =  CreateEvent ( NULL, false, false, false ) ;  
  18.     // 为每个管道实例分配一个线程,用于响应客户端的请求  
  19.     PipeInst[i].hTread = AfxBeginThread ( ServerThread, &PipeInst[i], THREAD_PRIORITY_NORMAL ) ;  
  20.   }  
  21.     
  22. 葡京正网网投,  this->SetWindowText ( "命名管道实例之服务器(运行)" ) ;  
  23.   this->MessageBox ( "服务启动成功" ) ;  
  24. }  
  25. // 停止服务  
  26. void CMyDlg::OnStop()   
  27. {  
  28.   DWORD dwNewMode = PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_NOWAIT ;  
  29.   for ( UINT i = 0; i < nMaxConn; i++ )  
  30.   {  
  31.     SetEvent ( PipeInst[i].hEvent ) ;  
  32.     CloseHandle ( PipeInst[i].hTread ) ;  
  33.     CloseHandle ( PipeInst[i].hPipe ) ;  
  34.   }  
  35.       
  36.   this->SetWindowText ( "命名管道实例之服务器" ) ;  
  37.   this->MessageBox ( "停止启动成功" ) ;  
  38. }  
  39.   
  40. // 线程服务函数  
  41. UINT ServerThread ( LPVOID lpParameter )  
  42. {  
  43.   DWORD  nReadByte = 0, nWriteByte = 0, dwByte = 0 ;    
  44.   char  szBuf[MAX_BUFFER_SIZE] = {0} ;  
  45.   PIPE_INSTRUCT  CurPipeInst = *(PIPE_INSTRUCT*)lpParameter ;  
  46.   OVERLAPPED OverLapStruct = { 0, 0, 0, 0, CurPipeInst.hEvent } ;  
  47.   while ( true )  
  48.   {  
  49.     memset ( szBuf, 0, sizeof(szBuf) ) ;    
  50.     // 命名管道的连接函数,等待客户端的连接(只针对NT)  
  51.     ConnectNamedPipe ( CurPipeInst.hPipe, &OverLapStruct ) ;  
  52.     // 实现重叠I/0,等待OVERLAPPED结构的事件对象  
  53.     WaitForSingleObject ( CurPipeInst.hEvent, INFINITE ) ;  
  54.     // 检测I/0是否已经完成,如果未完成,意味着该事件对象是人工设置,即服务需要停止  
  55.     if ( !GetOverlappedResult ( CurPipeInst.hPipe, &OverLapStruct, &dwByte, true ) )  
  56.       break ;  
  57.   
  58.     // 从管道中读取客户端的请求信息  
  59.     if ( !ReadFile ( CurPipeInst.hPipe, szBuf, MAX_BUFFER_SIZE, &nReadByte, NULL ) )  
  60.     {  
  61.       MessageBox ( 0, "读取管道错误!", 0, 0 ) ;  
  62.       break ;  
  63.     }  
  64.       
  65.     int a, b ;  
  66.     sscanf ( szBuf, "%d %d", &a, &b ) ;  
  67.     pMyDlg->nFirst    = a ;  
  68.     pMyDlg->nSecond    = b ;  
  69.     pMyDlg->nResValue  = a + b ;  
  70.     memset ( szBuf, 0, sizeof(szBuf) ) ;  
  71.     sprintf ( szBuf, "%d", pMyDlg->nResValue ) ;  
  72.     // 把反馈信息写入管道  
  73.     WriteFile ( CurPipeInst.hPipe, szBuf, strlen(szBuf), &nWriteByte, NULL ) ;  
  74.     pMyDlg->SetDlgItemInt ( IDC_FIRST, a, true ) ;  
  75.     pMyDlg->SetDlgItemInt ( IDC_SECOND, b, true ) ;  
  76.     pMyDlg->SetDlgItemInt ( IDC_RESULT, pMyDlg->nResValue, true ) ;  
  77.     // 断开客户端的连接,以便等待下一客户的到来  
  78.     DisconnectNamedPipe ( CurPipeInst.hPipe ) ;  
  79.   }  
  80.   
  81.   return 0 ;  
  82. }  

 DWORD dwWrite;
 if(!WriteFile(hPipe,buf,strlen(buf)+1,&dwWrite,NULL))
 {
  MessageBox("写匿名管道失败!");
  return;
 }
}

最后特别说明一下,此文章是看雪WIN32安全编程板块的斑竹北极星的,大家可以多多关注一下他的技术文章,看了几篇都认为很不错的。

运行结果如下:

链 接: 

葡京正网网投 3

这样,一个通过命名管道进行通信的服务器和客户端程序就设计好了。两个进程就可以实现通信了。

二 、邮槽

    邮槽是基于广播模式的单向通信方式,服务器只能从邮槽读取数据,客户端只能往邮槽写入数据,而且利用邮槽通信的信息量不能太大。下面介绍利用邮槽进行进程通信的过程:

(一)服务器

(1)创建油槽

    创建油槽可调用CreateMailslot()函数实现,该函数的第一个参数按照指定格式指定油槽的名字,第三个参数指定读操作等待的时间。下面的示例程序我设定读操作一直等待(MAILSLOT_WAIT_FOREVER),只到接收到数据为止。示例代码如下:

HANDLE hMailslot;
 hMailslot=CreateMailslot("\\.\mailslot\MyMailslot",0,MAILSLOT_WAIT_FOREVER,NULL);
 if (INVALID_HANDLE_VALUE==hMailslot)
 {
  MessageBox("创建邮槽失败!");
  CloseHandle(hMailslot);
  return;
 }

(2)读取数据

DWORD dwRead;
 char buf[200];
 if(!ReadFile(hMailslot,buf,200,&dwRead,NULL))
 {
  MessageBox("读取数据失败!");
  CloseHandle(hMailslot);
  return;
 }
 MessageBox(buf);
 CloseHandle(hMailslot);

(二)客户端

(1)打开油槽

CreateFile()函数不仅可以打开文件、管道还可以打开油槽。示例代码如下:

HANDLE hMailslot;
 hMailslot=CreateFile("\\.\mailslot\MyMailslot",GENERIC_WRITE,FILE_SHARE_READ,
  NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
 if (INVALID_HANDLE_VALUE==hMailslot)
 {
  MessageBox("打开邮槽失败!");
  CloseHandle(hMailslot);
  return;
 }

(2)写入数据

char buf[]="使用邮槽进行进程间通信rn乘风736博客园 ";

  DWORD dwWrite;
 if(!WriteFile(hMailslot,buf,strlen(buf)+1,&dwWrite,0))
 {
  MessageBox("写入数据失败!");
  CloseHandle(hMailslot);
  return;
 }
 CloseHandle(hMailslot);

运行结果如下:

葡京正网网投 4

使用油槽通信的实现是很简单的。油槽只能单向通信,如果要实现双向通信,可以在客户端和服务器分别实现读写操作就可以了

...

本文由葡京网投哪个正规发布于首页,转载请注明出处:命名管道和邮槽,Windows系统编程之进程间通信

关键词:

上一篇:TCP客户端加2个UDP端口_接收数据有问题,VC视频教程笔记葡京网投哪个正规:

下一篇:常见问题百问,mfc修改主窗口风格和窗口类葡京网投哪个正规: