2024年4月27日发(作者:)

少年易学老难成,一寸光阴不可轻 - 百度文库

WSAAsyncSelect()函数详解

WSAAsyncSelect()

简述:

通知套接口有请求事件发生.

#include

int PASCAL FAR WSAAsyncSelect ( SOCKET s, HWND hWnd,

unsigned int wMsg, long lEvent );

s 标识一个需要事件通知的套接口的描述符.

hWnd 标识一个在网络事件发生时需要接收消息的窗口句柄.

wMsg 在网络事件发生时要接收的消息.

lEvent 位屏蔽码,用于指明应用程序感兴趣的网络事件集合.

注释:

本函数用来请求Windows Sockets DLL为窗口句柄发一条消息-无论它何时检测

到由lEvent参数指明的网络事件.要发送的消息由wMsg参数标明.被通知的套接

口由s标识.

本函数自动将套接口设置为非阻塞模式.

lEvent参数由下表中列出的值组成.

值 意义

FD_READ 欲接收读准备好的通知.

FD_WRITE 欲接收写准备好的通知.

FD_OOB 欲接收带边数据到达的通知.

FD_ACCEPT 欲接收将要连接的通知.

FD_CONNECT 欲接收已连接好的通知.

FD_CLOSE 欲接收套接口关闭的通知.

启动一个WSAAsyncSelect()将使为同一个套接口启动的所有先前的

WSAAsyncSelect()作废. 例如,要接收读写通知,应用程序必须同时用FD_READ

和FD_WRITE调用WSAAsyncSelect(),如下:

rc = WSAAsyncSelect(s, hWnd, wMsg, FD_READ|FD_WRITE);

对不同的事件区分不同的消息是不可能的.下面的代码将不会工作;第二个调用

将会使第一次调用的作用失效,只有FD_WRITE会通过wMsg2消息通知到.

rc = WSAAsyncSelect(s, hWnd, wMsg1, FD_READ);

rc = WSAAsyncSelect(s, hWnd, wMsg2, FD_WRITE);

如果要取消所有的通知,也就是指出Windows Sockets的实现不再在套接口上发

送任何和网络事件相关的消息,则lEvent应置为0.

rc = WSAAsyncSelect(s, hWnd, 0, 0);

1

少年易学老难成,一寸光阴不可轻 - 百度文库

尽管在本例中,WSAAsyncSelect()立即使传给该套接口的事件消息无效, 仍有可

能有消息等在应用程序的消息队列中.应用程序因此也必须仍准备好接收网络消

息-即使消息作废.用closesocket()关闭一个套接口也同样使

WSAAsyncSelect()发送的消息作废,但在closesocke()之前队列中的消息仍然

起作用.

由于一个已调用accept()的套接口和用来接收它的侦听套接口有同样的属性,

任何为侦听套接口设置的的WSAAsyncSelect()事件也同样对已接收的套接口起

作用.例如, 如果一个侦听的套接口有WSAAsyncSelect()事件

FD_ACCEPT,FD_READ,FD_WRITE, 则任何在那个侦听的套接口上接收的套接口将

也有FD_ACCEPT,FD_READ,FD_WRITE事件,以及同样的wMsg的值.若需要不同的

wMsg及事件,应用程序应调用WSAAsyncSelect(),将已接收的套接口和想要发送

的新消息作为参数传递.

当某一套接口s上发生了一个已命名的网络事件,应用程序窗口hWnd会接收到消

息参数标识了网络事件发生的套接口.lParam的低字指明了发生的

网络事件.lParam的高字则含有一个错误代码.该错误代码可以是winsock.h中

定义的任何错误.

错误代码和事件可以通过WSAGETSELECTERRORH和WSAGETSELECTEVENT宏从

lParam中取出.定义如下:

#define WSAGETSELECTERROR(lParam) HIWORD(lParam)

#define WSAGETSELECTEVENT(lParam) LOWORD(lParam)

注意:在accept()调用和为改变事件或wMsg的WSAAsyncSelect()调用中有一个

计时窗口.应用程序如果需要给侦听的和调用过accept()的套接口以不同的

wMsg,它就应该在侦听的套接口上请求FD_ACCEPT事件,然后在accept()调用后

