Winsock重叠模型

重叠模型的基本设计原理是让应用程序使用一个重叠的数据结构,一次投递一个或多个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结构(可选):

  • WSASend
  • WSASendTo
  • WSARecv
  • WSARecvFrom
  • WSAIoctl
  • AcceptEx
  • TransmitFile

以上的每个函数都与一个套接字上的数据的发送、数据接收以及连接的接收有关。因此这些活动可能会花费极少的时间才能完成。这正是每个函数都可接收一个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重叠模型的编程步骤如下:

  1. 创建一个套接字,开始在指定的端口上监听连接请求
  2. 接收一个进入连接的请求
  3. 为接收的套接字新建一个WSAOVERLAPPED结构,并为该结构分配一个事件对象句柄。也将事件对象句柄分配给一个事件数组,以便稍后由WSAWaitForMultipleEvents函数使用。
  4. 在套接字上投递一个异步的WSARecv请求,指定参数为WSAOVERLAPPED机构。(注意 函数通常会以失败告终,返回SOCKET_ERROR错误状态WSA_IO_PENDING,即I/O操作尚未完成)。
  5. 使用步骤3的事件数组调用WSAWaitForMultipleEvents函数,并等待与重叠调用关联在一起的事件进入“已传信”状态(换言之,等待那个事件“触发”)。
  6. WSAWaitForMultipleEvents函数完成后,针对事件数组,调用WSAResetEvent(重设事件)函数,从而重设事件对象,并对完成的重叠请求进行处理。
  7. 使用WSAGetOverlappedResult函数,判断重叠调用返回状态是什么。
  8. 在套接字上投递另一个WSARecv请求。
  9. 重复步骤5-8。

你可能感兴趣的:(Socket,C++)