进程间通信 —— 命名管道(实例)

1、最简单的例子:

服务端:

#include 
#include 

void main(void)
{
	HANDLE PipeHandle;
	DWORD BytesRead;
	CHAR buffer[256] = {0};

	if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\mypipe",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,1,0,0,1000,NULL)) == INVALID_HANDLE_VALUE)
	{
		printf("CreateNamedPipe failed with error %x \n",GetLastError());
		return;
	}

	printf("Server is now running \n");

	if (ConnectNamedPipe(PipeHandle,NULL) == 0)
	{
		printf("ConnectNamePipe failed with error %x \n",GetLastError());
		CloseHandle(PipeHandle);
		return;
	}

	if(ReadFile(PipeHandle,buffer,sizeof(buffer),&BytesRead,NULL) <= 0)
	{
		printf("ReadFile failed with error %x \n",GetLastError());
		CloseHandle(PipeHandle);
		return;
	}

	printf("byteread = %d,buffer = %s \n",BytesRead,buffer);

	if (DisconnectNamedPipe(PipeHandle) == 0)
	{
		printf("DisconnectNamedPipe failed with error %x \n",GetLastError());
		return;
	}

	CloseHandle(PipeHandle);
	system("pause");

}

注意:同步情况下,ReadFile是阻塞读,遇到下列一个情况返回:

(1)A write operation completes on the write end of the pipe. 
(2)The number of bytes requested is read. 
(3)An error occurs.

客户端:

#include 
#include 

