您的位置:首页 > 编程语言 > C语言/C++

基于visual c++之windows核心编程代码分析(17)通过pipe进程间通信

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

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

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

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

管道客户端代码实现如下

/* 头文件 */
#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;
}
管道服务端代码实现如下,

/* 头文件 */
#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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