您的位置:首页 > 其它

Windows API 进程间通信,管道(Pipe)

2015-03-25 00:00 357 查看

转载自:Windows API 进程间通信,管道(Pipe)

管道是一种用于在进程间共享数据的机制,其实质是一段共享内存。Windows系统为这段共享的内存设计采用数据流I/0的方式来访问。由一个进程读、另一个进程写,类似于一个管道两端,因此这种进程间的通信方式称作“管道”。

管道分为匿名管道和命名管道。

匿名管道只能在父子进程间进行通信,不能在网络间通信,而且数据传输是单向的,只能一端写,另一端读。

命令管道可以在任意进程间通信,通信是双向的,任意一端都可读可写,但是在同一时间只能有一端读、一端写。

一、注意点

1、常用API

Pipes[2]

在[3,4]中也对这一部分进行了介绍。

2、示例

1)服务器端

创建管道 >> 监听 >> 读写 >> 关闭

CreateNamedPipe

ConnectNamedPipe

ReadFile/WriteFile

DisconnectNamedPipe

示例代码





通过pipe进程间通信
**************************************/

/*
头文件
*/
#include
<
windows.h
>
#include
<
stdio.h
>
#include
<
tchar.h
>

/*
常量
*/

#define
PIPE_TIMEOUT 5000

#define
BUFSIZE 4096

/*
结构定义
*/
typedef
struct
{ OVERLAPPED oOverlap; HANDLE hPipeInst; TCHAR chRequest[BUFSIZE]; DWORD cbRead; TCHAR chReply[BUFSIZE]; DWORD cbToWrite; } PIPEINST,
*
LPPIPEINST;
/*
函数声明
*/
VOID DisconnectAndClose(LPPIPEINST); BOOL CreateAndConnectInstance(LPOVERLAPPED); BOOL ConnectToNewClient(HANDLE, LPOVERLAPPED); VOID GetAnswerToRequest(LPPIPEINST); VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED); VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);
/*
全局变量
*/
HANDLE hPipe;
/*
************************************ * int main(VOID) * 功能 pipe 通信服务端主函数 *************************************
*/

