NamedPipe,利用IOCP实现命名管道异步通信

微软提供了利用回调函数来实现有名管道的示例:Named Pipe Server Using Completion Routines. 该示例使用了ReadFileEx和WriteFileEx函数来进行有名管道的异步读写操作。我们在该示例的基础上进行改写来实现通过IOCP来进行异步读写。主要改动在哪下几个方面:

1.删去 CreateEvent操作,改为CreateIoCompletionPort来算成完成端口,替换事件触发为完成端口触发;
2.IOCP不支持ReadFileEx和WriteFieEx函数,因此相应地替换为ReadFile和WriteFile函数。则回调函数的处理改为接收到GetQueuedCompletionStatus的返回后对完成状态进行处理;
3.在PIPEINST结构中增加opType成员变量,以便区分完成状态是牌读、写或者连接。

现在网上很多IOCP的教程示例都是基于SOCKET编程的,由于SOCKET编程相对于管道编程更加复杂,使得初学者不易掌握。而管道编程相对简单,使得初学者可以专注于IOCP部分的编程,因而更易掌握。在掌握了管道上的IOCP编程后再去学习SOCKET上的IOCP便不会觉得难了。而且管道上的IOCP编程还是有其实用性的,比如我们要编写一个网络通信程序,就可以给这个程序编写一个控制台程序,两个程序通过管道进行交互,这样网络能让部分和管道部分都可以放在一个完成端口内进行处理,不再需要用多个线程,效率就比较高,程序架构也会比较简单。当然我们也可以使用线程池的设计,但SOCKET和管道都可以在线程池里进行统一处理,不需要单独为管道处理建立一个线程。

#include <windows.h> 
#include <stdio.h>
#include <tchar.h>
#include <strsafe.h>
enum enum_op_type {
	OP_ACCEPT,
	OP_SEND,
	OP_RECV,
	OP_MAX,
};
const char *op_names[] = {
	"ACCEPT",
	"SEND",
	"RECV"
};
#define PIPE_TIMEOUT 5000
#define BUFSIZE 4096

typedef struct
{
	OVERLAPPED oOverlap;
	DWORD opType;
	HANDLE hPipeInst;
	TCHAR chRequest[BUFSIZE];
	DWORD cbRead;
	TCHAR chReply[BUFSIZE];
	DWORD cbToWrite;
} PIPEINST, *LPPIPEINST;

VOID DisconnectAndClose(LPPIPEINST);
BOOL CreateAndConnectInstance();
BOOL ConnectToNewClient(HANDLE);
VOID GetAnswerToRequest(LPPIPEINST);

VOID WINAPI CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED);
VOID WINAPI CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED);

// HANDLE hPipe;
HANDLE hIocp;

int _tmain(VOID)
{
	DWORD dwErr;
	BOOL fSuccess;
	DWORD dwNoOfBytes = 0;
	ULONG_PTR ulKey = 0;
	OVERLAPPED* pov = NULL;

	// Create one event object for the connect operation. 
	hIocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

	// Call a subroutine to create one instance, and wait for 
	// the client to connect. 

	CreateAndConnectInstance();

	while (1)
	{
		// Wait for a completion notification.  
		pov = NULL;
		fSuccess = GetQueuedCompletionStatus(
			hIocp,         // Completion port handle  
			&dwNoOfBytes,  // Bytes transferred  
			&ulKey,
			&pov,          // OVERLAPPED structure  
			INFINITE       // Notification time-out interval  
		);

		if (FALSE == fSuccess) {
			dwErr = GetLastError();
			printf("GetQueuedCompletionStatus: dwErr=%x.\n", dwErr);
		}
		// Get the base address of the RECEIVE_CONTEXT structure   
		// containing the OVERLAPPED structure received.  
		LPPIPEINST ppi = CONTAINING_RECORD(pov, PIPEINST, oOverlap);
		DWORD opType = ppi->opType;
		printf("GetQueuedCompletionStatus: opType=%s,dwNoOfBytes=%d.\n", op_names[opType], dwNoOfBytes);

		if (OP_ACCEPT == opType) {
			// Allocate storage for this instance. 

			ppi->cbToWrite = 0;
			CompletedWriteRoutine(0, 0, (LPOVERLAPPED)ppi);

			// Create new pipe instance for the next client. 

			CreateAndConnectInstance();

			// The wait is satisfied by a completed read or write 
			// operation. This allows the system to execute the 
			// completion routine. 
		}
		else if (OP_RECV == opType) {
			if (!dwNoOfBytes) {
				DisconnectAndClose(ppi);
			}
			else {
				CompletedReadRoutine(0, dwNoOfBytes, (LPOVERLAPPED)ppi);
			}
		}
		else {
			CompletedWriteRoutine(0, dwNoOfBytes, (LPOVERLAPPED)ppi);
		}
	}
	return 0;
}

