关于windows异步命名管道通信的一些记录

分为服务端和客户端,参考使用完成例程的命名管道服务器 - 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初始化。只要有

你可能感兴趣的:(windows)