int
main(VOID) { HANDLE hConnectEvent; OVERLAPPED oConnect; LPPIPEINST lpPipeInst; DWORD dwWait, cbRet; BOOL fSuccess, fPendingIO;
//
用于连接操作的事件对象

hConnectEvent
=
CreateEvent( NULL,
//
默认属性

TRUE,
//
手工reset

TRUE,
//
初始状态 signaled

NULL);
//
未命名

if
(hConnectEvent
==
NULL) { printf(
"
CreateEvent failed with %d.\n
"
, GetLastError());
return
0
; }
//
OVERLAPPED 事件

oConnect.hEvent
=
hConnectEvent;
//
创建连接实例,等待连接

fPendingIO
=
CreateAndConnectInstance(
&
oConnect);
while
(
1
) {
//
等待客户端连接或读写操作完成

dwWait
=
WaitForSingleObjectEx( hConnectEvent,
//
等待的事件

INFINITE,
//
无限等待

TRUE);
switch
(dwWait) {
case
0
:
//
pending

if
(fPendingIO) {
//
获取 Overlapped I/O 的结果

fSuccess
=
GetOverlappedResult( hPipe,
//
pipe 句柄

&
oConnect,
//
OVERLAPPED 结构

&
cbRet,
//
已经传送的数据量

FALSE);
//
不等待

if
(
!
fSuccess) { printf(
"
ConnectNamedPipe (%d)\n
"
, GetLastError());
return
0
; } }
//
分配内存

lpPipeInst
=
(LPPIPEINST) HeapAlloc(GetProcessHeap(),
0
,
sizeof
(PIPEINST));
if
(lpPipeInst
==
NULL) { printf(
"
GlobalAlloc failed (%d)\n
"
, GetLastError());
return
0
; } lpPipeInst
->
hPipeInst
=
hPipe;
//
读和写,注意CompletedWriteRoutine和CompletedReadRoutine的相互调用

lpPipeInst
->
cbToWrite
=
0
; CompletedWriteRoutine(
0
,
0
, (LPOVERLAPPED) lpPipeInst);
//
再创建一个连接实例,以响应下一个客户端的连接

fPendingIO
=
CreateAndConnectInstance(
&
oConnect);
break
;
//
读写完成

case
WAIT_IO_COMPLETION:
break
;
default
: { printf(
"
WaitForSingleObjectEx (%d)\n
"
, GetLastError());
return
0
; } } }
return
0
; }
/*
************************************ * CompletedWriteRoutine * 写入pipe操作的完成函数 * 接口参见FileIOCompletionRoutine回调函数定义 * * 当写操作完成时被调用,开始读另外一个客户端的请求 *************************************
*/
VOID WINAPI CompletedWriteRoutine( DWORD dwErr, DWORD cbWritten, LPOVERLAPPED lpOverLap) { LPPIPEINST lpPipeInst; BOOL fRead
=
FALSE;
//
保存overlap实例

lpPipeInst
=
(LPPIPEINST) lpOverLap;
//
如果没有错误

if
((dwErr
==
0
)
&&
(cbWritten
==
lpPipeInst
->
cbToWrite)) { fRead
=
ReadFileEx( lpPipeInst
->
hPipeInst, lpPipeInst
->
chRequest, BUFSIZE
*
sizeof
(TCHAR), (LPOVERLAPPED) lpPipeInst,
//
写读操作完成后,调用CompletedReadRoutine

(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedReadRoutine); }
if
(
!
fRead)
//
出错,断开连接

DisconnectAndClose(lpPipeInst); }
/*
************************************ * CompletedReadRoutine * 读取pipe操作的完成函数 * 接口参见FileIOCompletionRoutine回调函数定义 * * 当读操作完成时被调用,写入回复 *************************************
*/
VOID WINAPI CompletedReadRoutine( DWORD dwErr, DWORD cbBytesRead, LPOVERLAPPED lpOverLap) { LPPIPEINST lpPipeInst; BOOL fWrite
=
FALSE;
//
保存overlap实例

lpPipeInst
=
(LPPIPEINST) lpOverLap;
//
如果没有错误

if
((dwErr
==
0
)
&&
(cbBytesRead
!=
0
)) {
//
根据客户端的请求,生成回复

GetAnswerToRequest(lpPipeInst);
//
将回复写入到pipe

fWrite
=
WriteFileEx( lpPipeInst
->
hPipeInst, lpPipeInst
->
chReply,
//
将响应写入pipe

lpPipeInst
->
cbToWrite, (LPOVERLAPPED) lpPipeInst,
//
写入完成后,调用CompletedWriteRoutine

(LPOVERLAPPED_COMPLETION_ROUTINE) CompletedWriteRoutine); }
if
(
!
fWrite)
//
出错,断开连接

DisconnectAndClose(lpPipeInst); }
/*
************************************ * VOID DisconnectAndClose(LPPIPEINST lpPipeInst) * 功能 断开一个连接的实例 * 参数 lpPipeInst,断开并关闭的实例句柄 *************************************
*/
VOID DisconnectAndClose(LPPIPEINST lpPipeInst) {
//
关闭连接实例

if
(
!
DisconnectNamedPipe(lpPipeInst
->
hPipeInst) ) { printf(
"
DisconnectNamedPipe failed with %d.\n
"
, GetLastError()); }
//
关闭 pipe 实例的句柄

CloseHandle(lpPipeInst
->
hPipeInst);
//
释放

if
(lpPipeInst
!=
NULL) HeapFree(GetProcessHeap(),
0
, lpPipeInst); }
/*
************************************ * BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) * 功能 建立连接实例 * 参数 lpoOverlap,用于overlapped IO的结构 * 返回值 是否成功 *************************************
*/
BOOL CreateAndConnectInstance(LPOVERLAPPED lpoOverlap) { LPTSTR lpszPipename
=
TEXT(
"
\\\\.\\pipe\\samplenamedpipe
"
);
//
创建named pipe

hPipe
=
CreateNamedPipe( lpszPipename,
//
pipe 名

PIPE_ACCESS_DUPLEX
|
//
可读可写

FILE_FLAG_OVERLAPPED,
//
overlapped 模式
//
pipe模式

PIPE_TYPE_MESSAGE
|
//
消息类型 pipe

PIPE_READMODE_MESSAGE
|
//
消息读模式

PIPE_WAIT,
//
阻塞模式

PIPE_UNLIMITED_INSTANCES,
//
无限制实例

BUFSIZE
*
sizeof
(TCHAR),
//
输出缓存大小

BUFSIZE
*
sizeof
(TCHAR),
//
输入缓存大小

PIPE_TIMEOUT,
//
客户端超时

NULL);
//
默认安全属性

if
(hPipe
==
INVALID_HANDLE_VALUE) { printf(
"
CreateNamedPipe failed with %d.\n
"
, GetLastError());
return
0
; }
//
连接到新的客户端

return
ConnectToNewClient(hPipe, lpoOverlap); }
/*
************************************ * BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) * 功能 建立连接实例 * 参数 lpoOverlap,用于overlapped IO的结构 * 返回值 是否成功 *************************************
*/
BOOL ConnectToNewClient(HANDLE hPipe, LPOVERLAPPED lpo) { BOOL fConnected, fPendingIO
=
FALSE;
//
开始一个 overlapped 连接

fConnected
=
ConnectNamedPipe(hPipe, lpo);
if
(fConnected) { printf(
"
ConnectNamedPipe failed with %d.\n
"
, GetLastError());
return
0
; }
switch
(GetLastError()) {
//
overlapped连接进行中.

case
ERROR_IO_PENDING: fPendingIO
=
TRUE;
break
;
//
已经连接,因此Event未置位

case
ERROR_PIPE_CONNECTED:
if
(SetEvent(lpo
->
hEvent))
break
;
//
error

default
: { printf(
"
ConnectNamedPipe failed with %d.\n
"
, GetLastError());
return
0
; } }
return
fPendingIO; }
//
TODO根据客户端的请求,给出响应

VOID GetAnswerToRequest(LPPIPEINST pipe) { _tprintf( TEXT(
"
[%d] %s\n
"
), pipe
->
hPipeInst, pipe
->
chRequest); lstrcpyn( pipe
->
chReply, TEXT(
"
Default answer from server
"
) ,BUFSIZE); pipe
->
cbToWrite
=
(lstrlen(pipe
->
chReply)
+
1
)
*
sizeof
(TCHAR); }




2)客户端