设置相应的事件.由于FD_ACCEPT从不发送给已连接的套接口,而

FD_READ,FD_WRITE,FD_OOB及FD_CLOSE也从不发送给侦听套接口,所以不会产生

困难.

使用以上的宏将最大限度的提高应用程序的可移植性.

返回的可能网络事件如下:

值 意义

FD_READ 套接口s准备读

FD_WRITE 套接口s准备写

FD_OOB 带外数据准备好在套接口s上读.

FD_ACCEPT 套接口s准备接收新的将要到来的连接.

FD_CONNECT 套接口s上的连接完成.

FD_CLOSE 由套接口s标识的连接已关闭.

返回值:

0 若应用程序感兴趣的网络事件的声明成功.

SOCKET_ERROR 否则.可通过调用WSAGetLastError()返回特定的错误代码.

评价:

尽管WSAAsyncSelect()可以以多个事件的组合来调用,应用程序窗口还是会为

每个网络事件接收一条消息.

2

少年易学老难成,一寸光阴不可轻 - 百度文库

如同select()函数,WSAAsyncSelect()会被频繁地调用来决定,何时一次数据转

移操作(send()或recv())可以启动,并且可以立刻成功.尽管如此,健壮的应用

程序必须做好这样的准备, 即它可能接收到消息及启动了一个会立即返回

WSAEWOULDBLOCK的Windows Sockets API调用.例如,下列的事件序列是可能的:

(i) 数据到达套接口s;Windows Sockets传递WSAAsyncSelect消息.

(ii) 应用程序处理其它一些消息.

(iii) 在处理过程中,应用程序启动了ioctlsocket()并且注意

到有数据准备好读.

(iv) 应用程序启动recv(s,...)来读数据.

(v) 应用程序循环处理下一条消息,最终到达WSAAsyncSelect消息,表示数据已

准备好读.

(vi) 应用程序启动recv(s,...),但失败并有错误WSAEWOULDBLOCK.

其它的事件序列也是可能的.

Windows Sockets DLL不会不断地为某一特定的网络事件向一个应用程序发送消

息. 如果已成功地向应用程序窗口发送了一特定事件的通知,对该应用程序窗口

将不再为该网络事件发消息,直到应用程序调用函数隐含地重新通知该网络事

件.

事件 重新通知函数

FD_READ recv()或recvfrom()

FD_WRITE send()或sendto()

FD_OOB recv()

FD_ACCEPT accept()

FD_CONNECT 无

FD_CLOSE 无

任何对重新通知函数的调用,即使失败,也会达到为相关事件发重新通知消息的

效果.

对FD_READ,FD_OOB和FD_ACCEPT事件,消息传递是"水平触发

"(level-triggered)的.这意味着,若调用了重新通知函数并且相关的事件对该

调用仍有效,WSAAsyncSelect()消息就将传给应用程序.这为应用程序提供了事

件驱动以及不必考虑在任一时刻到达的数据量的能力.考虑下列序列:

(i) Windows Sockets DLL在套接口s上接收100字节的数据并传递一个FD_READ

消息.

(ii) 应用程序启动recv(s,buffptr,50,0)接收50字节.

(iii) 由于仍有数据未读,Windows Sockets DLL发送另一个FD_READ消息.

根据以上语义,应用程序不必在收到FD_READ消息时读进所有可读的数据-对应

于每一FD_READ消息进行一次recv()调用是恰当的.如果应用程序为一个

FD_READ消息而启动了多个recv()调用,它将接收到多个FD_READ消息.这样的应

用程序可能希望在开始recv()调用( 通过不为FD_READ事件置位的

WSAAsyncSelect()函数调用)之前关闭FD_READ消息.

如果在应用程序初次调用WSAAsyncSelect()或当调用了重新通知函数时,有一

个事件为真, 则会发送一个相应的消息.例如,若应用程序调用listen(),就会

试图进行连接,然后应用程序调用WSAAsyncSelect()声明它需要为套接口接收

FD_ACCEPT消息,Windows Sockets的实现就会立即传递一个FD_ACCEPT消息.

3

