WSAAsyncSelect( ),函数。
简介
#include
intPASCAL FAR WSAAsyncSelect (SOCKET s,HWND hWnd,unsigned int wMsg,long lEvent);
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);
尽管在本例中,WSAAsyncSelect( )立即使传给该套接口的事件消息无效,仍有可能有消息等在应用程序的
消息队列中.应用程序因此也必须仍准备好接收网络消息-即使消息作废.用closesocket( )关闭一个套接口也同样使WSAAsyncSelect( )发送的消息作废,但在closesocket( )之前队列中的消息仍然起作用.
由于一个已调用accept( )的套接口和用来接收它的侦听套接口有同样的属性,任何为侦听
套接口设置的的WSAAsyncSelect( )事件也同样对已接收的套接口起作用.例如,如果一个侦听的套接口有WSAAsyncSelect( )事件FD_ACCEPT,FD_READ,FD_WRITE,则任何在那个侦听的套接口上接收的套接口将也有FD_ACCEPT,FD_READ,FD_WRITE事件,以及同样的wMsg的值.若需要不同的wMsg及事件,应用程序应调用WSAAsyncSelect( ),将已接收的套接口和想要发送的新消息作为参数传递.
当某一套接口s上发生了一个已命名的网络事件,应用程序窗口
hWnd会接收到消息wMsg.wParam参数标识了网络事件发生的
套接口.
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_ACCEPT 套接口s准备接收新的将要到来的连接.
FD_CONNECT 套接口s上的连接完成.
FD_CLOSE 由套接口s标识的连接已关闭.
返回值:
0 若应用程序感兴趣的网络事件的声明成功.
SOCKET_ERROR 否则.可通过调用WSAGetLastError( )返回特定的错误代码.
评价:
尽管WSAAsyncSelect( )可以以多个事件的组合来调用,应用程序窗口还是会为每个网络事件接收一条消息.
如同select( )函数,WSAAsyncSelect( )会被频繁地调用来决定,何时一次数据转移操作(
send( )或recv( ))可以启动,并且可以立刻成功.尽管如此,健壮的应用程序必须做好这样的准备,即它可能接收到消息及启动了一个会立即返回WSAEWOULDBLOCK的Windows Sockets API调用.例如,下列的事件序列是可能的:
(i) 数据到达
套接口s;
Windows Sockets传递WSAAsyncSelect消息.
(ii)应用程序处理其它一些消息.
(iii) 在处理过程中,应用程序启动了ioctlsocket(s,FIONREAD...)并且注意到有数据准备好读.
(iv)应用程序启动recv(s,...)来读数据.
(v)应用程序循环处理下一条消息,最终到达WSAAsyncSelect消息,表示数据已准备好读.
(vi)应用程序启动recv(s,...),但失败并有错误WSAEWOULDBLOCK.
其它的事件序列也是可能的.
Windows Sockets DLL不会不断地为某一特定的网络事件向一个应用程序发送消息. 如果已成功地向应用程序窗口发送了一特定事件的通知,对该应用程序窗口将不再为该网络事件发消息,直到应用程序调用函数隐含地重新通知该网络事件.
事件
重新通知函数
FD_READ recv( )或recvfrom( )
FD_WRITE send( )或sendto( )
FD_OOBrecv( )
FD_ACCEPTaccept( )
FD_CONNECT无
FD_CLOSE 无
任何对重新通知函数的调用,即使失败,也会达到为相关事件发重新通知消息的效果.
事件驱动以及不必考虑在任一时刻到达的数据量的能力.考虑下列序列:
(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消息.
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( )调用.
WSAEINVAL 指出指定的参数之一是非法的.
WSAEINPROGRESS 一个阻塞的Windows Sockets操作正在进行.
附加的错误代码可能在应用程序窗口接收到消息时被置.这些代码可以用WSAGETSELECTERROR宏从
lParam中取出.对应于每个网络事件的可能错误代码为:
事件:FD_CONNECT
WSAEADDRINUSE 给定的地址已被使用.
WSAEADDRNOTAVAIL 指定的地址在本地机器不能使用.
WSAEAFNOSUPPORT 指定族的地址不能和本
套接口同时使用.
WSAECONNREFUSED 连接的尝试被拒绝.
WSAEDESTADDRREQ 需要一个目的地址.
WSAEFAULT namelen参数不正确.
WSAEINVAL 套接口已经约束到一个地址.
WSAEISCONN 套接口已经连接.
WSAENETUNREACH 此时网络不能从该主机访问.
WSAENOBUFS 无可用的缓冲区空间.
套接口不能连接.
WSAENOTCONN 套接口没有连接.
WSAENOTSOCK 该描述符是文件,不是套接口.
WSAETIMEDOUT 试图连接超时,未建立连接.
事件:FD_CLOSE
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( )之前可能已经发送的消息.
参见:select( )