1.基于事件通知模型的Overlapped I/O(重叠IO模型)
概括一点说,重叠模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。针对这些提交的请求,在它们完成之后,应用程序会收到通知,于是就可以通过自己另外的代码来处理这些数据了。
需要注意的是,有两个方法可以用来管理重叠IO请求的完成情况(就是说接到重叠操作完成的通知):
- 事件对象通知(event object notification)
- 完成例程(completion routines) ,注意,这里并不是完成端口
这一篇实现基于事件对象通知的重叠I/O模型。
既然是基于事件通知,就要求将Windows事件对象与WSAOVERLAPPED结构关联在一起(WSAOVERLAPPED结构中专门有对应的参数),发送接收数据的函数参数中都有一个Overlapped参数,我们可以假设是把我们的WSARecv这样的操作操作“绑定”到这个重叠结构上,提交一个请求,其他的事情就交给重叠结构去操心,而其中重叠结构又要与Windows的事件对象“绑定”在一起,这样我们调用完WSARecv以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要德数据了。
socket连接,WSAOVERLAPPED重叠IO结构,事件对象之间的关系如下图:
可以发现每建立一个socket连接时需要至少创建一个WSAOVERLAPPED结构和它关联,而每个WSAOVERLAPPED需要关联一个WSAEVENT类型的事件对象。所以为了实现多个客户端和服务器端通信,我们也需要像上一篇select模型一样建立一个类来管理多个socket和它们对应的WSAOVERLAPPED结构,WSAEVENT结构。
2.基本的函数和数据结构
2.1. WSAOVERLAPPED结构
这个结构自然是重叠模型里的核心,它是这么定义的
typedef struct _WSAOVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent; // 唯一需要关注的参数,用来关联WSAEvent对象
} WSAOVERLAPPED, *LPWSAOVERLAPPED;
我们需要把WSARecv等操作投递到一个重叠结构上,而我们又需要一个与重叠结构“绑定”在一起的事件对象来通知我们操作的完成,看到了和hEvent参数,不用我说你们也该知道如何来来把事件对象绑定到重叠结构上吧?大致如下:
WSAEVENT event; // 定义事件
WSAOVERLAPPED AcceptOverlapped ; // 定义重叠结构
event = WSACreateEvent(); // 建立一个事件对象句柄
ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED)); // 初始化重叠结构
AcceptOverlapped.hEvent = event;
2.2.WSARecv系列函数
int WSARecv(
SOCKET s, // 当然是投递这个操作的套接字
LPWSABUF lpBuffers, // 接收缓冲区,与Recv函数不同,这里需要一个由WSABUF结构构成的数组
DWORD dwBufferCount, // 数组中WSABUF结构的数量
LPDWORD lpNumberOfBytesRecvd, // 如果接收操作立即完成,这里会返回函数调用所接收到的字节数
LPDWORD lpFlags, // 设置为0即可
LPWSAOVERLAPPED lpOverlapped, // “绑定”的重叠结构
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // 完成例程中将会用到的参数,我们这里设置为