少年易学老难成,一寸光阴不可轻 - 百度文库

FD_WRITE事件处理起来稍有不同.FD_WRITE消息是在套接口第一次用connect()

连接或由accept()接受,并且在send()或sendto()以WSAWOULDBLOCK错误失败

后缓冲区空闲时发送的.因此,应用程序可以假设发送可能在第一次FD_WRITE消

息时开始,并持续到一次返回WSAEWOULDBLOCK的发送. 在这样的失败后,应用程

序将被通知,FD_WRITE消息的发送又将可能.

FD_OOB事件只用在当套接口配置成独立接收带外数据时.如果一个套接口被配

置成接收感兴趣的带外数据状态,带外数据将和普通数据等同视之,并且应用程

序应该注册它感兴趣的方面,然后将接收FD_READ事件,而不是FD_OOB事件.应用

程序可以设置或监控带外数据处理的方法(通过使用setsockopt()或

getsockopt()函数,及SO_OOBINLINE选项).

在FD_CLOSE消息中的错误代码指出套接口的关闭是正常的还是异常的.如果错

误代码是0,则关闭是正常的;若错误代码是WSAECONNRESET,则套接口的虚套接

口将被重置.这些只对SOCK_STREAM类型的套接口起作用.

FD_CLOSE消息在相应套接口的虚电路关闭指令接收到时发送.在TCP术语中,这

意味着FD_CLOSE在连接进入了FIN WAIT或CLOSE WAIT状态时发送.这是远端对

发送方进行了shutdown()调用或closesocket()调用的结果.

请注意你的应用程序将只会收到FD_CLOSE消息来指出虚电路的关闭.它不会收

到FD_READ消息来表示该状况.

错误代码:

WSANOTINITIALISED 在使用本API前必须进行一次成功的WSAStartup()调用.

WSAENETDOWN WINDOWS SOCKETS实现已检测到网络子系统故障.

WSAEINVAL 指出指定的参数之一是非法的.

WSAEINPROGRESS 一个阻塞的Windows Sockets操作正在进行.

附加的错误代码可能在应用程序窗口接收到消息时被置.这些代码可以用

WSAGETSELECTERROR宏从lParam中取出.对应于每个网络事件的可能错误代码

为:

事件:FD_CONNECT

WSAEADDRINUSE 给定的地址已被使用.

WSAEADDRNOTAVAIL 指定的地址在本地机器不能使用.

WSAEAFNOSUPPORT 指定族的地址不能和本套接口同时使用.

WSAECONNREFUSED 连接的尝试被拒绝.

WSAEDESTADDRREQ 需要一个目的地址.

WSAEFAULT namelen参数不正确.

WSAEINVAL 套接口已经约束到一个地址.

WSAEISCONN 套接口已经连接.

WSAEMFILE 没有可用的文件描述符.

WSAENETUNREACH 此时网络不能从该主机访问.

WSAENOBUFS 无可用的缓冲区空间.套接口不能连接.

WSAENOTCONN 套接口没有连接.

WSAENOTSOCK 该描述符是文件,不是套接口.

WSAETIMEDOUT 试图连接超时,未建立连接.

事件:FD_CLOSE

WSAENETDOWN WINDOWS SOCKETS实现已检测到网络子系统故障.

4

少年易学老难成,一寸光阴不可轻 - 百度文库

WSAECONNRESET 连接由远端重建.

WSAECONNABORTED 由于超时或其它失败放弃连接.

事件:FD_READ

事件:FD_WRITE

事件:FD_OOB

事件:FD_ACCEPT

WSAENETDOWN WINDOWS SOCKETS实现已检测到网络子系统故障.

关于Windows Sockets提供者的说明:

Windows Sockets的提供者应确保消息可以成功地传给应用程序.如果

PostMessag()操作失败,Windows Sockets的实现必须重发该消息-只要窗口存

在.

Windows Sockets提供者应使用WSAMAKESELECTREPLY宏来构造消息中的lParam

参数.

当套接口关闭时,Windows Sockets提供者应清除所有保留下来要发送给应用程

序窗口的消息.然而应用程序必须准备好接收,放弃任何在closesocket()之前

可能已经发送的消息.

5