管道异步通信

c++

Posted by YiMiTuMi on August 4, 2021

管道异步通信

管道异步通信,我们通过在 CreateFileCreateNamedPipe 中添加 FILE_FLAG_OVERLAPPED 来实现管道的异步通信也叫重叠I/O

重叠I/o的使用:当你调用调用ReadFileWriteFile时,结果立马返回,这样就可以去执行其他的代码,同时可以使用WaitForSingleObjectGetOverlappedResult来获取重叠I\O的结果,如果GetOverlappedResult的返回值为TRUE,则说明重叠操作成功。

操作流程一般是:先调用WaitForSingleObject函数来等待某一个I\O请求绑定的事件对象的激活。等到激活事件对象后,再调用GetOverlappedResult函数取得重叠I\O的结果,这里GetOverlappedResult的返回值一般都是TRUE(即重叠操作成功)。

当cpu执行你的代码时遇上一个i/o请求后,系统这是为你开一根内部线程去处理i/o请求,并且你的线程并不挂起,但你可能会觉得如果i/o还没完成,后续的代码就算他让我执行,我也执行不下去了,如果下面的代码和这个i/o操作有关的话,那么它就要等一等,等到这个i/o操作完成,通过在一个线程中调用 WaitForSingleObject就可以得到i/o完成的消息,然后再对其作相应的处理。 但如果后续的代码和这个i/o操作无关,你就可以以更快的速度之行下去了,而无需等待io请求的完成了这也就是异步了。

也可以使用 WaitForSingleObject 去设置延时等待。

Readfile 和 WriteFile

重写ReadFileWriteFile函数,使其和WaitForSingleObjectGetOverlappedResult一起使用,用来延时和等候。

Readfile:

BOOL RewriteReadFile(__in HANDLE hFile, __out_bcount_part_opt(nNumberOfBytesToRead, *lpNumberOfBytesRead) __out_data_source(FILE) LPVOID lpBuffer, __in DWORD nNumberOfBytesToRead, __out_opt  LPDWORD lpNumberOfBytesRead, __inout_opt LPOVERLAPPED lpOverlapped, DWORD iDelayeds)
{
	BOOL bRead = TRUE;
	bRead = ::ReadFile(hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped);

	if (GetLastError() == ERROR_IO_PENDING)
	{
		WaitForSingleObject(hFile, iDelayeds);
		bRead = GetOverlappedResult(hFile, lpOverlapped, &nNumberOfBytesToRead, TRUE);
	}

	return bRead;
}

WriteFile:

BOOL RewriteWriteFile(HANDLE hFile, __in_bcount_opt(nNumberOfBytesToWrite)LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped, DWORD iDelayeds)
{
	BOOL bWrite = TRUE;

	BOOL ret = ::WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped);

	if (GetLastError() == ERROR_IO_PENDING)
	{
		WaitForSingleObject(hFile, iDelayeds);
		bWrite = GetOverlappedResult(hFile, lpOverlapped, &nNumberOfBytesToWrite, TRUE);
	}

	return bWrite;
}

这两个函数,最后都接受一个超时时间,一般情况下WaitForSingleObject的第二个参数为INFINITE的话,除非出现什么其他I\O错误,要不然等wait到事件对象后,I\O操作都是已经成功完成了的。那么再调用GetOverlappedResult函数(设置最后一个参数为FLASE)就会直接返回TRUE了。

如果把WaitForSingleObject的第二个参数为某个超时时间值时,那么GetOverlappedResult函数的最后一个blwait参数最好设置为TRUE,这样的话才能保证GetOverlappedResult返回TRUE,要不然就会返回FALSE,而此时的GetLastError返回值就是ERROR_IO_INCOMPLETE了,代表I\O操作还未完成。

Client和Server实现

此时服务器在线程中一直循环等待连接,Cliet为一个进程(.exe),当Client启动时去连接服务器。连接成功后,先向服务器发送一个标志为,表示可以接受服务器返回的数据,服务器收到后,将相关消息发送到Client,Client收到后,向服务器发送一个断开连接的标志位,服务器收到后断开连接后,继续等待下次连接。(如果不断开,下次Cliet启动时连不上服务器。)

Server:

unsigned int __stdcall SendExEServerInfo(void* pParam)   //线程
{
	CClient * pClient = CClient::instance();
	
	CString strPipeName = L"\\\\.\\pipe\\ServerInfo";
	HANDLE h_pipe = NULL;

	//char buf[256] = "";
	//DWORD rlen = 0;
	h_pipe = CreateNamedPipe(
		strPipeName.GetString(),                                  //管道名
		PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,                 //管道类型,重叠I/O
		PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE|PIPE_WAIT,      //管道参数,PIPE_WAIT
		PIPE_UNLIMITED_INSTANCES,                               //管道能创建的最大实例数量
		0,                                                      //输出缓冲区长度 0表示默认
		0,                                                     //输入缓冲区长度 0表示默认
		NMPWAIT_WAIT_FOREVER,                                   //超时时间
		NULL);         

	if (h_pipe == INVALID_HANDLE_VALUE)
	{
		OutputDebugStringA("------CreateNamedPipe--------");
		return 0;
	}
	
	CLIENT_SERVER_INFO ClientServerInfo;                       //客户端要收到消息的结构体
	memset(&ClientServerInfo, 0, sizeof(ClientServerInfo));    //清空
	ClientServerInfo.iFage = 3;

	HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);         //定义消息
	OVERLAPPED overlapped;                                     //设定 OVERLAPPED 结构体
	memset(&overlapped, 0, sizeof(OVERLAPPED));               
	overlapped.hEvent = hEvent;

	DWORD dwInfoSize = 0;
	BOOL bDelayed = FALSE;

	while (TRUE)
	{
		if (!ConnectNamedPipe(h_pipe, NULL))                    //等待客户端连接
		{
			OutputDebugStringA("----------ConnectNamedPipe----");
			continue;
		}
		
		while (TRUE)
		{
			DWORD dwSendClientServer = 0;                       //接受客户端发送的标志位

			BOOL ret = RewriteReadFile(h_pipe, &dwSendClientServer, sizeof(dwSendClientServer), 0, &overlapped, 3000);

			if (!ret)
			{
				//失败
				DisconnectNamedPipe(h_pipe);                    //断开连接
				break;
			}

			if (dwSendClientServer == RETURN_DATA_SERVER_INFO_FLAG)   //收到标志位,向客户端发送数据
			{
				cout<< "----------2----------" << endl;
				
				bDelayed = RewriteWriteFile(h_pipe, &ClientServerInfo, sizeof(ClientServerInfo), 0, &overlapped, 3000);

				if (bDelayed)
				{
					continue;                                       //等待断开连接
				}
				else
				{
					DisconnectNamedPipe(h_pipe);                    //断开连接
					break;
				}
			}
			else if (dwSendClientServer == RETURN_DATA_SERVER_INFO_FLAG_END) //收到客户端断开连接的标志位
			{
				DisconnectNamedPipe(h_pipe);                 //断开连接
				break;
			}

		}
	}
	
	CloseHandle(h_pipe);//关闭管道

	return 0;
}

Client:

unsigned int __stdcall USSEServerInfo(void* pParam)    //线程
{
		
	cout << "========Thread===========" << endl;

	Dowrd _iDelayed = 3000; //延时

	wstring strPipeName = L"\\\\.\\pipe\\ServerInfo";
	HANDLE h_pipe = NULL;

	if (!WaitNamedPipeW(strPipeName.c_str(), _iDelayed)) 
	{
		cout<< "----------0----------" << endl;
		return 0;
	}

	h_pipe = ::CreateFileW(strPipeName.c_str(), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED , NULL);
	if (h_pipe == INVALID_HANDLE_VALUE)
	{
		cout << "========Thread====end=======" << endl;
		return 0;
	}

	CLIENT_SERVER_INFO ClientServerInfo;
	memset(&ClientServerInfo, 0, sizeof(ClientServerInfo));

	DWORD dwSendClientServer = RETURN_DATA_SERVER_INFO_FLAG;

	HANDLE hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	OVERLAPPED overlapped;
	memset(&overlapped, 0, sizeof(OVERLAPPED));
	overlapped.hEvent = hEvent;

	BOOL bDelayed = FALSE;
	DWORD dwInfoSize = 0;
	
	//向客户端发送接受信息标志位
	bDelayed = RewriteWriteFile(h_pipe, &dwSendClientServer, sizeof(dwSendClientServer), 0, &overlapped, _iDelayed);   

	while (bDelayed)     //循环等待接受
	{
		cout<< "----------3----------" << endl;

		BOOL bRead = TRUE;

		bRead = RewriteReadFile(h_pipe, &ClientServerInfo, sizeof(ClientServerInfo), 0, &overlapped, _iDelayed);

		if (!bRead)
		{
			//链接失败
			cout<< "----------5----------" << endl;
			break;
		}
		else
		{

			//收到客户端消息				

			BOOL bDelayedWr = FALSE;

			dwSendClientServer = RETURN_DATA_SERVER_INFO_FLAG_END;
			
			bDelayedWr = RewriteWriteFile(h_pipe, &dwSendClientServer, sizeof(dwSendClientServer), 0, &overlapped, _iDelayed);  //向客户端发送结束连接标志位

			if (bDelayedWr)
			{
				cout<< "----------8----------" << endl;
			}

			//读取出值
			cout << ClientServerInfo.iFage << endl;  //读取客户端值,并结束
			break;
		}

	}

	CloseHandle(h_pipe);

	return 0;
}

蓝色妖姬 – 相守