// CompletedWriteRoutine(DWORD, DWORD, LPOVERLAPPED) 
// This routine is called as a completion routine after writing to 
// the pipe, or when a new client has connected to a pipe instance.
// It starts another read operation. 

VOID WINAPI CompletedWriteRoutine(DWORD dwErr, DWORD cbWritten,
	LPOVERLAPPED lpOverLap)
{
	LPPIPEINST lpPipeInst;
	BOOL fRead = FALSE;

	// lpOverlap points to storage for this instance. 

	lpPipeInst = (LPPIPEINST)lpOverLap;

	// The write operation has finished, so read the next request (if 
	// there is no error). 
	if ((dwErr == 0) && (cbWritten == lpPipeInst->cbToWrite)) {
		DWORD dwRead;
		lpPipeInst->opType = OP_RECV;
	
		fRead = ReadFile(
		lpPipeInst->hPipeInst,
		lpPipeInst->chRequest,
		BUFSIZE * sizeof(TCHAR),
		&dwRead,
		(LPOVERLAPPED)lpPipeInst);
		//(LPOVERLAPPED_COMPLETION_ROUTINE)CompletedReadRoutine);
		if (!fRead && ERROR_IO_PENDING != GetLastError()) {
			printf("ReadFile: dwRead=%d,GLE=%d\n", dwRead, GetLastError());
		}
	}

	// Disconnect if an error occurred. 
	printf("%s: PipInst=%x,dwErr=%x,cbWritten=%d,fRead=%d.\n",
		__func__, (int)lpPipeInst->hPipeInst, dwErr, cbWritten, fRead);

	/*
	if (!fRead) {
		DisconnectAndClose(lpPipeInst);
	}
	*/
}

// CompletedReadRoutine(DWORD, DWORD, LPOVERLAPPED) 
// This routine is called as an I/O completion routine after reading 
// a request from the client. It gets data and writes it to the pipe. 

VOID WINAPI CompletedReadRoutine(DWORD dwErr, DWORD cbBytesRead,
	LPOVERLAPPED lpOverLap)
{
	LPPIPEINST lpPipeInst;
	BOOL fWrite = FALSE;

	// lpOverlap points to storage for this instance. 
	/*
	ERROR_BROKEN_PIPE
	109 (0x6D)
	The pipe has been ended.
	*/
	lpPipeInst = (LPPIPEINST)lpOverLap;
	if (0x6d == dwErr) {
		printf("%s: PipInst=%x, the pipe has been ended.\n",
			__func__, (int)lpPipeInst->hPipeInst);
	}
	else {
		printf("%s: PipInst=%x,dwErr=%x,cbBytesRead=%d.\n",
			__func__, (int)lpPipeInst->hPipeInst, dwErr, cbBytesRead);
	}

	// The read operation has finished, so write a response (if no 
	// error occurred). 

	if ((dwErr == 0) && (cbBytesRead != 0))
	{
		DWORD dwWritten;
		GetAnswerToRequest(lpPipeInst);
		lpPipeInst->opType = OP_SEND;

		fWrite = WriteFile(
			lpPipeInst->hPipeInst,
			lpPipeInst->chReply,
			lpPipeInst->cbToWrite,
			&dwWritten,
			(LPOVERLAPPED)lpPipeInst);
			// (LPOVERLAPPED_COMPLETION_ROUTINE)CompletedWriteRoutine);
		if (!fWrite) {
			printf("WriteFie: dwWritten=%d,GLE=%d\n", dwWritten, GetLastError());
		}
	}

	// Disconnect if an error occurred. 

	/*
	if (!fWrite) {
		DisconnectAndClose(lpPipeInst);
	}
	*/
}

