Overlapped Model
- 这个模型的基本设计思想是允许应用程序使用重叠数据结构一次投递一个或者多个异步IO请求(既所谓的重叠IO),提交的I/O请求完成之后,与之关联的重叠数据结构中的事件对象受信,应用程序便可使用WSAGetOverlappedResult函数获取重叠操作的结果
重叠I/O函数
- 为了使用重叠I/O模型,必须调用特定的重叠I/O函数创建套接字,在套接字上传输数据,这些函数有Winsock2中新添的函数,如WSASend、WSARecv等。也有一些I/O扩展函数,如AcceptEx等。
创建套接字
- 要使用重叠I/O模型,在创建套接字时必须使用WSASocket函数设置重叠标志。
SOCKET WSASocket(int af,int type,int protocol,//前三个函数与socket函数相同
LPWSAPROTOCOL_INFO lpProtocolInfo,//指定下层服务提供者,可以是NULL
GROUP g,//保留
DWORD dwFlags//指定套接字属性,要使用重叠I/O模型,必须指定WSA_FLAG_OVERLAPPED
)
- 在重叠I/O模型中,传输数据的函数是WSASend、WSARecv(TCP)、WSASendTo、WSARecvFrom等。下面是WSASend函数的定义,其他函数与之类似。
int WSASend(
SOCKET s,//套接字句柄
LPWSABUP lpBuffers,//Wsabuf结构数据,包含一个缓冲区指针和对应缓冲区的长度
DWORD dwBufferCount,//上面WSABUF数据大小
LPDWORD lpNumberOfBytesSent,//如果I/O操作立即完成,此参数取得实际传输数据的字节数
DWORD dwFlags,//标志
LPWSAOVERLAPPED lpOverlapped,//与此I/O操作关联的WSAOVERLAPPED结构
LPWSAOVERLAPPED_COMPLETION_REUTINE lpCompletionRoutine//指定一个完成例程
)
- 这些函数与Winsock1中的send、recv等函数比,都多了如下两个参数。
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_REUTINE lpCompletionRoutine
- I/O操作函数都接收一个WSAOVERLAPPED结构类型的参数,这些函数被调用之后会立即返回,他们依靠应用程序传递的WSAOVERLAPPED结构管理I/O请求的完成。应用程序有两种方法可以接收到重叠I/O请求操作完成的通知。
- 在与WSAOVERLAPPED结构关联的事件对象上等待,I/O操作完成后,此事件对象受信这是最经常使用的方法。
- 使用lpCompletionRoutine指向的完成例程,完成例程是一个自定义函数,I/O操作完成后,Winsock便于调用它,这种方法很少使用,将lpCompletionRoutine设为NULL即可。
接收连接
- 可以异步接收连接请求的函数是AcceptEx,这是一个Microsoft的扩展函数,它接受一个新的连接,返回本地和远程地址,取得客户程序发送的第一块数据,函数定义如下。
BOOL AcceptEx(
SOCKET sListenSocket,//监听套接字句柄
SOCKET sAcceptSocket,//指定一个未被使用的套接字,在这个套接字上接受新的连接
PVOID lpOutputBuffer,//指定一个缓冲区,用来取得在新连接接收到的第一块数据,服务器的本地地址和客户端地址
DWORD dwReceiveDataLength,//上面lpOutputBuffer所指缓冲区的大小
DWORD dwLocalAddressLength,//缓冲区中为本地地址留的长度,必须比最大地址长度多16
DWORD dwRemoteAddressLength,//缓冲区中为远程地址预留的长度,必须比最大地址长度多16
LPDWORD lpdwBytesReceived,//用来取得接收到数据的长度
LPOVERLAPPED lpOverlapped //指定用来处理请求的OVERLAPPED结构,不能为NULL
);//声明在Mswsock.h中,需要添加Mswsock.lib库的链接.
- Accept函数将几个套接字函数的功能集合在一起,如果它投递的请求成功完成,则执行了如下三个操作。
- 接受了新的连接
- 新连接的本地地址和远程地址都会返回
- 接收远程主机发来的第一块数据。
- AcceptEx和大家熟悉的accept函数很大的不同就是Accept函数需要调用者提供两个套接字,一个指定在哪个套接字上监听(sListenSocket参数)另一个指定在哪个套接字上接收连接(sAcceptSocket参数),也就是说AcceptEx不会像accept函数一样为新连接建立套接字。
- 如果提供了接收缓冲区,AcceptEx投递的重叠操作 直到接受连接并且读到数据之后才会返回。以SO_CONNECT_TIME为参数调用getsockopt函数可以检查是否接受了连接,如果接受了连接,这个调用还可以取得连接已经建立了多长时间。
- AcceptEx函数从Mswsock.lib库中导出的,为了能直接调用它,而不用链接到Mswsock.lib库,需要使用WSAIoctl函数将AcceptEx函数加载到内存。
- WSAIoctl函数是ioctlsocket函数的扩展,它可以使用重叠I/O,函数第三个到第六个参数是输入和输出缓冲区,在这里传递AcceptEx函数的指针,具体加载代码如下。
//加载扩展函数AccepEx
CUID GuidAcceptEx=WSAID_ACCEPTEX;
DWORD dwBytes;
WSAIoctl(pListen->s,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx,
sizeof(GuidAcceptEx),
&pListen->lpfnAcceptEx,
sizeof(pListen->lpfnAcceptEx),
&dwBytes,
NULL,
NULL
);
事件通知方式
- 为了使用重叠I/O,每个I/O函数都要接收一个WSAOVERLAPPED结构类型的参数,在Winsock2.h文件定义如下。
typedef struct _WSAOVERLAPPED{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;//在此为这个操作关联一个事件对象句柄
}WSAOVERLAPPED,*LPWSAOVERLAPPED;
- 为了使用重叠I/O,每个I/O函数都要接收一个WSAOVERLAPPED结构类型的参数,这个结构在Winsock2.h文件中定义如下。
typedef struct _WSAOVERLAPPED{
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
WSAEVENT hEvent;//再此为这个操作关联一个事件对象句柄
}WSAOVERLAPPED,*LPWSAOVERLAPPED;
- 前四个Internal、InternalHigh、Offset、OffsetHigh由系统内部使用,应用程序不应该操作或者直接使用他们。hEvent域允许应用程序为这个操作关联一个事件对象句柄,重叠I/O的事件通知方法,需要将windows事件对象关联到上面的WSAOVERLAPPED结构。
- 当使用WSAOVERLAPPED结构进行I/O调用时,如调用WSASend和WSARecv,这些函数立即返回。通常情况下I/O调用会失败,返回值是SOCKET_ERROR,并且WSAGetLastError函数报告了WSA_IO_PENDING出错状态。这个出错状态标示I/O操作正在进行,在以后的一个时间,应用程序需要通过在关联到WSAOVERLAPPED结构的事件对象上等待以确定什么时候一个重叠I/O请求完成,就这样WSAOVERLAPPED结构在重叠I/O请求的初始化和随后完成之间提供了交流媒介。
- 当重叠I/O最终完成之后,与之关联的事件对象受信,等待函数返回,应用程序可以使用WSAGetOverlappedResult函数取得重叠操作的结果。
BOOL WSAGetOverlappedResult(
SOCKET s,//套接字句柄
LPWSAOVERLAPPED lpOverlapped,//重叠操作启动时指定WSAOVERLAPPED结构
LPDWORD lpcbTransfer,//用来取得实际传输字节的数量
BOOL fWait,//指定是否要等待未决的重叠操作
LPDWORD lpdwFlags//用于取得完成状态
)