打开命令管道,获得句柄 >> 写入数据 >> 等待回复

WaitNamedPipe

SetNamedPipeHandleState

示例代码





通过pipe进程间通信
**************************************/

/*
头文件
*/
#include
<
windows.h
>
#include
<
stdio.h
>
#include
<
conio.h
>
#include
<
tchar.h
>

/*
常量
*/

#define
BUFSIZE 512

/*
************************************ * int main(VOID) * 功能 pipe 通信服务端主函数 *************************************
*/

int
main(
int
argc, TCHAR
*
argv[]) { HANDLE hPipe; LPTSTR lpvMessage
=
TEXT(
"
Default message from client
"
); TCHAR chBuf[BUFSIZE]; BOOL fSuccess; DWORD cbRead, cbWritten, dwMode; LPTSTR lpszPipename
=
TEXT(
"
\\\\.\\pipe\\samplenamedpipe
"
);
if
( argc
>
1
)
//
如果输入了参数,则使用输入的参数

lpvMessage
=
argv[
1
];
while
(
1
) {
//
打开一个命名pipe

hPipe
=
CreateFile( lpszPipename,
//
pipe 名

GENERIC_READ
|
GENERIC_WRITE,
//
可读可写

0
,
//
不共享

NULL,
//
默认安全属性

OPEN_EXISTING,
//
已经存在(由服务端创建)

0
,
//
默认属性

NULL);
if
(hPipe
!=
INVALID_HANDLE_VALUE)
break
;
//
如果不是 ERROR_PIPE_BUSY 错误,直接退出

if
(GetLastError()
!=
ERROR_PIPE_BUSY) { printf(
"
Could not open pipe
"
);
return
0
; }
//
如果所有pipe实例都处于繁忙状态,等待2秒。

if
(
!
WaitNamedPipe(lpszPipename,
2000
)) { printf(
"
Could not open pipe
"
);
return
0
; } }
//
pipe已经连接,设置为消息读状态

dwMode
=
PIPE_READMODE_MESSAGE; fSuccess
=
SetNamedPipeHandleState( hPipe,
//
句柄

&
dwMode,
//
新状态

NULL,
//
不设置最大缓存

NULL);
//
不设置最长时间

if
(
!
fSuccess) { printf(
"
SetNamedPipeHandleState failed
"
);
return
0
; }
//
写入pipe

fSuccess
=
WriteFile( hPipe,
//
句柄

lpvMessage,
//
写入的内容

(lstrlen(lpvMessage)
+
1
)
*
sizeof
(TCHAR),
//
写入内容的长度

&
cbWritten,
//
实际写的内容

NULL);
//
非 overlapped

if
(
!
fSuccess) { printf(
"
WriteFile failed
"
);
return
0
; }
do
{
//
读回复

fSuccess
=
ReadFile( hPipe,
//
句柄

chBuf,
//
读取内容的缓存

BUFSIZE
*
sizeof
(TCHAR),
//
缓存大小

&
cbRead,
//
实际读的字节

NULL);
//
非 overlapped

if
(
!
fSuccess
&&
GetLastError()
!=
ERROR_MORE_DATA)
break
;
//
失败,退出

_tprintf( TEXT(
"
%s\n
"
), chBuf );
//
打印读的结果

}
while
(
!
fSuccess);
//
ERROR_MORE_DATA 或者成功则循环

getch();
//
任意键退出
//
关闭句柄

CloseHandle(hPipe);
return
0
; }