// DisconnectAndClose(LPPIPEINST) 
// This routine is called when an error occurs or the client closes 
// its handle to the pipe. 

VOID DisconnectAndClose(LPPIPEINST lpPipeInst)
{
	// Disconnect the pipe instance. 

	if (!DisconnectNamedPipe(lpPipeInst->hPipeInst))
	{
		printf("DisconnectNamedPipe failed with %d.\n", GetLastError());
	}

	// Close the handle to the pipe instance. 

	CloseHandle(lpPipeInst->hPipeInst);

	// Release the storage for the pipe instance. 

	if (lpPipeInst != NULL)
		GlobalFree(lpPipeInst);
}

// CreateAndConnectInstance(LPOVERLAPPED) 
// This function creates a pipe instance and connects to the client. 
// It returns TRUE if the connect operation is pending, and FALSE if 
// the connection has been completed. 

BOOL CreateAndConnectInstance()
{
	LPCWSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

	HANDLE hPipe = CreateNamedPipe(
		lpszPipename,             // pipe name 
		PIPE_ACCESS_DUPLEX |      // read/write access 
		FILE_FLAG_OVERLAPPED,     // overlapped mode 
		PIPE_TYPE_MESSAGE |       // message-type pipe 
		PIPE_READMODE_MESSAGE |   // message read mode 
		PIPE_WAIT,                // blocking mode 
		PIPE_UNLIMITED_INSTANCES, // unlimited instances 
		BUFSIZE * sizeof(TCHAR),    // output buffer size 
		BUFSIZE * sizeof(TCHAR),    // input buffer size 
		PIPE_TIMEOUT,             // client time-out 
		NULL);                    // default security attributes

	printf("CreateNamedPipe, hPipe=%llx.\n", (INT64)hPipe);
	if (hPipe == INVALID_HANDLE_VALUE)
	{
		printf("CreateNamedPipe failed with %d.\n", GetLastError());
		return 0;
	}
	HANDLE hRet = CreateIoCompletionPort(hPipe, hIocp, NULL, 0);
	// Call a subroutine to connect to the new client. 

	return ConnectToNewClient(hPipe);
}

BOOL ConnectToNewClient(HANDLE hPipe)
{
	BOOL fConnected, fPendingIO = FALSE;

	LPPIPEINST lpPipeInst = (LPPIPEINST)GlobalAlloc(GPTR, sizeof(PIPEINST));
	if (lpPipeInst == NULL)
	{
		printf("GlobalAlloc failed (%d)\n", GetLastError());
		return 0;
	}
	ZeroMemory(&lpPipeInst->oOverlap, sizeof(OVERLAPPED));
	lpPipeInst->hPipeInst = hPipe;
	lpPipeInst->opType = OP_ACCEPT;

	// Start an overlapped connection for this pipe instance. 
	fConnected = ConnectNamedPipe(hPipe, &lpPipeInst->oOverlap);

	// Overlapped ConnectNamedPipe should return zero. 
	if (fConnected)
	{
		printf("ConnectNamedPipe failed with %d.\n", GetLastError());
		return 0;
	}

	switch (GetLastError())
	{
		// The overlapped connection in progress. 
	case ERROR_IO_PENDING:
		fPendingIO = TRUE;
		break;

		// Client is already connected, so signal an event. 

	case ERROR_PIPE_CONNECTED:
	{
		CreateAndConnectInstance();
	}
			break;

		// If an error occurs during the connect operation... 
	default:
	{
		printf("ConnectNamedPipe failed with %d.\n", GetLastError());
		return 0;
	}
	}
	return fPendingIO;
}

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




  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值