2024年6月11日发(作者:)

在Winsock中,重叠 I/O(Overlapped I/O)模型能达到更佳的系统性能,高于之前的选择模型,

异步选择模型和事件选择模型。重叠模型的基本设计原理便是让应用程序使用一个重叠的数据结构

(WSAOVERLAPPED),一次投递一个或多个 Winsock I/O 请求。针对这些提交的请求,在它

们完成之后,我们的应用程序会收到通知,于是我们就可以对数据进行处理了。

使用重叠I/O模型必须使用 WSA_FLAG_OVERLAPPED 这个标志,创建一个套接字。例如:

1 SOCKET s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

如果使用socket这个函数的话,会默认自动使用WSA_FLAG_OVERLAPPED。

创建好套接字后,将其与本地接口绑定到一起之后便可以开始重叠I/O操作了。不过需要注意的时,

在重叠I/O的状态下不能使用send、recv等函数,他们被WSASend、WSARecv函数替换掉了。

基本上都是以WSA开头的Winsock函数,主要有一下几种:

WSASend

WSASendTo

WSARecv

WSARecvFrom

WSAloctl

AcceptEx

TrnasmitFile

使用时还要指定一个WSAOVERLAPPED结构(可选)。

通常情况下,调用这些函数会返回WSA_IO_PENDING,这就说明函数调用成功了,但是I/O函

数还没完成。

当调用这些函数的时候,如果指定了WSAOVERLAPPED参数,函数完成后会立即返回,无论套接

字是否为阻塞模式,可以通过等待时间对象通知或者通过完成例程来得知I/O请求是否成功。通常

情况下第一种时间通知的方法比较常用。

这里只需要注意一点,重叠函数(如:WSARecv)的参数中都有一个 Overlapped 参数,我们可以

假设是把我们的WSARecv这样的操作“绑定”到这个重叠结构上,提交一个请求,而不是将操作立

即完成,其他的事情就交给重叠结构去做,而其中重叠结构又要与Windows的事件对象“绑定”在一

起,这样我们调用完 WSARecv 以后就可以“坐享其成”,等到重叠操作完成以后,自然会有与之对

应的事件来通知我们操作完成,然后我们就可以来根据重叠操作的结果取得我们想要的数据了。

重叠I/O的事件通知方法要求将Win32事件对象和WSAOVERLAPPED结构关联在一起。I/O操

作完成后,事件的状态就会变为已传信。

下面是WSAOVERLAPPED结构体的定义:

1

2

3

4

5

typedef struct _WSAOVERLAPPED {

ULONG_PTR Internal;

ULONG_PTR InternalHigh;

union {

struct {

防爆摄像机厂家/,龙岗厂房出租/

6

7

8

9

10

11

DWORD Offset;

DWORD OffsetHigh;

}; PVOID Pointer;

};

HANDLE hEvent;

} WSAOVERLAPPED, *LPWSAOVERLAPPED;

Internal, InternalHigh, Offset, OffsetHigh为系统内部使用,通常情况下不作修改。

hEvent则需要传递一个事件句柄。可用 WSACreateEvent 函数来创建一个事件对象句柄。一旦创

建好一个事件句柄,简单地将重叠结构的 hEvent 字段分配给事件句柄,再使用重叠结构,调用一

个Winsock函数即可,比如 WSASend 或 WSARecv。

一个重叠 I/O 请求最终完成后,我们的应用程序要负责取回重叠 I/O 操作的结果。一个重叠请求

操作最终完成之后,在事件通知方法中,Winsock会更改与一个 WSAOVERLAPPED 结构对应的

一个事件对象的事件传信状态,将其从“未传信”变成“已传信”。由于一个事件对象已分配给

WSAOVERLAPPED 结构,所以只需简单地调用 WSAWaitForMultipleEvents 函数,

从而判断出一个重叠 I/O 调用在什么时候完成。

发现一次重叠请求完成之后,需要调用WSAGetOverlappedResult函数来判断那个重叠调用到底是

成功还是失败了。该函数定义如下:

1

2

3

4

5

6

7

BOOL WSAAPI WSAGetOverlappedResult(

__in SOCKET s,

__in LPWSAOVERLAPPED lpOverlapped,

__out LPDWORD lpcbTransfer,

__in BOOL fWait,

__out LPDWORD lpdwFlags

);

s对应的就是套接字。lpOverlapped参数指定一个WSAOVERLAPPED结构。lpcbTransfer参数负

责接收一次重叠发送或接收操作实际传输的字节数。fWait 参数用于决定函数是否应该等待一次待

决(未决)的重叠操作完成。若将 fWait设为 TRUE,那么除非操作完成,否则函数不会返回。

若设为FALSE,而且操作仍然处于“待决”状态,那么WSAGetOverlappedResult 函数会返回 FALSE

值,同时返回一个WSAIOINCOMPLETE(I/O操作未完成)错误。但就我们目前的情况来说,由

于需要等候重叠操作的一个已传信事件完成,所以该参数无论采用什么设置,都没有任何效果。

参数 lpdwFlags 对应于一个指针,指向一个DWORD(双字),负责接收结果标志(假如原先的

重叠调用是用WSARecv或WSARecvFrom函数发出的)。

关于这个函数的返回值,一旦调用成功,则会返回TRUE,并且lpcbTransfer会更新。若失败则会

返回FALSE。失败的原因有这么几种:

重叠I/O操作仍在“待决”状态。

重叠I/O操作已经完成,但是存在错误。

重叠操作的完成状态不可判决,因为在WSAGetOverlappedResult函数的参数中存在错误

失败后,lpcbTransfer值不会更新,此时可以调用WSAGetLastError函数。

防爆摄像机厂家/,龙岗厂房出租/