重叠模型的基本设计原理是让应用程序使用一个重叠的数据结构,一次投递一个或多个Winsock I/O请求。针对那些提交的请求,在它们完成之后,应用程序为他们提供服务。
要想在一个套接字上使用重叠I/O模型,首先必须使用WSA_FLAG_OVERLAPPED这个标志,创建一个套接字。如下所示:
s=WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED);
创建套接字的时候,假如使用的是socket函数,而非WSASocket函数,那么会默认设置WSA_FLAG_OVERLAPPED标志。成功创建好一个套接字,同时将其与一个本地接口绑定在一起后,便可以开始进行重叠I/O操作,方法是调用下属的Winsock函数,同时指定一个WSAOVERLAPPED结构(可选):
以上的每个函数都与一个套接字上的数据的发送、数据接收以及连接的接收有关。因此这些活动可能会花费极少的时间才能完成。这正是每个函数都可接收一个WSAOVERLAPPED结构作为参数的原因。若随一个WSAOVERLAPPED结构一起调用这些函数,函数会立即完成并返回,无论套接字是否设定为锁定模式。它们依赖于WSAOVERLAPPED结构来返回一个I/O请求的返回。主要有两个方法可用来管理一个重叠I/O请求的完成:我们的应用程序可以等待”事件对象通知“,亦可通过”完成例程“,对已经完成的请求加以处理。上面列出的函数(AcceptEx除外)还有另一个常用参数:lpCompletionROUTINE。该参数指定的是一个可选的指针,指向一个完成例程函数。在重叠请求完成后调用。
1.事件对象同时
重叠I/O的事件通知方法要求将Win32的事件对象与WSAOVERLAPPED结构关联在一起,若使用一个WSAOVERLAPPED结构,发出像WSASend和WSARecv这样的I/O调用,他们会立即返回。
通常大家会发现I/O调用会以失败告终,返回SOCKET_ERROR。使用WSAGetLastError函数,便可获得与错误状态有关的一个报告,这个错误状态意味着I/O操作正在进行。稍后的某个时间我们的应用程序需要等候与WSAOVERLAPPED结构对应的事件对象,了解一个重叠I/O请示何时完成。WSAOVERLAPPED结构在一个重叠I/O请求的初始化,及后续完成之间,提供了一种沟通或通讯机制。
要将一个WSAOVERLAPPED结构与一个事件对象联系起来,可以用WSACreateEvent函数来创建一个事件对象句柄。一旦创建好一个事件对象句柄,简单地将重叠结构的hEvent字段分配给事件句柄,再使用重叠结构,调用一个Winsock函数即可,比如WSASend或WSARecv。
一个重叠I/O请求最终完成后,我们的应用程序要负责取回重叠I/O操作的结果。一个重叠请求操作最终完成之后,在事件通知方法中,Winsock会更改与一个WSAOVERLAPPED结构对应的一个事件对象的事件传信状态,将其从”未传信“变为”已传信“。由于一个事件对象已分配给WSAOVERLAPPED结构,所以只需简单地调用WSAWaitForMultipleEvents函数,从而判断出一个重叠I/O请求在什么时候完成。此处需要注意的是WSAWaitForMultipleEvents函数一次最多只能等待64个事件对象。发现一次重叠请求完成后,接着需要调用WSAGetIoverlappedResult(取得重叠结构)函数,判断哪个重叠调用到底是成功还是失败。该函数的定义如下:
BOOL WSAGetOverlappedResult(
Socket s,
LPWSAOVERLAPPED lpOverlapped,
LPWORD lpcbTransfer,
BOOL fWait,
LPWORD lpdwFlags
);
其中:
Socket s:指定在重叠操作开始的时候,与之对应的那个套接字。
LPWSAOVERLAPPED lpOverlapped:是一个指针,对应于在重叠操作开始时指定的那个WSAOVERLAPPED结构。
LPWORD lpcbTransfer:参数是一个指针对应一个DWORD变量指定发送或接收的实际字节数。
由此我们总结Winsock重叠模型的编程步骤如下: