基于事件通知的重叠I/O网络模型
2017-11-03 20:20
423 查看
这里就补充一个API就足够了,其余的在基于完成例程的重叠IO网络模型中有详尽的解释,这里且就不一一赘述
s [in]
标识套接字的描述符。
lpOverlapped [in]
指向重叠操作开始时指定的WSAOVERLAPPED结构的指针。这个参数不能是一个NULL指针。
lpcbTransfer [out]
指向32位变量的指针,该变量存储经过接收操作、发送操作或WSAIoctl函数实际传输的字节数。这个参数不能是一个NULL指针。
fWait [in]
一个标志,指定函数是否应等待挂起的重叠操作完成。如果为TRUE,则在操作完成之前,该功能不会返回。如果FALSE且该操作仍处于挂起状态,则函数返回FALSE,WSAGetLastError函数返回WSA_IO_INCOMPLETE。仅当选择为基于事件通知的重叠操作时,fWait参数才可以设置为TRUE。
lpdwFlags [out]
指向一个32位变量的指针,它将接收一个或多个补充完成状态的标志。如果重叠操作是通过WSARecv或WSARecvFrom启动的,则此参数将包含lpFlags参数的结果值。这个参数不能是一个NULL指针。
功能
该函数返回指定套接字上重叠操作的结果。
返回值
如果WSAGetOverlappedResult成功,则返回值为TRUE。 这意味着重叠的操作已经成功完成,并且lpcbTransfer指向的值已被更新。
如果WSAGetOverlappedResult返回FALSE,则表示重叠操作未完成,重叠操作完成但出错,或者由于WSAGetOverlappedResult的一个或多个参数错误,无法确定重叠操作的完成状态。 失败时,lpcbTransfer指向的值不会被更新。 使用WSAGetLastError来确定失败的原因(通过WSAGetOverlappedResult函数或关联的重叠操作)。
错误码及含意
WSANOTINITIALISED:在使用这个函数之前,一个成功的WSAStartup调用必须发生。
WSAENETDOWN:网络子系统失败。
WSA_INVALID_HANDLE:WSAOVERLAPPED结构的hEvent参数不包含有效的事件对象句柄。
WSA_INVALID_PARAMETER:其中一个参数是不可接受的。
WSA_IO_INCOMPLETE:fWait参数为FALSE时产生该错误,表示I / O操作尚未完成。
WSAEFAULT:一个或多个lpOverlapped,lpcbTransfer或lpdwFlags参数不在用户地址空间的有效部分中。 如果lpOverlapped,lpcbTransfer或lpdwFlags参数在Windows Server 2003和更早版本上是空指针,则会返回此错误。
2.源码
OverlapIOModel.h
InitNet
初始化网络的一切信息,包括初始化网络环境,绑定,监听,并调用_BeginAccept开始接受客户端的连接
_BeginAccept
接受到客户端的连接后,将客户端的连接信息保存到结构体PER_IO_OPERATION_DATA全局数组变量g_pPerIODataArr中保存好信息,然后开始投递WSARecv异步操作,告诉操作系统帮我们接受该套接字的消息。
ServiceProc
WSAWaitForMultipleEvents开始监听我们在接受套接字时绑定的网络事件对象是否有消息到来,如果客户端发来了数据并且事件对象有信号,此时必定是内核已经将数据拷贝到了我们WSABUF的缓冲区中(也就是PER_IO_OPERATION_DATA中的szMessage中),不必我们自己再去内核拷贝数据到用户空间中来。
接下来,我们对用户进行一些判断是否断线后,开始进行对数据进行处理,这里的处理是我们的处理函数指针NetFunc,函数指针的定义在头文件中有。
处理完数据后,我们再投递一次WSARecv。这样我们就完成了一次数据的收
WSASendToClient
对数据进行处理之后,我们再通过WSASendToClient将数据处理之后结果发送给客户端,投递一个WSASend,让操作系统帮我们发送数据,同样是不占用自己程序的时间片,让用户程序做更多事情。
基于事件通知的重叠IO网络模型源码:http://pan.baidu.com/s/1o87OoHk
重要API
WSAGetOverlappedResultBOOL WSAAPI WSAGetOverlappedResult( _In_ SOCKET s, _In_ LPWSAOVERLAPPED lpOverlapped, _Out_ LPDWORD lpcbTransfer, _In_ BOOL fWait, _Out_ LPDWORD lpdwFlags );
s [in]
标识套接字的描述符。
lpOverlapped [in]
指向重叠操作开始时指定的WSAOVERLAPPED结构的指针。这个参数不能是一个NULL指针。
lpcbTransfer [out]
指向32位变量的指针,该变量存储经过接收操作、发送操作或WSAIoctl函数实际传输的字节数。这个参数不能是一个NULL指针。
fWait [in]
一个标志,指定函数是否应等待挂起的重叠操作完成。如果为TRUE,则在操作完成之前,该功能不会返回。如果FALSE且该操作仍处于挂起状态,则函数返回FALSE,WSAGetLastError函数返回WSA_IO_INCOMPLETE。仅当选择为基于事件通知的重叠操作时,fWait参数才可以设置为TRUE。
lpdwFlags [out]
指向一个32位变量的指针,它将接收一个或多个补充完成状态的标志。如果重叠操作是通过WSARecv或WSARecvFrom启动的,则此参数将包含lpFlags参数的结果值。这个参数不能是一个NULL指针。
功能
该函数返回指定套接字上重叠操作的结果。
返回值
如果WSAGetOverlappedResult成功,则返回值为TRUE。 这意味着重叠的操作已经成功完成,并且lpcbTransfer指向的值已被更新。
如果WSAGetOverlappedResult返回FALSE,则表示重叠操作未完成,重叠操作完成但出错,或者由于WSAGetOverlappedResult的一个或多个参数错误,无法确定重叠操作的完成状态。 失败时,lpcbTransfer指向的值不会被更新。 使用WSAGetLastError来确定失败的原因(通过WSAGetOverlappedResult函数或关联的重叠操作)。
错误码及含意
WSANOTINITIALISED:在使用这个函数之前,一个成功的WSAStartup调用必须发生。
WSAENETDOWN:网络子系统失败。
WSA_INVALID_HANDLE:WSAOVERLAPPED结构的hEvent参数不包含有效的事件对象句柄。
WSA_INVALID_PARAMETER:其中一个参数是不可接受的。
WSA_IO_INCOMPLETE:fWait参数为FALSE时产生该错误,表示I / O操作尚未完成。
WSAEFAULT:一个或多个lpOverlapped,lpcbTransfer或lpdwFlags参数不在用户地址空间的有效部分中。 如果lpOverlapped,lpcbTransfer或lpdwFlags参数在Windows Server 2003和更早版本上是空指针,则会返回此错误。
源码分析
1.实现流程图2.源码
OverlapIOModel.h
#pragma once #include "Socket.h" #define MSGSIZE 1024 /************************************************************************/ /* 注意,此处的 WSAOVERLAPPED 不一定要在首部,区分和完成例程的区别(完成例程中有解释) /************************************************************************/ typedef struct { WSAOVERLAPPED overlap; // 这里保存的一个事件对象...缓冲区的位置 WSABUF wsaBuf; // 指明缓冲区的成员 char szMessage[MSGSIZE]; // 真正的缓冲区 DWORD NumberOfBytesRecvd; // 接收到的字节 DWORD Flags; // 是否成功接收 SOCKADDR_IN addr; // 客户端信息 SOCKET sock; // 套接字 }PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA; typedef void(*NetCallBack) (DWORD id, void* param, int len); // 定义消息处理函数指针 /************************************************************************/ /* 基于事件通知的重叠IO模型类 /* Socket为自己封装的socket类 /************************************************************************/ class OverlapIOModel : public Socket { private: int g_iTotalConn; // 总的连接数 WSAEVENT g_CliEventArr[MAXIMUM_WAIT_OBJECTS]; // 事件对象数组 LPPER_IO_OPERATION_DATA g_pPerIODataArr[MAXIMUM_WAIT_OBJECTS]; // 重叠结构数组 protected: // 消息处理函数 NetCallBack NetFunc; //begin accept void _BeginAccept(); //clean client socket void Cleanup(int index); //service proc static DWORD WINAPI ServiceProc(LPARAM lparam); public: OverlapIOModel(); ~OverlapIOModel(); //init net BOOL InitNet(NetCallBack func, UINT nSocketPOrt, LPCSTR lpszSocketAddr = ADDR_ANY); //wsa send to client bool WSASendToClient(DWORD id, void* lparam); };
InitNet
BOOL OverlapIOModel::InitNet(NetCallBack func, UINT nSocketPOrt, LPCSTR lpszSocketAddr /*= ADDR_ANY*/) { NetFunc = func; if (!Create(nSocketPOrt, SOCK_STREAM, lpszSocketAddr)) return false; if (!Listen(5)) return false; CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ServiceProc, this, NULL, NULL); _BeginAccept(); return true; }
初始化网络的一切信息,包括初始化网络环境,绑定,监听,并调用_BeginAccept开始接受客户端的连接
_BeginAccept
void OverlapIOModel::_BeginAccept() { SOCKET sClient = 0; SOCKADDR_IN addrClient = { 0 }; int nLen = sizeof(SOCKADDR_IN); int err = 0; while (true) { sClient = accept(m_hSocket, (sockaddr*)&addrClient, &nLen); if (sClient == INVALID_SOCKET) continue; printf("[%s:%d]->log on\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_por e501 t)); g_pPerIODataArr[g_iTotalConn] = (LPPER_IO_OPERATION_DATA)HeapAlloc(// 在程序的默认堆上申请内存 GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA)); g_pPerIODataArr[g_iTotalConn]->sock = sClient; g_pPerIODataArr[g_iTotalConn]->addr = addrClient; g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd = 0; g_pPerIODataArr[g_iTotalConn]->wsaBuf.len = MSGSIZE; g_pPerIODataArr[g_iTotalConn]->wsaBuf.buf = g_pPerIODataArr[g_iTotalConn]->szMessage; //create net event g_CliEventArr[g_iTotalConn] = g_pPerIODataArr[g_iTotalConn]->overlap.hEvent = WSACreateEvent(); //begin wsa recv(only can recv one time) if (SOCKET_ERROR == WSARecv( g_pPerIODataArr[g_iTotalConn]->sock, &(g_pPerIODataArr[g_iTotalConn]->wsaBuf), 1, &(g_pPerIODataArr[g_iTotalConn]->NumberOfBytesRecvd), &(g_pPerIODataArr[g_iTotalConn]->Flags), &(g_pPerIODataArr[g_iTotalConn]->overlap), NULL)) { err = WSAGetLastError(); if (WSA_IO_PENDING != err) // WSA_IO_PENDING 重叠IO操作成功,等待稍后完成 { HeapFree(GetProcessHeap(), 0, g_pPerIODataArr[g_iTotalConn]); printf("WSARecv failed:%d\n", err); continue; }; } g_iTotalConn++; } }
接受到客户端的连接后,将客户端的连接信息保存到结构体PER_IO_OPERATION_DATA全局数组变量g_pPerIODataArr中保存好信息,然后开始投递WSARecv异步操作,告诉操作系统帮我们接受该套接字的消息。
ServiceProc
/************************************************************************/ /* 基于事件通知的重叠IO网络模型的主要实现(工作线程) */ /************************************************************************/ DWORD WINAPI OverlapIOModel::ServiceProc(LPARAM lparam) { OverlapIOModel* pOverLapIO = (OverlapIOModel*)lparam; int ret = 0; int index = 0; DWORD err = 0; while (TRUE) { ////////////////////////////////////////////////////////////////////////// // 等待多个事件对象有信号发送 ret = WSAWaitForMultipleEvents(pOverLapIO->g_iTotalConn, pOverLapIO->g_CliEventArr, FALSE, 1000, FALSE); if (ret == WSA_WAIT_FAILED || ret == WSA_WAIT_TIMEOUT) continue; index = ret - WSA_WAIT_EVENT_0; // 获取索引值,此处 WSA_WAIT_EVENT_0 其实就等于0... ////////////////////////////////////////////////////////////////////////// // 将事件对象设为无信号状态 if (FALSE == WSAResetEvent(pOverLapIO->g_CliEventArr[index])) { pOverLapIO->Cleanup(index); continue; } ////////////////////////////////////////////////////////////////////////// // 获取重叠操作的结果,如果返回FALSE,表示重叠操作未完成 if (FALSE == WSAGetOverlappedResult( pOverLapIO->g_pPerIODataArr[index]->sock, &(pOverLapIO->g_pPerIODataArr[index]->overlap), &(pOverLapIO->g_pPerIODataArr[index]->NumberOfBytesRecvd), TRUE, &(pOverLapIO->g_pPerIODataArr[index]->Flags))) { printf("WSAGetOverlappedResult failed:%d\n", WSAGetLastError()); continue; } ////////////////////////////////////////////////////////////////////////// // 接收到的重叠结果的字节数为0是,表示客户端断开连接 if (pOverLapIO->g_pPerIODataArr[index]->NumberOfBytesRecvd == 0) { printf("[%s:%d]->log off\n", inet_ntoa(pOverLapIO->g_pPerIODataArr[index]->addr.sin_addr), ntohs(pOverLapIO->g_pPerIODataArr[index]->addr.sin_port)); pOverLapIO->Cleanup(index); } else { ////////////////////////////////////////////////////////////////////////// // 有数据接收到,对数据进行处理(NetFunc),并再投递一个异步请求(WSARecv) // 因为 WSARecv 只有一次生效的机会,如果我们在一次操作完之后,还有接下来的操作就还要继续投递一个WSARecv pOverLapIO->NetFunc(pOverLapIO->g_pPerIODataArr[index]->sock, pOverLapIO->g_pPerIODataArr[index]->szMessage, pOverLapIO->g_pPerIODataArr[index]->NumberOfBytesRecvd); ZeroMemory(pOverLapIO->g_pPerIODataArr[index]->szMessage, sizeof(pOverLapIO->g_pPerIODataArr[index]->szMessage)); pOverLapIO->g_pPerIODataArr[index]->Flags = 0; if (SOCKET_ERROR == WSARecv( //create new WSARecv pOverLapIO->g_pPerIODataArr[index]->sock, &(pOverLapIO->g_pPerIODataArr[index]->wsaBuf), 1, &(pOverLapIO->g_pPerIODataArr[index]->NumberOfBytesRecvd), &(pOverLapIO->g_pPerIODataArr[index]->Flags), &(pOverLapIO->g_pPerIODataArr[index]->overlap), NULL)) { err = WSAGetLastError(); if (WSA_IO_PENDING != err) // WSA_IO_PENDING 重叠IO操作成功,等待稍后完成 { HeapFree(GetProcessHeap(), 0, pOverLapIO->g_pPerIODataArr[index]); printf("WSARecv failed:%d\n", err); }; } } } }
WSAWaitForMultipleEvents开始监听我们在接受套接字时绑定的网络事件对象是否有消息到来,如果客户端发来了数据并且事件对象有信号,此时必定是内核已经将数据拷贝到了我们WSABUF的缓冲区中(也就是PER_IO_OPERATION_DATA中的szMessage中),不必我们自己再去内核拷贝数据到用户空间中来。
接下来,我们对用户进行一些判断是否断线后,开始进行对数据进行处理,这里的处理是我们的处理函数指针NetFunc,函数指针的定义在头文件中有。
处理完数据后,我们再投递一次WSARecv。这样我们就完成了一次数据的收
WSASendToClient
/************************************************************************/ /* 进行消息发送的异步投递 */ /************************************************************************/ bool OverlapIOModel::WSASendToClient(DWORD id, void* lparam) { char* buf = (char*)lparam; int dwRet = 0; WSABUF wsaBuf; wsaBuf.len = strlen(buf); wsaBuf.buf = buf; DWORD dwSendBytes = 0; DWORD Flags = 0; WSAOVERLAPPED overlap; overlap.hEvent = WSACreateEvent(); // 投递一个WSASend给操作系统,让它帮助我们完成消息的发送 if (SOCKET_ERROR == WSASend(id, &wsaBuf, 1, &dwSendBytes, Flags, &overlap, NULL)) { dwRet = WSAGetLastError(); if (dwRet != WSA_IO_PENDING) { printf("WSASend failed:%d\n", dwRet); return false; } } WSACloseEvent(overlap.hEvent);// 对发送的消息没有事件的检测,所以清除掉内核资源 return true; }
对数据进行处理之后,我们再通过WSASendToClient将数据处理之后结果发送给客户端,投递一个WSASend,让操作系统帮我们发送数据,同样是不占用自己程序的时间片,让用户程序做更多事情。
基于事件通知的重叠IO网络模型源码:http://pan.baidu.com/s/1o87OoHk
相关文章推荐
- 基于完成例程的重叠I/O网络模型
- c#基于事件模型的UDP通讯框架(适用于网络包编解码)
- winsock IO 模型---重叠IO之事件通知 example code
- 用事件通知方式实现的重叠I/O模型
- Windows Socket I/O模型之 重叠I/O事件通知模式
- Socket I/O模型之重叠I/O(overlapped I/O)--事件通知
- c#基于事件模型的UDP通讯框架(适用于网络包编解码)
- 网络事件模型---重叠IO
- Socket IO重叠模型(事件通知)
- WinSock IO模型四: 重叠I/O (事件通知)
- 事件通知方式实现的重叠I/O模型
- 重叠模型--事件对象通知
- 模型设计与实践---(六)重叠IO,事件通知(Overlap Event)
- 重叠IO之事件通知模型
- Linux网络事件通知机制
- 【Java Web】——基于事件流的访问XML方式之推拉模型
- 基于android的网络音乐播放器-通知栏控制(RemoteViews)(十)
- 基于事件的开源网络库—libevent:应用介绍
- Event---Android事件处理模型一(基于回调机制的事件处理)
- 网络IO模型--事件模型