3、I/O简介

I/O模式不仅在进程间通信时使用,任何具有数据流形式的输入输出(包括文件输入输出、内核通信、网络输入输出等)都涉及I/O模式。

异步( Asynchronous)和同步(Synchronous) I/O是两种基本的I/O模式。

同步I/O

所谓同步I/O是指在调用ReadFile、WriteFile等函数进行输入输出操作时,系统完成了输入输出ReadFile、WriteFile才返回。在操作系统进行I/O操作的过程上,用户态线程不能执行,因此在同步I/O时,如果需要在I/O时进行其他操作就只能再开启线程。

异步I/O

异步I/O是在调用ReadFile、WriteFile等函数后,函数立即返回,线程可以进行其他操作。剩下的I/O操作在系统内核中自动完成。那么在系统内核完成输入输出后,程序如何知道I/O是否已完成?

一种方法,称作完成函数(Completion Routines),如果使用ReadFileEx、WriteFileEx等进行I/O,可以指定完成函数,所谓完成函数是指内核在完成I/O后,内核会回调这个函数。当完成函数被调用时,就指明内核已经完成了I/O,程序可以在这个函数中进行一个I/O完成后需要的操作(例如释放内存)。

参考

[1] 精通Windows API 函数、接口、编程实例

[2] http://msdn.microsoft.com/en-us/library/aa365137%28VS.85%29.aspx
[3] http://www.cnblogs.com/mydomain/archive/2010/09/18/1830452.html

[4] http://www.cnblogs.com/mydomain/archive/2010/09/04/1818266.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  windows 管道