分为服务端和客户端,参考使用完成例程的命名管道服务器 - Win32 apps | Microsoft Learn
服务端使用CreateNamedPipe和ConnectNamedPipe等待客户端连接,ConnectNamedPipe的第二个参数是overlap,需要初始化其中的hEvent。要注意CreateEvent的第二个参数bManualReset:是否手动重置事件,一般手动设置的事件需要在事件触发后自行reset,是event处于无信号状态,如果是自动设置的事件,事件触发后,所有的wait结束后会自行变为无信号状态。读和写完成的时候也会让wait事件返回,只不过该值固定是WAIT_IO_COMPLETION。
客户端使用CreateFile打开命名管道,使用ReadFileEx和WriteFileEx进行管道的读写,经过测试,可以不用给overlap的hevent初始化,只需要有wait函数,读写完成后就能触发完成回调函数。
可能遇到的问题:WriteFileEx、ReadFileEx即使实际上已经完成,但是完成函数没有被调用,这种情况一般是因为没有wait函数,与overlap中的hevent是否初始化反而关系不大。
wait函数一般是WaitForSingleObjectEx、WaitForMultipleObjectsEx
定义通用结构
#pragma once
#include
#define PIPE_NAME L"\\\\.\\pipe\\mypipe.test"
#define BUFF_SIZE 81920
#define PIPE_TIMEOUT 5000
typedef struct _message_item {
int type;
int size;
char data[0];
}MESSAGE_ITEM;
typedef struct _overlap_more {
OVERLAPPED overlap;
HANDLE hPipe;
int dwLastPos;
char buffer[1024];
}OVERLAP_ITEM;
void pipeServer();
void pipeClient();
服务端比较简单:
#include
#include
#include
#include "pipeCommon.h"
HANDLE m_hPipe = NULL;
HANDLE m_hConnectEvent = NULL;
HANDLE m_hOutMessageEvent = NULL;
OVERLAP_ITEM overlapRead;
OVERLAP_ITEM overlapWrite;
std::queue m_arrMessage;
HRESULT ConnectNewClient(LPOVERLAPPED lpOverlapped)
{
HRESULT hr = S_OK;
BOOL fConnected = ConnectNamedPipe(m_hPipe, lpOverlapped);
printf("server ConnectNamedPipe return bool:%d\n", fConnected);
if (fConnected)
{
return HRESULT_FROM_WIN32(GetLastError());
}
DWORD dwErr = GetLastError();
printf("ConnectNamedPipe wait:%d,pending:997,connected:535\n", dwErr);
switch (dwErr)
{
case ERROR_IO_PENDING:
hr = S_FALSE;
break;
case ERROR_PIPE_CONNECTED:
SetEvent(m_hConnectEvent);
break;
default:
hr = HRESULT_FROM_WIN32(dwErr);
break;
}
return hr;
}
HRESULT CreateAndConnectInstance(LPOVERLAPPED lpOverlapped)
{
m_hPipe = CreateNamedPipe(
PIPE_NAME,
PIPE_ACCESS_DUPLEX |
FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE |
PIPE_READMODE_BYTE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
BUFF_SIZE,
BUFF_SIZE,
PIPE_TIMEOUT,
NULL);
if (m_hPipe == NULL) {
return HRESULT_FROM_WIN32(GetLastError());
}
return ConnectNewClient(lpOverlapped);
}
void pushMessage(const char* prefix) {
SYSTEMTIME st;
GetLocalTime(&st);
MESSAGE_ITEM* pRespone = (MESSAGE_ITEM*)new char[sizeof(MESSAGE_ITEM) + 100];
sprintf_s(pRespone->data, 100, "%s server response at:%d-%02d-%02d %02d:%02d:%02d.%03d", prefix?prefix:"*..*", st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
pRespone->type = st.wSecond;
pRespone->size = strlen(pRespone->data) + 1;
m_arrMessage.push(pRespone);
printf("server add message:%d,len %d\n", m_arrMessage.size(), pRespone->size);
if (m_arrMessage.size() == 1) {
printf("server trigger message event\n");
SetEvent(m_hOutMessageEvent);
}
}
void DispatchServerMessage(OVERLAP_ITEM* pInst) {
MESSAGE_ITEM* pMessageItem = (MESSAGE_ITEM*)pInst->buffer;
printf("server get message:size:%d,%s\n", pMessageItem->size, pMessageItem->data);
pushMessage("dispatch");
}
void CALLBACK WriteCompletionRoutine(DWORD dwErr, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OVERLAP_ITEM* pPipeInst = (OVERLAP_ITEM*)lpOverlapped;
if (dwErr != 0)
{
printf("WriteCompletionRoutinue failed, %lu\n", dwErr);
return;
}
printf("WriteCompletionRoutine size = %d, total size = %d, error:%d\n", dwNumberOfBytesTransfered, pPipeInst->dwLastPos, dwErr);
pPipeInst->dwLastPos += dwNumberOfBytesTransfered;
MESSAGE_ITEM* pGuestMessage = m_arrMessage.front();
DWORD dwCount = pGuestMessage->size + sizeof(MESSAGE_ITEM) - pPipeInst->dwLastPos;
if (dwCount == 0)
{
pPipeInst->dwLastPos = 0;
m_arrMessage.pop();
//vd_printf("write success, size = %d, type = %d", pGuestMessage->header.size, pGuestMessage->header.type);
delete pGuestMessage;
if (m_arrMessage.size())
{
pGuestMessage = m_arrMessage.front();
dwCount = pGuestMessage->size + sizeof(MESSAGE_ITEM);
}
}
if (dwCount > 0)
{
//vd_printf("dwCount size = %d, size = %d, type = %d, pos = %d", dwCount, pGuestMessage->header.size, pGuestMessage->header.type, pPipeInst->dwWritePos);
if (!WriteFileEx(pPipeInst->hPipe, (char*)pGuestMessage + pPipeInst->dwLastPos, dwCount, lpOverlapped, WriteCompletionRoutine))
{
printf("WriteFileEx failed, %lu\n", GetLastError());
}
}
}
void CALLBACK ReadCompletionRoutine(DWORD dwErr, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OVERLAP_ITEM* pPipeInst = (OVERLAP_ITEM*)lpOverlapped;
printf("ReadCompletionRoutine %d,%d\n", dwErr, dwNumberOfBytesTransfered);
if (dwErr != 0)
{
if (dwErr == 109) {
printf("ReadCompletionRoutine server disconnected\n");
}
else {
printf("ReadCompletionRoutine server read completion error:%d\n", dwErr);
}
return;
}
DWORD dwCount = 0;
MESSAGE_ITEM* pGuestHeader = (MESSAGE_ITEM*)pPipeInst->buffer;
pPipeInst->dwLastPos += dwNumberOfBytesTransfered;
//vd_printf("read size = %d, total size = %d", dwNumberOfBytesTransfered, pPipeInst->dwReadPos);
if (pPipeInst->dwLastPos < sizeof(MESSAGE_ITEM)) {
dwCount = sizeof(MESSAGE_ITEM) - pPipeInst->dwLastPos;
//pushMessage("short head");
}
else if (pPipeInst->dwLastPos == sizeof(MESSAGE_ITEM))
{
dwCount = pGuestHeader->size;
//vd_printf("read header end, type = %d, size = %d", pGuestHeader->type, pGuestHeader->size);
if (dwCount + pPipeInst->dwLastPos > sizeof(pPipeInst->buffer))
{
printf("read size too large dwCount = %lu, dwReadPos = %lu\n", dwCount, pPipeInst->dwLastPos);
return;
}
}
else if (pPipeInst->dwLastPos == pGuestHeader->size + sizeof(MESSAGE_ITEM))
{
DispatchServerMessage(pPipeInst);
pPipeInst->dwLastPos = 0;
dwCount = sizeof(MESSAGE_ITEM);
//vd_printf("Read success, type = %d, size =%d start read new :%d", pGuestHeader->type, pGuestHeader->size, GUEST_MESSAGE_HEADER_SIZE);
}
else
{
if (pPipeInst->dwLastPos > sizeof(MESSAGE_ITEM) + pGuestHeader->size)
{
printf("read size too large , dwReadPos = %lu, size = %d, type = %d\n", pPipeInst->dwLastPos, pGuestHeader->size, pGuestHeader->type);
return;
}
//pushMessage("short message\n");
dwCount = sizeof(MESSAGE_ITEM) + pGuestHeader->size - pPipeInst->dwLastPos;
}
if (dwCount > 0)
{
printf("server try read:%p,%d,for %p\n", (char*)pPipeInst->buffer + pPipeInst->dwLastPos, dwCount, pPipeInst->hPipe);
if (!ReadFileEx(pPipeInst->hPipe, (char*)pPipeInst->buffer + pPipeInst->dwLastPos, dwCount, lpOverlapped, ReadCompletionRoutine))
{
printf("ReadFileEx failed, %lu\n", GetLastError());
}
}
}
void pipeServer() {
OVERLAPPED oOverlapped = {};
DWORD dwRet = 0;
int iIndex = 0;
m_hConnectEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
m_hOutMessageEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
oOverlapped.hEvent = m_hConnectEvent;
HRESULT hr = CreateAndConnectInstance(&oOverlapped);
if (FAILED(hr))
{
printf("CreateAndConnectInstance, 0x%lu\n", hr);
return;
}
HANDLE handles[] = { m_hConnectEvent, m_hOutMessageEvent };
DWORD dwCount = sizeof(handles) / sizeof(HANDLE);
while (1)
{
++iIndex;
if (iIndex > 100) {
printf("server loop out\n");
break;
}
printf("server wait event:%d\n", iIndex);
DWORD dwRet = WaitForMultipleObjectsEx(dwCount, handles, FALSE, INFINITE, TRUE);
switch (dwRet)
{
case 0: //m_hConnectEvent;
{
printf("server get connect event:%d\n", iIndex);
if (hr == S_FALSE)
{
DWORD dwNumberOfBytesTransfer = 0;
BOOL fSuccess = GetOverlappedResult(m_hPipe, &oOverlapped, &dwNumberOfBytesTransfer, FALSE);
if (!fSuccess)
{
DWORD dwErr = GetLastError();
printf("server GetOverlappedResult failed:%lu\n", dwErr);
continue;
}
}
memset(&overlapRead, 0, sizeof(overlapRead));
memset(&overlapWrite, 0, sizeof(overlapWrite));
overlapRead.hPipe = m_hPipe;
overlapWrite.hPipe = m_hPipe;
printf("try read\n");
dwRet = ReadFileEx(overlapRead.hPipe, overlapRead.buffer, sizeof(MESSAGE_ITEM), &overlapRead.overlap, ReadCompletionRoutine);
if (!dwRet) {
printf("failed to readfile ex server:%d\n", GetLastError());
}
else {
printf("server readfileex success\n");
}
//pushMessage("no read");
}
break;
case 1:
{
printf("server get message event:%d, need write pipe\n", iIndex);
WriteCompletionRoutine(0, 0, &overlapWrite.overlap);
}
break;
case WAIT_IO_COMPLETION: {
printf("server wait io completion:%d\n", iIndex);
}
break;
default: {
printf("server event:%d,%d\n", dwRet, iIndex);
}
break;
}
}
}
客户端要注意是否需要CreateEvent:
#include
#include
#include
#include "pipeCommon.h"
HANDLE _hPipe = NULL;
HANDLE m_hClientMessageEvent = NULL;
OVERLAP_ITEM clientOverlapRead;
OVERLAP_ITEM clientOverlapWrite;
std::queue m_arrClientMessage;
void CALLBACK ClientWriteCompletion(DWORD dwErr, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped);
bool ConnectNamepipe()
{
if (!WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER))
{
printf("WaitNamedPipe failed = %d\n", GetLastError());
return false;
}
_hPipe = CreateFile(
PIPE_NAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == _hPipe)
{
printf("CreateFile failed = %d\n", GetLastError());
CloseHandle(_hPipe);
_hPipe = NULL;
return false;
}
printf("CreateFile succeed _hPipe = %p\n", _hPipe);
return true;
}
void CloseNamepipe()
{
if (_hPipe)
{
CloseHandle(_hPipe);
_hPipe = NULL;
}
}
void pushClientMessage(const char* prefix) {
SYSTEMTIME st;
GetLocalTime(&st);
MESSAGE_ITEM* pRespone = (MESSAGE_ITEM*)new char[sizeof(MESSAGE_ITEM) + 100];
sprintf_s(pRespone->data, 100, "%s client send at:%d-%02d-%02d %02d:%02d:%02d.%03d", prefix ? prefix : "*..*", st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
pRespone->type = st.wSecond;
pRespone->size = strlen(pRespone->data) + 1;
m_arrClientMessage.push(pRespone);
printf("client message size:%d, curlen:%d\n", m_arrClientMessage.size(), pRespone->size);
if (m_arrClientMessage.size() == 1) {
//SetEvent(m_hClientMessageEvent);
ClientWriteCompletion(0, 0, &clientOverlapWrite.overlap);
}
}
void DispatchClientMessage(OVERLAP_ITEM* pInst) {
MESSAGE_ITEM* pMessageItem = (MESSAGE_ITEM*)pInst->buffer;
printf("DispatchClientMessage size:%d,%s\n", pMessageItem->size, pMessageItem->data);
pushClientMessage("dispatch");
}
void CALLBACK ClientReadCompletion(DWORD dwErr, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OVERLAP_ITEM* pPipeInst = (OVERLAP_ITEM*)lpOverlapped;
printf("ClientReadCompletion %d,%d\n", dwErr, dwNumberOfBytesTransfered);
if (dwErr != 0)
{
if (dwErr == 109) {
printf("ClientReadCompletion disconnected\n");
}
else {
printf("ClientReadCompletion read completion error:%d\n", dwErr);
}
return;
}
DWORD dwCount = 0;
MESSAGE_ITEM* pGuestHeader = (MESSAGE_ITEM*)pPipeInst->buffer;
pPipeInst->dwLastPos += dwNumberOfBytesTransfered;
//vd_printf("read size = %d, total size = %d", dwNumberOfBytesTransfered, pPipeInst->dwReadPos);
if (pPipeInst->dwLastPos < sizeof(MESSAGE_ITEM)) {
dwCount = sizeof(MESSAGE_ITEM) - pPipeInst->dwLastPos;
//pushClientMessage("short head");
}
else if (pPipeInst->dwLastPos == sizeof(MESSAGE_ITEM))
{
dwCount = pGuestHeader->size;
//vd_printf("read header end, type = %d, size = %d", pGuestHeader->type, pGuestHeader->size);
if (dwCount + pPipeInst->dwLastPos > sizeof(pPipeInst->buffer))
{
printf("read size too large dwCount = %lu, dwReadPos = %lu\n", dwCount, pPipeInst->dwLastPos);
return;
}
}
else if (pPipeInst->dwLastPos == pGuestHeader->size + sizeof(MESSAGE_ITEM))
{
DispatchClientMessage(pPipeInst);
pPipeInst->dwLastPos = 0;
dwCount = sizeof(MESSAGE_ITEM);
//vd_printf("Read success, type = %d, size =%d start read new :%d", pGuestHeader->type, pGuestHeader->size, GUEST_MESSAGE_HEADER_SIZE);
}
else
{
if (pPipeInst->dwLastPos > sizeof(MESSAGE_ITEM) + pGuestHeader->size)
{
printf("read size too large , dwReadPos = %lu, size = %d, type = %d\n", pPipeInst->dwLastPos, pGuestHeader->size, pGuestHeader->type);
return;
}
//pushClientMessage("short message");
dwCount = sizeof(MESSAGE_ITEM) + pGuestHeader->size - pPipeInst->dwLastPos;
}
if (dwCount > 0)
{
if (!ReadFileEx(pPipeInst->hPipe, pPipeInst->buffer + pPipeInst->dwLastPos, dwCount, lpOverlapped, ClientReadCompletion))
{
printf("ReadFileEx failed, %lu\n", GetLastError());
}
}
}
void CALLBACK ClientWriteCompletion(DWORD dwErr, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
{
OVERLAP_ITEM* pPipeInst = (OVERLAP_ITEM*)lpOverlapped;
if (dwErr != 0)
{
printf("ClientWriteCompletion failed, %lu\n", dwErr);
return;
}
printf("ClientWriteCompletion size = %d, total size = %d\n", dwNumberOfBytesTransfered, pPipeInst->dwLastPos);
pPipeInst->dwLastPos += dwNumberOfBytesTransfered;
MESSAGE_ITEM* pGuestMessage = m_arrClientMessage.front();
DWORD dwCount = pGuestMessage->size + sizeof(MESSAGE_ITEM) - pPipeInst->dwLastPos;
printf("dwCount=%d, message arr size:%d, pipe=%p, message:%p\n", dwCount, m_arrClientMessage.size(), pPipeInst->hPipe, pGuestMessage);
if (dwCount == 0)
{
pPipeInst->dwLastPos = 0;
m_arrClientMessage.pop();
//vd_printf("write success, size = %d, type = %d", pGuestMessage->header.size, pGuestMessage->header.type);
delete pGuestMessage;
if (m_arrClientMessage.size())
{
pGuestMessage = m_arrClientMessage.front();
dwCount = pGuestMessage->size + sizeof(MESSAGE_ITEM);
printf("new dwCount=%d\n", dwCount);
}
}
if (dwCount > 0)
{
//BOOL bSuccess = FALSE;
//DWORD dwAvailableBytes = 0;
//bSuccess = PeekNamedPipe(pPipeInst->hPipe, NULL, 0, NULL, &dwAvailableBytes, NULL);
//if (!bSuccess || dwAvailableBytes == 0) {
// printf("client Pipe is closed or empty\n");
// CloseHandle(pPipeInst->hPipe);
// return;
//}
//vd_printf("dwCount size = %d, size = %d, type = %d, pos = %d", dwCount, pGuestMessage->header.size, pGuestMessage->header.type, pPipeInst->dwWritePos);
printf("client try write:%p,%d for pipe:%p\n", pGuestMessage + pPipeInst->dwLastPos, dwCount, pPipeInst->hPipe);
if (!WriteFileEx(pPipeInst->hPipe, (char*)pGuestMessage + pPipeInst->dwLastPos, dwCount, lpOverlapped, ClientWriteCompletion))
{
printf("client WriteFileEx failed, %lu\n", GetLastError());
}
else {
printf("client WriteFileEx success\n");
}
}
}
void EventDispatcher(DWORD timeout, DWORD wake_mask)
{
HANDLE events[1];
DWORD event_count = 1;
DWORD wait_ret;
MSG msg;
HANDLE _control_event = CreateEvent(NULL, FALSE, FALSE, NULL);
events[0] = _control_event;
wait_ret = MsgWaitForMultipleObjectsEx(event_count, events, timeout, wake_mask, MWMO_ALERTABLE);
if (wait_ret == WAIT_OBJECT_0 + event_count)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return;
}
else if (wait_ret == WAIT_IO_COMPLETION || wait_ret == WAIT_TIMEOUT)
{
return;
}
else if (wait_ret < WAIT_OBJECT_0 || wait_ret > WAIT_OBJECT_0 + event_count)
{
return;
}
}
void pipeClient() {
BOOL bRet = FALSE;
DWORD dwErr = 0;
int iIndex = 0;
//m_hClientMessageEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
do{
bRet = ConnectNamepipe();
Sleep(2000);
} while (!bRet);
memset(&clientOverlapRead, 0, sizeof(clientOverlapRead));
memset(&clientOverlapWrite, 0, sizeof(clientOverlapWrite));
clientOverlapRead.hPipe = _hPipe;
//clientOverlapRead.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
clientOverlapWrite.hPipe = _hPipe;
//clientOverlapWrite.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!ReadFileEx(_hPipe, clientOverlapRead.buffer, sizeof(MESSAGE_ITEM), &clientOverlapRead.overlap, ClientReadCompletion))
{
dwErr = GetLastError();
printf("client pipe read error %lu\n", dwErr);
return;
}
else {
printf("Client ReadFileEx success\n");
}
while (TRUE) {
printf("wait:%d\n", ++iIndex);
if (3 == iIndex) {
pushClientMessage("client first:");
}
Sleep(1000);
if (6 == iIndex) {
break;
}
}
//while (true)
//{
// EventDispatcher(INFINITE, QS_ALLINPUT);
//}
HANDLE nouseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
//HANDLE handles[] = { clientOverlapRead.overlap.hEvent, clientOverlapWrite.overlap.hEvent };
HANDLE handles[] = { nouseEvent };
DWORD dwCount = sizeof(handles) / sizeof(HANDLE);
while (TRUE) {
printf("client wait:%d\n", ++iIndex);
DWORD dwRet = WaitForMultipleObjectsEx(dwCount, handles, FALSE, INFINITE, TRUE);
printf("client get event:%d\n", dwRet);
switch (dwRet)
{
case 0: //read
{
printf("get read event\n");
}
break;
case 1: //write
{
printf("get write event\n");
}
break;
case WAIT_IO_COMPLETION: {
printf("client wait io completion:%d\n", iIndex);
}
break;
default: {
printf("client event:%d,%d\n", dwRet, iIndex);
}
break;
}
}
}
执行CreateFileEx和WriteFileEx的时候不一定需要给overlap的hEvent初始化。只要有