管道实现进程间通讯 (zz)

 
一.基本的理论知识
   1 .什么是管道以及分类
  管道是两个头的东西,每个头各连接一个进程或者同一个进程的不同代码,按照管道的类别分有两种管道,匿名的和命名的;按照管道的传输方向分也可以分成两种,单向的双向的。根据管道的特点,命名管道通常用在网络环境下不同计算机上运行的进程之间的通信(当然也可以用在同一台机的不同进程中)它可以是单向或双向的;而匿名管道只能用在同一台计算机中,它只能是单向的。匿名管道其实是通过用给了一个指定名字的有名管道来实现的。
  使用管道的好处在于:读写它使用的是对文件操作的 api ,结果操作管道就和操作文件一样。即使你在不同的计算机之间用命名管道来通信,你也不必了解和自己去实现网络间通信的具体细节。
   2 .管道的使用
   A .命名管道
  命名管道是由服务器端的进程建立的,管道的命名必须遵循特定的命名方法,就是 "//./pipe/ 管道名 " ,当作为客户端的进程要使用时,使用 "// 计算机名 //pipe/ 管道名 " 来打开使用,具体步骤如下:
  服务端通过函数 CreateNamedPipe 创建一个命名管道的实例并返回用于今后操作的句柄,或为已存在的管道创建新的实例。服务端侦听来自客户端的连接请求,该功能通过 ConnectNamedPipe 函数实现。
   客户端通过函数 WaitNamedPipe 来等待管道的出现,如果在超时值变为零以前,有一个管道可以使用,则 WaitNamedPipe 将返回 True ,并通过调用 CreateFile CallNamedPipe 来呼叫对服务端的连接。
   此时服务端将接受客户端的连接请求,成功建立连接,服务端 ConnectNamedPipe 返回 True 建立连接之后,客户端与服务器端即可通过 ReadFile WriteFile ,利用得到的管道文件句柄,彼此间进行信息交换。 当客户端与服务端的通信结束,客户端调用 CloseFile ,服务端接着调用 DisconnectNamedPipe 。最后调用函数 CloseHandle 来关闭该管道。
   B .匿名管道
   由于命名管道使用时作为客户端的程序必须知道管道的名称,所以更多的用在同一 作者 编写的服务器 / 工作站程序中,你不可能随便找出一个程序来要求它和你写的程序来通过命名管道通信。而匿名管道的使用则完全不同,它允许你和完全不相干的进程通信,条件是这个进程通过控制台 “console” 来输入输出,典型的例子是老的 Dos 应用程序,它们在运行时 Windows 为它们开了个 Dos 窗口,它们的输入输出就是 console 方式的。还有一些标准的 Win32 程序也使用控制台输入输出,如果在 Win32 编程中不想使用图形界面,你照样可以使用 AllocConsole 得到一个控制台,然后通过 GetStdHandle 得到输入或输出句柄,再通过 WriteConsole WriteFile 把结果输出到控制台(通常是一个象 Dos 窗口)的屏幕上。虽然这些程序看起来象 Dos 程序,但它们是不折不扣的 Win32 程序,如果你在纯 Dos 下使用,就会显示 “The program must run under Windows!”
  一个控制台有三个句柄:标准输入、标准输出和和标准错误句柄,标准输入、标准输出句柄是可以重新定向的,你可以用匿名管道来代替它,这样一来,你可以在管道的另一端用别的进程来接收或输入,而控制台一方并没有感到什么不同,就象 Dos 下的 > 或者 < 可以重新定向输出或输入一样。通常控制台程序的输入输出如下:
   ( 控制台进程 output) write ----> 标准输出设备(一般是屏幕)
   ( 控制台进程 input) read <---- 标准输入设备(一般是键盘)
  而用管道代替后: ( 作为子进程的控制台进程 output) write ----> 管道 1 ----> read ( 父进程 )
   ( 作为子进程的控制台进程 input) read <----> 管道 2 <---- write ( 父进程 )
  使用匿名管道的步骤如下:
  使用 CreatePipe 建立两个管道,得到管道句柄,一个用来输入,一个用来输出
  准备执行控制台子进程,首先使用 GetStartupInfo 得到 StartupInfo
   使用第一个管道句柄代替 StartupInfo 中的 hStdInput ,第二个代替 hStdOutput hStdError ,即标准输入、输出、错误句柄
   使用 CreateProcess 执行子进程,这样建立的子进程输入和输出就被定向到管道中
  父进程通过 ReadFile 读第二个管道来获得子进程的输出,通过 WriteFile 写第一个管道来将输入写到子进程
   父进程可以通过 PeekNamedPipe 来查询子进程有没有输出
   子进程结束后,要通过 CloseHandle 来关闭两个管道。
 
