|
WinSock提供了另一个有用的异步I/O模型,即WSAEventSelect事件模型。和WSAAsyncSelect模型类似,它也允许应用程序在一个或多个套接字上接收以事件为基础的网络事件通知。一般来说,由WSAAsyncSelect模型采用的网络事件可以原封不动地移植到新模型,在新模型开发的应用程序中,也能接收和处理所有这些事件。WSAEventSelect模型与WSAAsyncSelect模型的最主要差别在于网络事件会发送到一个事件对象句柄,而不是投递到一个窗口例程,因此,无窗口句柄的工作线程也可以处理事件通知。 WSAEventSelect模型通过WSAEventSelect函数注册要处理的网络事件类型。WSAEventSelect函数声明如下: int WSAAPI WSAEventSelect ( SOCKET s, WSAEVENT hEventObject, long lNetworkEvents ); 其中参数功能如下: ? s 一个标识套接字的描述符。 ? hEventObject 一个句柄,用于标识与所提供的FD_XXX网络事件集合相关的一个事件对象。 ? lNetworkEvents 一个屏蔽位,用于指定感兴趣的FD_XXX网络事件组合。 如果应用程序指定的网络事件及其相应的事件对象成功设置,则返回0。否则返回INVALID_SOCKET错误,应用程序可通过WSAGetLastError()函数来获取相应的错误代码。 WSAEventSelect()常用来决定何时进行数据IO操作(如send( )或recv( )),并期望能立即成功。但是一个稳定的应用程序应该做好这样的准备,即事件对象被设置,并且一个WinSock调用以WSAEWOULDBLOCK立即返回。举例来说,有可能发生下述操作序列: (1)套接字s上到达数据,WinSock设置了WSAEventSelect事件对象。 (2)应用程序进行其他操作。 (3)在进行操作时,应用程序调用了ioctlSocket(s, FIONREAD...)并发现有数据可读。 (4)应用程序调用一个recv(s,...)来读取数据。 (5)最后应用程序等待WSAEventSelect( )所指定的数据对象,该数据对象指出数据可读。 (6)应用程序调用recv(s,...),但以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( )。 WSAEventSelect函数返回值的错误代码意义如下: ? WSANOTINITIALISED 在调用本API之前应成功调用WSAStartup( )。 ? WSAENETDOWN 网络子系统失效。 ? WSAEINVAL 参数中有非法值,或者指定的套接字处于非法状态。 ? WSAEINPROGRESS 一个阻塞的WinSock调用正在进行中,或者服务提供者仍在处理一个回调函数。 ? WSAENOTSOCK 描述符不是一个套接字。 下面给出WSAEventSelect()模型的编程实现例程。 //=========================================================== //程序清单 WSAEventSelect模型 //示例程序:演示如何使用WSAEventSelect()模型 //编写: suyu 2005/09 //============================================================ void main(void) { SOCKET Listen; SOCKET Accept; //…省去若干定义 if ((Ret = WSAStartup(0x0202, &wsaData)) != 0) { printf("WSAStartup() failed with error %d\n", Ret); return; } printf("WSAStartup OK\n"); if ((Listen = socket (AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { printf("socket() failed with error %d\n", WSAGetLastError()); return; } printf("socket creating OK\n"); CreateSocketInformation(Listen); //选择FD_ACCEPT和FD_CLOSE事件 if (WSAEventSelect(Listen,EventArray[EventTotal-1], FD_ACCEPT| FD_CLOSE) == SOCKET_ERROR) { printf("WSAEventSelect() failed with error %d\n", WSAGetLastError()); return; } printf("WSAEventSelect OK\n"); InternetAddr.sin_family = AF_INET; InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY); InternetAddr.sin_port = htons(PORT);
if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR) { printf("bind() failed with error %d\n", WSAGetLastError()); return; } if (listen(Listen, 5)) { printf("listen() failed with error %d\n", WSAGetLastError()); return; } printf("listen OK\n"); //循环处理事件 while(TRUE) { if ((Event = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE)) == WSA_WAIT_FAILED) { printf("WSAWaitForMultipleEvents error %d\n", WSAGetLastError()); return; } if(WSAEnumNetworkEvents(SocketArray[Event-WSA_WAIT_EVENT_0]-> Socket,EventArray[Event-WSA_WAIT_EVENT_0],&NetworkEvents)== SOCKET_ERROR) { printf("WSAEnumNetworkEvents error %d\n", WSAGetLastError()); return; } if (NetworkEvents.lNetworkEvents & FD_ACCEPT) { //FD_ACCEPT事件 printf("accept new connection... \n"); /* 处理FD_ACCEPT事件,调用accept函数*/ }
if (NetworkEvents.lNetworkEvents & FD_READ || NetworkEvents.lNetworkEvents & FD_WRITE) { /* 处理FD_ READ和FD_WRITE事件 */ } if (NetworkEvents.lNetworkEvents & FD_CLOSE) { /*处理 FD_CLOSE事件 */ if (NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0) { printf("FD_CLOSE failed with error %d\n", NetworkEvents. iErrorCode[FD_CLOSE_BIT]); break; } printf("Closing socket information %d\n", SocketArray[Event - WSA_WAIT_EVENT_0]->Socket); FreeSocketInformation(Event - WSA_WAIT_EVENT_0); } } return; } 点击下载此文件 |
|