主要涉及的WSAAPI函数
一、信号对象
创建一个初始状态为失信的匿名的需要手动重置的事件对象。
WSAEVENT WSAAPI WSACreateEvent( VOID );
返回值:
如果函数成功,则返回值即是事件对象的句柄。
如果函数失败,返回WSA_INVALID_EVENT。应用程序可通过调用WSAGetLastError()函数获取进一步的错误信息:
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSA_NOT_ENOUGH_MEMORY 无足够内存创建事件对象。
二、于信号对象对立的关闭信号
关闭一个开放的事件对象句柄。
#include <winsock2.h>
BOOL WSAAPI WSACloseEvent( WSAEVENT hEvent );
hEvent:标识一个开放的事件对象句柄。
返回值:
如果函数顺利完成,返回值为真TRUE。如果失败,返回值为假FALSE。可用 WSAGetLastError()调用获取更多的错误信息。
错误代码:
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSA_INVALID_HANDLE hEvent不是一个合法的事件对象句柄。
三、WINSOCK 提供的异步事件通知I/O模型(注册事件)
确定与所提供的FD_XXX网络事件(如FD_READ、FD_CONNECT、FD_OOB)集合相关的一个事件对象。
int WSAAPI WSAEventSelect ( SOCKET s, WSAEVENT
hEventObject, long lNetworkEvents );
s:一个标识套接口的描述字。
hEventObject:一个句柄,用于标识与所提供的FD_XXX网络事件集合相关的一个事件对象。
lNetworkEvents:一个掩码位,用于指定感兴趣的FD_XXX网络事件组合。
如果应用程序指定的网络事件及其相应的事件对象成功设置,则返回0。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。
其中还有特殊情况:在使用select()和WSAAsyncSelect()函数时,WSAEventSelect()常用来决定何时进行数据传输操作(如send()或recv()),并期望能立即成功。但是一个稳定的应用程序应该做好这样的准备,即事件对象被设置,并且一个WinSock调用以WSAEWOULDBLOCK立即返回 。举例来说,可能发生下述操作序列:
套接字上到达数据,WinSock设置了WSAEventSelect事件对象。事件和套接字都有信号到达,发现有数据可读,应用程序调用recv来读取数据,然后应用程序等待WSAEventSelect所指定的数据对象,该数据对象指出数据可读。应用程序调用recv,但以WSAEWOULDBLOCK错误失败。但是WSAEWOULDBLOCK不代表出错,也可能是缓存已满,也能接收失败,继续接收。
网络事件 重新允许函数
FD_READ recv() 或 recvfrom()
FD_WRITE send() 或 sendto()
FD_OOB recv()
FD_ACCEPT accept() 或WSAAccept(),直到返回的错误代码为 WSATRY_AGAIN,指明条件函数返回CF_DEFER。
FD_CONNECT NONE
FD_CLOSE NONE
FD_QOS 用SIO_GET_QOS 命令调用WSAIoctl()。
FD_GROUP_QOS 用SIO_GET_GROUP_QOS命令调用WSAIoctl()。
错误代码:
WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup()。
WSAENETDOWN 网络子系统失效。
WSAEINVAL 参数中有非法值,或者指定的套接口处于非法状态。
WSAEINPROGRESS 一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。
WSAENOTSOCK 描述字不是一个套接口。
四、注册完成后,就应该是WSAWaitForMultipleEvent
只要指定事件对象中的一个或全部处于有信号状态,或者超时就返回。
DWORD WSAAPI WSAWaitForMultipleEvents(
DWORD cEvents, //指出lphEvents所指数组中的事件对象句柄的数目。
const WSAEVENT FAR * lphEvents, // 指向一个事件对象句柄数组的指针
BOOL fWaitAll, //等待一个事件(false)还是全部等待(true)
DWORD dwTimeout, //等待时间(以毫秒计)
BOOL fAlertable ); //在WSAEventSelect中应用此函数,设为false
返回值:如果成功,返回值指出造成函数返回的事件对象。理解为如果有事件被传信,函数返回这个事件的索引值,但是这个索引值需要减去预定义值WSA_WAIT_EVENT_0才是这个事件在事件数组中的位置。
五、一旦收到信号,并且确定事件在数组中的位置。就需要检测套接口上的网络事件。
int WSAAPI WSAEnumNetworkEvents ( SOCKET s, //标识套接口的描述字。
WSAEVENT hEventObject, //网络事件
LPWSANETWORKEVENTS lpNetworkEvents); //一个WSANETWORKEVENTS结构的数组,每一个元素记录了一个网络事件和相应的错误代码。
WSANETWORKEVENTS结构如下:
typedef struct _WSANETWORKEVENTS
{
long lNetworkEvents;
int iErrorCode[FD_MAX_EVENTS];
} WSANETWORKEVENTS
if (nRet == 0)
{
if (NetEvent.lNetworkEvents & FD_ACCEPT)
{
if (NetEvent.iErrorCode[FD_ACCEPT_BIT] != 0)
{
int nErrCode = WSAGetLastError();
}
}
if (NetEvent.lNetworkEvents & FD_READ)
{
if (NetEvent.iErrorCode[FD_READ_BIT] != 0)
{
int nErrCode = WSAGetLastError();
}
}
还有一个函数也要用到,WSAResetEvent,当一个事件信号全部处理完成后,单线程未结束,可以单把自己的信号手动清零。