管道异步通信
管道异步通信,我们通过在 CreateFile 和 CreateNamedPipe 中添加 FILE_FLAG_OVERLAPPED 来实现管道的异步通信也叫重叠I/O。
重叠I/o的使用:当你调用调用ReadFile和WriteFile时,结果立马返回,这样就可以去执行其他的代码,同时可以使用WaitForSingleObject和GetOverlappedResult来获取重叠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
重写ReadFile和WriteFile函数,使其和WaitForSingleObject 、GetOverlappedResult一起使用,用来延时和等候。
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;
}