void main(void)
{
	HANDLE PipeHandle;
	DWORD BytesWritten;


	if (WaitNamedPipe("\\\\.\\Pipe\\mypipe", NMPWAIT_WAIT_FOREVER) == 0)
	{
		printf("CreateNamedPipe failed with error %x \n",GetLastError());
		return;
	}


	if ((PipeHandle = CreateFile("\\\\.\\Pipe\\mypipe",GENERIC_READ | GENERIC_WRITE, 0 ,(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) == INVALID_HANDLE_VALUE)
	{
		printf("CreateFile failed with error %x \n",GetLastError());
		return;
	}

	if(WriteFile(PipeHandle,"This is a test", 14, &BytesWritten, NULL) == 0)
	{
		printf("WriteFile failed with error %x \n",GetLastError());
		CloseHandle(PipeHandle);
		return;
	}
	
	printf("Wrote %d bytes \n", BytesWritten);
	CloseHandle(PipeHandle);

	system("pause");

}


2、多线程设计

服务端:为每一个实例创建一个单独线程进程通信

#include 
#include 
#include "CC_Debug.h"
#define NUM_PIPES 2
DWORD WINAPI PipeInstanceProc(LPVOID lpParameter);

void main(void)
{
	HANDLE ThreadHandle;
	INT i;
	DWORD ThreadId;

	for(i = 0; i < NUM_PIPES; i++)
	{
		//为每一个实例创建一个线程
		ThreadHandle = CreateThread(NULL,0,PipeInstanceProc,NULL,0,&ThreadId);
		if (NULL == ThreadHandle)
		{
			CC_A_INFO("CreateThread failed with error %x \n",GetLastError());
			return;
		}
		else
		{
			CC_A_INFO("CreateThread success! ThreadHandle = %x",ThreadHandle);
		}
		CloseHandle(ThreadHandle);		
	}

	system("pause");
}

DWORD WINAPI PipeInstanceProc(LPVOID lpParameter)
{
	HANDLE PipeHandle;
	DWORD BytesRead;
	DWORD BytesWritten;
	CHAR Buffer[256];

	//注意:这里一定要更改管道实例的参数,否则不能创建管道。
	if ((PipeHandle = CreateNamedPipe("\\\\.\\Pipe\\mypipe",PIPE_ACCESS_DUPLEX,PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,NUM_PIPES,0,0,1000,NULL)) == INVALID_HANDLE_VALUE)
	{
		CC_A_INFO("CreateNamedPipe failed with error %x \n",GetLastError());
		return 0;
	}
	else
	{
		CC_A_INFO("CreateNamedPipe sucess! PipeHandle = %x",PipeHandle);
	}

	CC_A_INFO("Server is now running ......");

	while (1)
	{
		if (ConnectNamedPipe(PipeHandle,NULL) == 0)
		{
			CC_A_INFO("ConnectNamedPipe failed!");
			break;
		}
		else
		{
			CC_A_INFO("ConnectNamedPipe success!");
		}

		while (ReadFile(PipeHandle,Buffer,sizeof(Buffer),&BytesRead,NULL) > 0)
		{
			CC_A_INFO("Echo %d bytes to Client",BytesRead);
		}

		//这里要断开与客户的连接,如果调用ConnectNamedPipe,可以为另外一个客户的提供服务。
		if (DisconnectNamedPipe(PipeHandle) == 0)
		{
			printf("DisconnectNamedPipe failed with error %x \n",GetLastError());
			CC_A_INFO("Server DisconnectNamedPipe");
			return 1;
		}

	}

	CC_A_INFO("Server exit");
	CloseHandle(PipeHandle);
	system("pause");

}

客户端:

#include 
#include 
#include "CC_Debug.h"

void main(void)
{
	HANDLE PipeHandle;
	DWORD BytesWritten = 0;


	if (WaitNamedPipe("\\\\.\\Pipe\\mypipe", NMPWAIT_WAIT_FOREVER) == 0)
	{
		printf("CreateNamedPipe failed with error %x \n",GetLastError());
		return;
	}

	if ((PipeHandle = CreateFile("\\\\.\\Pipe\\mypipe",GENERIC_READ | GENERIC_WRITE, 0 ,(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) == INVALID_HANDLE_VALUE)
	{
		CC_A_INFO("CreateFile failed with error %x \n",GetLastError());
		return;
	}

	if(WriteFile(PipeHandle,"This is a test", 14, &BytesWritten, NULL) == 0)
	{
		CC_A_INFO("WriteFile failed with error %x \n",GetLastError());
		CloseHandle(PipeHandle);
		return;
	}
	
	CC_A_INFO("Wrote %d bytes \n", BytesWritten);
	CloseHandle(PipeHandle);
	system("pause");

}

3、重叠IO实现

服务端:

#include 
#include 
#include "CC_Debug.h"
#define NUM_PIPES (2)
#define BUFFER_SIZE (256)

void main(void)
{
	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]; //用于保存pipe的状态
	DWORD Ret;
	DWORD Pipe;

	for(i = 0; i < NUM_PIPES; i++)
	{
		//注意:这里一定要更改管道实例的参数,否则不能创建管道。同时要指定FILE_FLAG_OVERLAPPED。
		if ((PipeHandles[i] = CreateNamedPipe("\\\\.\\Pipe\\mypipe",PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
			                 PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,NUM_PIPES,0,0,1000,NULL)) == INVALID_HANDLE_VALUE)
		{
			CC_A_INFO("CreateNamedPipe failed with error %x \n",GetLastError());
			return;
		}
		else
		{
			CC_A_INFO("CreateNamedPipe sucess! PipeHandle = %x",PipeHandles[i]);
		}

		Event[i] = CreateEvent(NULL,TRUE,FALSE,NULL); //手动重置、初始为无信号。
		if (NULL == Event[i])
		{
			CC_A_INFO("CreateEvent failed error = %x",GetLastError());
			continue;
		}

		DataRead[i] = FALSE;
		ZeroMemory(&Ovlap[i],sizeof(OVERLAPPED));

		Ovlap[i].hEvent = Event[i];

		/*
		ConnectNamedPipe:等待客户端连接
        &Ovlap[i]:如果此参数为NULL,如果此参数设为NULL,表示将线程挂起,直到一个客户同管道连接为时返回。
		           如果传入OVERLAPPED,表示是异步的,函数立即返回;
				   此时,如管道尚未连接,客户端同管道连接时就会触发lpOverlapped结构中的事件对象。
				   随后,可用一个等待函数来监视连接。
		*/	

		if (ConnectNamedPipe(PipeHandles[i],&Ovlap[i]) == 0)  //这里要传入一个Overlap结构。
		{
			if (GetLastError() != ERROR_IO_PENDING)  //ERROR_IO_PENDING说明重叠I/O投递成功,只是没有处理完成。
			{
				CC_A_INFO("ConnectNamedPipe failed ,errorcode = %x",GetLastError());
				CloseHandle(PipeHandles[i]);
				return;
			}
		}

	}
		CC_A_INFO("Server is now running ....");

		while(1)
		{
			/*
			WaitForMultipleObjects主要参数说明:
			(1)NUM_PIPES,等待事件个数
			(2)Event,事件数组。
			(3)FALSE:此参数如果为TRUE,则需要等所有对象都有信号,才返回。为FALSE,则只要有一个有信号,就返回
			            其返回值减去WAIT_OBJECT_0 就是参数lpHandles数组的序号。如果同时有多个内核对象被触发,这
						个函数返回的只是其中序号最小的那个。
			(4)INFINITE:等待时间。

			*/
			Ret = WaitForMultipleObjects(NUM_PIPES,Event,FALSE,INFINITE);  //这里每个客户端连接上的时候,都会设置有信号。
			if (Ret == WAIT_FAILED)
			{
				CC_A_INFO("WaitForMultipleObjects failed with error = %x",GetLastError());
				return;
			}

			Pipe = Ret - WAIT_OBJECT_0;
			ResetEvent(Event[Pipe]); //设置成无信号

			/*
			GetOverlappedResult:判断异步操作是否完成,通过OVERLAPPED结构中的hEvent是否被置位来判断。非0表示成功,0表示失败。
			(1)PipeHandles[Pipe]:管道句柄
			(2)&Ovlap[Pipe]:需要检查的结构
			(3)BytesTransferred:用于容纳传输字节数量的一个变量
			(4)如果为TRUE,就一直等到异步操作结束才返回。FALSE表示立即返回。
			*/
			
			if (GetOverlappedResult(PipeHandles[Pipe],&Ovlap[Pipe],&BytesTransferred,TRUE) == 0)
			{
				CC_A_INFO("GetOverlappedResult failed erro = %x",GetLastError());

				if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)
				{
					CC_A_INFO("DisconnectNamePipe failed with error = %x",GetLastError());
					return;
				}
				{
					CC_A_INFO("DisconnectNamedPipe,wait another client!");
				}

				if (ConnectNamedPipe(PipeHandles[Pipe],&Ovlap[Pipe]) == 0)
				{
					if (GetLastError() != ERROR_IO_PENDING)
					{
						CC_A_INFO("ConnectNamedPipe failed,error = %x",GetLastError());
						CloseHandle(PipeHandles[Pipe]);
					}
				}

				DataRead[Pipe] = FALSE;

			}

			else
			{
				CC_A_INFO("GetOverlappedResult success! BytesTransferred = %d",BytesTransferred);

				if (DataRead[Pipe] == FALSE)
				{
					ZeroMemory(&Ovlap[i],sizeof(OVERLAPPED));
					Ovlap[i].hEvent = Event[i];

					//这里不会阻塞,而是会以异步方式继续工作。BUFFER_SIZE为要读的长度。
					//问题:我客户端只写了14个字节,而这儿要读BUFFER_SIZE长度,那为什么这个也会返回呢?难道是按实际读取的长度算?
					if (ReadFile(PipeHandles[Pipe],Buffer[Pipe],BUFFER_SIZE,NULL,&Ovlap[Pipe]) == 0)
					{
						if (GetLastError() != ERROR_IO_PENDING)
						{
							CC_A_INFO("ReadFile failed,error = %x",GetLastError());
						}
					}
					else
					{
						CC_A_INFO("ReadFile return");
					}

					DataRead[Pipe] = TRUE;

				}
				else
				{
					CC_A_INFO("Received %d bytes, echo bytes back", BytesTransferred);

					ZeroMemory(&Ovlap[Pipe],sizeof(OVERLAPPED));
					Ovlap[Pipe].hEvent = Event[Pipe]; //这里会不会将刚刚读成功的信号,给覆盖了???

					//问题1、如果使劲写,客户端不读,会怎么样?
					//问题2、WriteFile完成后,GetOverlappedResult会不会设置有信号?—— 也会的!!!
					//问题3、同时读和写,如果返回有信号了,我知道是读成功了,还是写成功了啊???不知道为啥啊?

					if(WriteFile(PipeHandles[Pipe],"This is a haha66", 16, NULL, &Ovlap[Pipe]) == 0)
					{
						if (GetLastError() != ERROR_IO_PENDING)
						{
							CC_A_INFO("WriteFile failed,error = %x",GetLastError());
						}
					}

					DataRead[Pipe] = FALSE;

				}

			}
		}
	
	system("pause");
}

客户端:

#include 
#include 
#include "CC_Debug.h"

void main(void)
{
	HANDLE PipeHandle;
	DWORD BytesWritten = 0;
	DWORD BytesRead;
	CHAR Buffer[256];

	//等待管道实例有效,如果在超时值变为零以前,有一个管道可以使用,则 WaitNamedPipe 将返回 True,
	//如果指定的命名管道没有实例存在,即没有服务端创建该命名管道,函数无视超时等待时间直接返回0

	if (WaitNamedPipe("\\\\.\\Pipe\\mypipe", NMPWAIT_WAIT_FOREVER) == 0)
	{
		CC_A_INFO("CreateNamedPipe failed with error %x \n",GetLastError());
		return;
	}

	if ((PipeHandle = CreateFile("\\\\.\\Pipe\\mypipe",GENERIC_READ | GENERIC_WRITE, 0 ,(LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 
		FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL)) == INVALID_HANDLE_VALUE)
	{
		CC_A_INFO("CreateFile failed with error %x \n",GetLastError());
		return;
	}

	if(WriteFile(PipeHandle,"This is a test", 14, &BytesWritten, NULL) == 0)
	{
		CC_A_INFO("WriteFile failed with error %x \n",GetLastError());
		CloseHandle(PipeHandle);
		return;
	}

	CC_A_INFO("Wrote %d bytes \n", BytesWritten);
	
	if (ReadFile(PipeHandle,Buffer,sizeof(Buffer),&BytesRead,NULL) == 0)  
	{  
		CC_A_INFO("Echo %d bytes to Client",BytesRead);  
	} 
	else
	{
        CC_A_INFO("Client Read %d bytes from server",BytesRead);  	
	}
	
	CloseHandle(PipeHandle);
	system("pause");

}

问题:IO不是按照先进先出的顺序处理的,所以收到事件通知后,不知道到底是哪个IO处理完成了。



你可能感兴趣的:(网络编程,windows编程)