基于事件通知的重叠I/O网络模型
来源:互联网 发布:c 高级编程 第7版 pdf 编辑:程序博客网 时间:2024/05/22 03:08
这里就补充一个API就足够了,其余的在基于完成例程的重叠IO网络模型中有详尽的解释,这里且就不一一赘述
重要API
WSAGetOverlappedResult
BOOL 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_port)); 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网络模型
- 事件通知方式实现的重叠I/O模型
- 基于完成例程的重叠I/O网络模型
- 重叠I/O-事件通知
- 重叠I/O-事件通知
- WinSock IO模型四: 重叠I/O (事件通知)
- Socket I/O模型之重叠I/O(overlapped I/O)--事件通知
- 用事件通知方式实现的重叠I/O模型
- Windows Socket I/O模型之 重叠I/O事件通知模式
- 重叠I/O之事件对象通知
- 重叠I/O之事件对象通知
- Overlapped重叠I/O之事件通知
- 重叠I/O之事件通知
- socket 五种模型理解之三---------重叠I/O模型(1.事件通知形式)
- 重叠I/O模型
- 重叠I/O模型
- 重叠I/O模型
- 重叠I/O模型
- 【java集合】自己实现简易的HashMap~改良
- 今日内容介绍 1、自定义类型的定义及使用 2、自定义类的内存图 3、ArrayList集合的基本功能 4、随机点名器案例及库存案例代码优化 ###01引用数据类型_类 * A: 数据类型
- day07笔记
- Spring 整合 Quartz 实现动态定时任务
- LVS负载均衡服务的工作原理及方式
- 基于事件通知的重叠I/O网络模型
- matlab如何写一个类
- 编译caffe出现错误 opencv
- bzoj 4810 [Ynoi2017]由乃的玉米田(莫队+bitset)
- 利用异或进行密码加密
- hibernate.cfg.xml
- day08笔记
- 高精度(加法)
- js进行图片相关操作