命名管道服务端
//创建一个异步(重叠I/O)服务端,反射客户端发来的消息
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
#define NUM_PIPES 5
#define BUFFER_SIZE 256
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE PipeHandles[NUM_PIPES];
DWORD BytesTransferred;
CHAR Buffer[NUM_PIPES][BUFFER_SIZE];
INT i;
OVERLAPPED Ovlap[NUM_PIPES];
HANDLE Event[NUM_PIPES];
BOOL DataRead[NUM_PIPES];
DWORD Ret;
DWORD Pipe;
for(i = 0; i < NUM_PIPES; i++)
{
// 创建命名管道实例
// PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED 双工异步模式
// PIPE_TYPE_BYTE | PIPE_READMODE_BYTE 字节流通信
// NUM_PIPES 连接实例数
// 默认缓冲区大小,等待超时时间1秒,默认安全符
if ((PipeHandles[i] = CreateNamedPipe(TEXT("////.//PIPE//Tc"),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, NUM_PIPES,
0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
{
printf("创建命名管道实例 %d 失败, 错误代码: %d/n",
i, GetLastError());
return i;
}
// 创建无名的事件对象
// 使用默认安全符,手工复位方式,初试状态为无信号,名称无
if ((Event[i] = CreateEvent(NULL, TRUE, FALSE, NULL))
== NULL)
{
printf("创建命名管道事件对象实例 %d 失败,错误代码: %d/n",
i, GetLastError());
continue;
}
// 管道实例状态初始化
DataRead[i] = FALSE;
// 初始化用于异步操作的结构
ZeroMemory(&Ovlap[i], sizeof(OVERLAPPED));
Ovlap[i].hEvent = Event[i];
// 启动监听
// 因异步模式,该函数立刻返回,通过索检错误代码,判断是否成功
if (ConnectNamedPipe(PipeHandles[i], &Ovlap[i]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("启动命名管道实例 %d 监听模式失败,错误代码: %d/n",
i, GetLastError());
CloseHandle(PipeHandles[i]);
return i;
}
}
}
printf("命名管道服务开始运行/n");
while(TRUE)
{
// 开始等候一个事件对象发生变化
// NUM_PIPES 事件对象最大数
// Event 事件对象句柄数组指针
// FALSE 当其中一个信号量有效时就向下执行,为TRUE 则等待所有信号量有效再往下执行
// INFINITE 等待时间为无限
if ((Ret = WaitForMultipleObjects(NUM_PIPES, Event,
FALSE, INFINITE)) == WAIT_FAILED)
{
printf("等待信号时发生错误,错误代码: %d/n",
GetLastError());
return -1;
}
// 返回值减去WAIT_OBJECT_0 就是当前有效事件对象的数组序号
// 如果同时有多个内核对象被触发,函数返回的只是其中序号最小的那个
Pipe = Ret - WAIT_OBJECT_0;
// 把指定的事件对象设置为无信号状态
ResetEvent(Event[Pipe]);
// 索检一个异步(重叠)操作实例当前的状态
// PipeHandles[Pipe] 要索检的管道句柄
// Ovlap[Pipe] 之前设定的该管道实例的异步结构
// &BytesTransferred 用于记录该次传输字节数量的一个缓冲区
// TRUE 指明一直等到异步操作结束才返回;FALSE表示立即返回
if (GetOverlappedResult(PipeHandles[Pipe], &Ovlap[Pipe],
&BytesTransferred, TRUE) == 0)
{
printf("取得异步操作状态失败或客户端关闭连接,错误代码: %d/n 重新启动该管道实例/n",
GetLastError());
// 断开当前管道连接
if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)
{
printf("管道断开失败,错误代码: %d/n",
GetLastError());
return -1;
}
// 重新开始监听当前管道实例
if (ConnectNamedPipe(PipeHandles[Pipe], &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("管道实例 %d 重启失败,错误代码: %d/n",
i, GetLastError());
// 关闭失败的管道实例
CloseHandle(PipeHandles[Pipe]);
}
}
// 管道实例状态
DataRead[Pipe] = FALSE;
}
// 当取得一个重叠操作状态未发生错误时,则开始读或写操作
else
{
if (DataRead[Pipe] == FALSE)
{
// 重设当前异步结构
ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
Ovlap[Pipe].hEvent = Event[Pipe];
// 读取数据,因异步操作,该函数立刻返回,当读取完毕时,触发事件对象有效
if (ReadFile(PipeHandles[Pipe], Buffer[Pipe],
BUFFER_SIZE, NULL, &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("读取消息时发生错误,错误代码: %d/n",
GetLastError());
}
}
DataRead[Pipe] = TRUE;
}
else
{
printf("读取到的数据大小为: %d bytes/n",
BytesTransferred);
ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPED));
Ovlap[Pipe].hEvent = Event[Pipe];
// 将读取到的数据返回给客户端
if (WriteFile(PipeHandles[Pipe], Buffer[Pipe],
BytesTransferred, NULL, &Ovlap[Pipe]) == 0)
{
if (GetLastError() != ERROR_IO_PENDING)
{
printf("发送消息时发生错误,错误代码: %d/n",
GetLastError());
}
}
DataRead[Pipe] = FALSE;
}
}
}
return 0;
}
命名管道客户端
#pragma once
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#define PIPE_NAME TEXT("////.//Pipe//Tc")
#define MyDATE TEXT("测试。。。测试")
int _tmain(int argc, _TCHAR* argv[])
{
HANDLE PipeHandle;
DWORD BytesWritten;
// 检查是否存在一个现成的命名管道实例
// PIPE_NAME 试图与之建立连接的命名管道服务器名称
// NMPWAIT_WAIT_FOREVER 指定客户机可以等待一个管道进程的时间;
// 让它在管道上完成一个待决的ConnectNamedPipe操作
// 该常数表示无限制
if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)
{
printf("等待管道连接失败,错误代码: %d/n",
GetLastError());
return -1;
}
// 打开一个管道
// PIPE_NAME 要打开的命名管道服务器名称
// GENERIC_READ | GENERIC_WRITE 打开模式,可读可写。该模式需要与服务端兼容
// 参数三 0 表示不共享;因为一次只能有一个客户机访问一个管道实例
// (LPSECURITY_ATTRIBUTES) NULL 该参数应设为NULL,除非需要子进程继承客户机的句柄
// OPEN_EXISTING 管道实例必须已经存在,函数会在管道不存在的情况下调用失败
// FILE_ATTRIBUTE_NORMAL 默认属性
// (HANDLE) NULL 它对命名管道无效,应设为NULL
if ((PipeHandle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE, 0,
(LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL)) == INVALID_HANDLE_VALUE)
{
printf("打开管道失败,错误代码: %d/n", GetLastError());
return -1;
}
if (WriteFile(PipeHandle, MyDATE, sizeof(MyDATE), &BytesWritten,
NULL) == 0)
{
printf("发送消息失败,错误代码: %d/n", GetLastError());
CloseHandle(PipeHandle);
return -1;
}
printf("发送消息数量为: %d bytes", BytesWritten);
CloseHandle(PipeHandle);
return 0;
}