二.管道使用的 API 函数集

CallNamedPipe 函数
函数原型: BOOL CallNamedPipe( LPCTSTR lpNamedPipeName, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, DWORD nTimeOut );
说明 这个函数由一个希望通过管道通信的一个客户进程调用。如有可能,它就同一个管道连接(在必要的情况下等候管道可用)。随后,它对指定的数据进行读写,然后将管道关闭
返回值 : Long ,非零表示成功,零表示失败。会设置 GetLastError
参数表 :
lpNamedPipeName: LPCTSTR ,指定管道名,采用的形式是: //./ 管道 / 管道名。最多可达 256 个字符的长度,而且不用区分大小写。如果存在指定名字的一个管道,则创建那个管道的一个新实例
lpInBuffer: LPVOID ,包含了要写入管道的数据的一个内存缓冲区
nInBufferSize: DWORD lpInBuffer 缓冲区中的字符数量
lpOutBuffer LPVOID 指定一个内存缓冲区,用于装载从管道中读出的数据
nOutBufferSize DWORD 指定一个长整数变量,用于装载来自管道的数据
lpBytesRead LPDWORD 指定从管道中读出的字节数。会阅读单条消息。如 lpOutBuffer 的容量不够大,不能容下整条消息,则函数会返回 FALSE ,而且 GetLastError 会设为 ERROR_MORE_DATA (消息中留下的任何字节都会丢失)
nTimeOut DWORD ,下列常量之一:
1.        NMPWAIT_NOWAIT   如管道不可用,则立即返回一个错误
2.        NMPWAIT_WAIT_FOREVER 永远等候管道可用 .
3.        NMPWAIT_USE_DEFAULT_WAIT : 使用管道的默认超时设置,这个设置是用 CreateNamedPipe 函数指定的
ConnectNamedPipe 函数
函数原型: BOOL ConnectNamedPipe( HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped );
说明: 指示一台服务器等待下去,直至客户机同一个命名管道连接
返回值: BOOL ,如 lpOverlapped NULL ,那么:
1.      如管道已连接,就返回 Ture (非零);如发生错误,或者管道已经连接,就返回零( GetLastError 此时会返回 ERROR_PIPE_CONNECTED
2.     lpOverlapped 有效,就返回零;如管道已经连接, GetLastError 会返回 ERROR_PIPE_CONNECTED ;如重叠操作成功完成,就返回 ERROR_IO_PENDING 。在这两种情况下,倘若一个客户已关闭了管道,且服务器尚未用 DisconnectNamedPipe 函数同客户断开连接,那么 GetLastError 都会返回 ERROR_NO_DATA
参数 :
hNamedPipe HANDLE ,管道的句柄
lpOverlapped LPOVERLAPPED ,如设为 NULL (传递 ByVal As Long ),表示将线程挂起,直到一个客户同管道连接为止。否则就立即返回;此时,如管道尚未连接,客户同管道连接时就会触发 lpOverlapped 结构中的事件对象。随后,可用一个等待函数来监视连接
适用平台 Windows NT
注释: 可用这个函数将一个管道换成同另一个客户连接,但首先必须用 DisconnectNamedPipe 函数断开同当前进程的连接
CreateNamedPipe 函数
函数原型: HANDLE CreateNamedPipe( LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, LPSECURITY_ATTRIBUTES lpSecurityAttributes);
说明: 创建一个命名管道。返回的句柄由管道的服务器端使用
返回值: HANDLE ,如执行成功,返回管道的句柄。 INVALID_HANDLE_VALUE 表示失败 . 会设置 GetLastError
参数:
lpName LPCTSTR ,指定管道名,采用的形式是: //./ 管道 / 管道名 。最多可达 256 个字符的长度,而且不用区分大小写。如果存在指定名字的一个管道,则创建那个管道的一个新实例
dwOpenMode DWORD 下述常数组的一个组合
下述常数之一(对于管道的所有实例都要一样):
1.          PIPE_ACCESS_DUPLEX 管道是双向的
2.          PIPE_ACCESS_INBOUND 数据从客户端流到服务器端
3.          PIPE_ACCESS_OUTBOUND 数据从服务器端流到客户端
下述常数的任意组合
1.          FILE_FLAG_WRITE_THROUGH 在网络中建立的字节型管道内,强迫数据在每次读写操作的时候通过网络传输。否则传输就可能延迟
2.          FILE_FLAG_OVERLAPPED 允许(但不要求)用这个管道进行异步(重叠式)操作
常数 WRITE_DAC WRITE_OWNER ACCESS_ SYSTEM_SECURITY 提供了附加的安全选项
dwPipeMode DWORD ,下述常数组的一个组合
下述常数之一(管道的所有实例都必须指定相同的常数)
1.          PIPE_TYPE_BYTE 数据作为一个连续的字节数据流写入管道
2.          PIPE_TYPE_MESSAGE 数据用数据块(名为 消息 报文 )的形式写入管道
下述常数之一:
1.          PIPE_READMODE_PIPE 数据以单独字节的形式从管道中读出
2.          PIPE_READMODE_MESSAGE 数据以名为 消息 的数据块形式从管道中读出(要求指定 PIPE_TYPE_MESSAGE
下述常数之一:
1.          PIPE_WAIT 同步操作在等待的时候挂起线程
2.          PIPE_NOWAIT (不推荐!) 同步操作立即返回。这样可为异步传输提供一种落后的实现方法,已由 Win32 的重叠式传输机制取代了
nMaxInstances DWORD ,这个管道能够创建的最大实例数量。必须是 1 到常数 PIPE_UNLIMITED_INSTANCES 间的一个值。它对于管道的所有实例来说都应是相同的
nOutBufferSize DWORD ,建议的输出缓冲区长度;零表示用默认设置
nInBufferSize DWORD ,建议的输入缓冲区长度;零表示用默认设置
nDefaultTimeOut DWORD ,管道的默认等待超时。对一个管道的所有实例来说都应相同
lpSecurityAttributes LPSECURITY_ATTRIBUTES ,指定一个 SECURITY_ATTRIBUTES 结构,或者传递零值(将参数声明为 ByVal As Long ,并传递零值),以便使用不允许继承的一个默认描述符
适用平台: Windows NT
CreatePipe 函数
函数原型: BOOL CreatePipe(PHANDLE hReadPipe, PHANDLE hWritePipe, LPSECURITY_ATTRIBUTES lpPipeAttributes, DWORD nSize );
说明: 创建一个匿名管道
返回值: Long ,非零表示成功,零表示失败。会设置 GetLastError
参数 :
phReadPipe PHANDLE ,指定一个变量,设为管道读入(输出)端的一个句柄
phWritePipe PHANDLE ,指定一个变量,设为管道写入(输入)端的一个句柄
lpPipeAttributes LPSECURITY _ATTRIBUTES ,指定一个 SECURITY_ATTRIBUTES 结构,或者传递零值,以便使用不允许继承的一个默认描述符
nSize DWORD ,管道缓冲区的建议大小。零表示用默认值
注解: 匿名管道不允许异步操作,所以如在一个管道中写入数据,且缓冲区已满,那么除非另一个进程从管道中读出数据,从而腾出了缓冲区的空间,否则写入函数不会返回
DisconnectNamedPipe 函数
函数原型: BOOL DisconnectNamedPipe( HANDLE hNamedPipe );
说明: 断开一个客户与一个命名管道的连接
返回值: BOOL ,非零表示成功,零表示失败。会设置 GetLastError
参数: hNamedPipe Long ,管道的句柄
适用平台: Windows NT
注解 :如客户尚未在它自己那端关闭管道句柄,下次试图访问管道的时候就会发生错误
GetNamedPipeHandleState 函数
函数原型: BOOL GetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpState, LPDWORD lpCurInstances, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout, LPTSTR lpUserName, DWORD nMaxUserNameSize);
说明 : 获取一个命名管道当前的状态信息
返回值 : BOOL ,非零表示成功,零表示失败。会设置 GetLastError
参数 :
hNamedPipe Long ,指定一个命名管道的句柄
lpState Long ,用于装载下述一个或多个常数的长整数变量 PIPE_NOWAIT 管道设置成永不堵塞,这种模式很少使用 PIPE_READMODE_MESSAGE 管道设置成读取消息
lpCurInstances Long ,装载这个管道目前存在的实例数量
lpMaxCollectionCount Long ,如管道设置成通过一个网络传输数据,就用这个变量装载通过管道发送之前可排队等候的最大数据量
lpCollectDataTimeout Long ,如管道设置成通过一个网络传输数据,就在这里指定一个长整数变量,用它装载进行一次网络数据传输前需要等候的最长时间
lpUserName String ,如这是个服务器句柄,就在这里指定一个字串缓冲区,在其中载入客户应用程序的用户名。可设为 vbNullString ,表示不取回信息
nMaxUserNameSize Long ,指定 lpUserName 缓冲区的长度,可以为零
GetNamedPipeInfo 函数
函数原型 BOOL GetNamedPipeInfo(HANDLE hNamedPipe,      LPDWORD lpFlags, LPDWORD lpOutBufferSize, LPDWORD lpInBufferSize, LPDWORD lpMaxInstances);
说明 :获得指定命名管道的信息
返回值 :如果函数执行成功,返回值非零,否则,返回值为零,此时调用 GetLastError 函数获得扩展错误信息
参数
hNamedPipe :命名管道句柄。这个句柄具有命名管道的 GENERIC_READ 访问权限。
lpFlags :指定一个识别命名管道类型的 32 位变量。如果这个信息不需获得,可以给此参数置 NULL 。否则使用以下的值:
1.        PIPE_CLIENT_END 这个句柄是关于一个命名管道的客户端,此值被默认
2.        PIPE_SERVER_END  这个句柄是关于命名管道的服务器端。如果这个值没有被指定,这个命名管道句柄是关于客户端的
3.        PIPE_TYPE_BYTE  命名管道是一个字节管道型,此值被默认
4.        PIPE_TYPE_MESSAGE  命名管道是一个消息管道。如果这个值没有被指定,则默认为字节管道型
lpOutBufferSize :一个 32 变量地址。用来按字节,返回输出数据缓冲的尺寸。如果缓冲值为零,则这个缓冲区没有按要求分配。如果镇魂歌信息不需要的,可以被置 NULL.
lpInBufferSize :一个 32 变量地址。用来按字节,返回输入数据缓冲的尺寸。如果缓冲值为零,则这个缓冲区没有按要求分配。如果这个信息不需要的,可以被置 NULL.
lpMaxInstances :一个 32 变量地址。用来获得被创建的管道实例的最大尺寸。如果此位被设置为 PIPE_UNLIMITED_INSTANCES, 被创建的管道实例的最大尺寸被按照系统的可容量所限制。如果这个信息不需要的,可以被置 NULL.
PeekNamedPipe 函数
函数原型 : BOOL PeekNamedPipe(HANDLE hNamedPipe, LPVOID lpBuffer,DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage );
说明 : 预览一个管道中的数据,或取得与管道中的数据有关的信息
返回值 :   BOOL ,非零表示成功,零表示失败。会设置 GetLastError
参数 :
hNamedPipe  HANDLE ,指定一个管道的句柄。这并不一定是某个命名管道的句柄 —— 匿名管道同样适用
lpBuffer  LPVOID ,指定要装载数据的一个缓冲区的头一个字符。可以为零
nBufferSize  DWORD lpBuffer 缓冲区长度
lpBytesRead  LPDWORD ,保存装载到缓冲区的字符数量
lpTotalBytesAvail LPDWORD ,保存管道中可用的字符数量
lpBytesLeftThisMessage Long ,保存这次读操作后仍然保留在消息中的字符数。只能为那些基于消息的命名管道设置
注解 : 由这个函数读入的数据实际并不能从管道中删除。如果要对一个管道进行轮询,了解是否有可能数据,那么使用这个函数特别理想
SetNamedPipeHandleState 函数
函数原型 :   BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, LPDWORD lpMode, LPDWORD lpMaxCollectionCount, LPDWORD lpCollectDataTimeout );
说明 : 设置与一个命名管道的运作有关的信息
返回值值 :   BOOL ,非零表示成功,零表示失败。会设置 GetLastError
参数:
hNamedPipe   HANDLE ,指定一个命名管道的句柄
lpMode   LPDWORD ,下列常数的一个或多个: PIPE_WAIT PIPE_NOWAIT PIPE_READMODE_BYTE 以及 PIPE_READMODE_MESSAGE 。请参考 CreateNamedPipe 函数,了解有关这些标志的进一步情况
pMaxCollectionCount   LPDWORD ,如管道设为通过一个网络传输数据,则在这里指定通过管道发送之前可排除等候的最大数据量
lpCollectDataTimeout   LPDWORD ,如管道设为通过一个网络传输数据,则在这里指定网络数据传输前能够忍受的最长等候时间(超时)
TransactNamedPipe 函数
函数原型 :   BOOL TransactNamedPipe( HANDLE hNamedPipe, LPVOID lpInBuffer, DWORD nInBufferSize, LPVOID lpOutBuffer, DWORD nOutBufferSize, LPDWORD lpBytesRead, LPOVERLAPPED lpOverlapped );
说明 :   该函数在单独一个函数中同时合并了对管道的读、写操作。客户和服务器进程都可用它
返回值 :   BOOL ,如操作已结束,则返回 TRUE( 非零 ); 否则返回零。在异步模式中, GetLastError 会设置成 ERROR_IO_PENDING ,而且操作会继续在后台进行。可测试 lpOverlapped 结构中的事件对象,了解操作是否结束
参数
hNamedPipe   HANDLE ,指定一个消息类型的命名管道的句柄
lpInBuffer   LPVOID ,指定一个内存缓冲区,在其中包含要写入管道的数据
nInBufferSize DWORD ,指定 lpInBuffer 缓冲区中的字节数量
lpOutBuffer   LPVOID ,指定一个内存缓冲区,用于装载从管道中读入的数据
nOutBufferSize   DWORD ,用于装载来自管道的数据
lpBytesRead   LPDWORD ,指定要从管道读入的字节数量。会读入单条消息。如由于 lpOutBuffer 不够大,不能容下完整的消息,那么函数会返回 FALSE ,而且 GetLastError 会设为 ERROR_MORE_DATA (消息中剩下的所有字节都会丢失)
lpOverlapped   LPOVERLAPPED ,可以为 NULL (变成 ByVal As Long ,并传递零值),或指定包含了一个事件对象的 OVERLAPPED 结构
注解 : lpOverlapped 设为 NULL ,或者句柄没有创建成 FILE_FLAG_OVERLAPPED 样式,那么除非读和写操作都完成,否则函数不会返回
WaitNamedPipe 函数
函数原型 :   BOOL WaitNamedPipe(LPCTSTR lpNamedPipeName, DWORD nTimeOut );
说明: 由一个客户进程调用,等候一个管道变成可用状态(比如服务器已调用 ConnectNamedPipe 函数同一个客户连接)
返回值: BOOL ,非零表示成功;如果失败,或者管道不存在,则返回零。会设置 GetLastError
参数 :
lpNamedPipeName  LPCTSTR ,指定要连接的管道名称
nTimeOut  DWORD ,以毫秒数表示的等待时间,或者下述常数之一:
1.      NMPWAIT_USE_DEFAULT_WAIT 使用管道创建时的默认超时设定
2.     NMPWAIT_WAIT_FOREVER 永远等待
注解:  在这个函数之后,用 CreateFile 打开管道。注意从这个函数返回到调用 CreateFile 函数期间,倘若有另一个进程同管道连接,那么这个 CreateFile 调用可能失败

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值