int WSAAsyncSelect( SOCKET s, //标识一个需要事件通知的套接口描述符 HWND hWnd, //标识一个在网络事件发生时要想收到消息的窗口或对话框的句柄 u_int wMsg, //在网络事件发生时要接收的消息,该消息会投递到由hWnd句柄指定的窗口或对话框 long lEvent //位屏蔽码,用于指明应用程序感兴趣的网络事件集合 );
#define WSAGETSELECTERROR(lParam) HIWORD(lParam) #define WSAGETSELECTEVENT(lParam) LOWORD(lParam)
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) { switch(uMsg) { case WM_USER_SOCKET: { SOCKET sock=wParam; //wParam参数标志了网络事件发生的套接口 if (WSAGETSELECTERROR(lParam)) { closesocket(sock); return 0; } switch (WSAGETSELECTEVENT(lParam)) { case FD_ACCEPT: //连接请求到来 { sockaddr_in add; int len=sizeof(add); SOCKET sNew=accept(sock,(sockaddr*)&add,&len); WSAAsyncSelect(sNew,hwnd,WM_USER_SOCKET,FD_READ|FD_CLOSE); } break; case FD_READ: //数据发送来 { recv(...); ... send(...); } break; case FD_CLOSE: //关闭连接 { closesocket(sock); } break; } } break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd,uMsg,wParam,lParam); } return 0; } int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { // 初始化网络库 WSADATA wsaData; WORD wVersionRequested=MAKEWORD(2,2); WSAStartup(wVersionRequested,&wsaData); // 创建窗口 HWND hwnd=CreateWindow(...); // 创建Socket SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // 绑定Socket sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(2148); sin.sin_addr.S_un.S_addr=inet_addr("192.168.1.1"); bind(s,(sockaddr*)&sin,sizeof(sin)); // 监听 listen(s,1); // 关联窗口和Socket消息 WSAAsyncSelect(s,hwnd,WM_USER_SOCKET,FD_ACCEPT|FD_CLOSE); //---消息循环---- MSG msg; while (GetMessage(&msg,0,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 关闭Socket closesocket(s); // 库饭初始化 WSACleanup(); }
// 创建事件 WSAEVENT WSAAPI WSACreateEvent( VOID );
BOOL WSAAPI WSAResetEvent( WSAEVENT hEvent //事件句柄 );
BOOL WSAAPI WSASetEvent( WSAEVENT hEvent //事件句柄 );
BOOL WSAAPI WSACloseEvent( WSAEVENT hEvent // hEvent:标识一个开放的事件对象句柄。 );
int WSAEventSelect( SOCKET s, //一个标识套接口的描述字 WSAEVENT hEventObject, //是一个由WSACreateEvent(?)函数创建的事件对象句柄,用于标识与所提供的FD_XXX网络事件集合相关的一个事件对象 long lNetworkEvents //指定应用程序感兴趣的各种网络事件(FD_XXX)的组合 );
DWORD WSAWaitForMultipleEvents( DWORD cEvents, //指定下面lpEvents所指的数组中事件对象句柄的个数,事件对象句柄的最大值为WSA_MAXIMUM_WAIT_EVENTS。 const WSAEVENT* lphEvents, //指向一个事件对象句柄的数组 BOOL fWaitAll, //指定是否等待所有的事件对象都变成受信状态(为TRUE:是;FALSE:否) DWORD dwTimeout, //指定要等待的时间,可以为WSA_INFINITE BOOL fAlertable //指定当系统将一个输入/输出完成例程放入队列以供执行时,函数是否返回。 //若为真TRUE,则函数返回且执行完成例程。若为假FALSE,函数不返回,不执行完成例程。 );
int WSAEnumNetworkEvents( SOCKET s, //标识套接口的描述字。 WSAEVENT hEventObject, //hEventObject 参数则是可选的; 它指定了一个事件句柄,对应于打算重设的那个事件对象,令其自动重置为非激发状态 //如果不想用 hEventObject 参数来重设事件,那么可使用 WSAResetEvent函数手动重置 LPWSANETWORKEVENTS lpNetworkEvents, //代表一个指针,指向 WSANETWORKEVENTS 结构,用于接收套接字上发生的网络事件类型以及可能出现的任何错误代码。 LPINT lpiCount //数组中的元素数目。在返回时,本参数表示数组中的实际元素数目; //如果返回值是WSAENOBUFS,则表示为获取所有网络事件所需的元素数目。 );
typedef struct _WSANETWORKEVENTS { long lNetworkEvents; //指定了一个值,对应于套接字上发生的所有网络事件类型(FD_READ、FD_WRITE 等) int iErrorCode[FD_MAX_EVENTS]; //参数指定的是一个错误代码数组,同 lNetworkEvents 中的事件关联在一起. //针对每个网络事件类型,都存在着一个特殊的事件索引,名字与事件类型的名字类似,需要在事件名字后面添加一个"_BIT" } WSANETWORKEVENTS, *LPWSANETWORKEVENTS;
int main() { //初始化库 WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); //创建套接字 SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //设置套接口s"非阻塞模式" u_long u1=1; ioctlsocket(s,FIONBIO,(u_long*)&u1); //绑定本地地址 struct sockaddr_in Sadd; Sadd.sin_family=AF_INET; Sadd.sin_port=htons(1928); Sadd.sin_addr.S_un.S_addr=inet_addr("192.168.1.1"); bind(s,(sockaddr*)&Sadd,sizeof(Sadd)); //监听 listen(s,5); //创建事件对象 WSAEVENT NewEvent=WSACreateEvent(); //网络事件注册 WSAEventSelect(s,NewEvent,FD_ACCEPT|FD_CLOSE); //准备工作 int t=1; WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS]; SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS]; int n=0; eventArray[n]=NewEvent; sockArray[n]=s; n++; //循环处理 while (true) { //等待事件对象 int nIndex=WSAWaitForMultipleEvents(n,eventArray,FALSE,40000,FALSE); if (nIndex==WSA_WAIT_FAILED) { //调用失败 break; } else if (nIndex==WSA_WAIT_TIMEOUT) { //超时 break; } else { //网络事件触发事件对象句柄的工作状态 WSANETWORKEVENTS event;//该结构记录网络事件和对应出错代码 //网络事件查询 WSAEnumNetworkEvents(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,&event); WSAResetEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); if ((event.lNetworkEvents&FD_ACCEPT)!=0) { //处理FD_ACCEPT通知消息 if (event.iErrorCode[FD_ACCEPT_BIT]==0) { if (n>WSA_MAXIMUM_WAIT_EVENTS) { //连接超上限 break; } SOCKET sNew=accept(sockArray[nIndex-WSA_WAIT_EVENT_0],NULL,NULL); NewEvent=WSACreateEvent(); WSAEventSelect(sNew,NewEvent,FD_READ|FD_CLOSE); eventArray[n]=NewEvent; sockArray[n]=sNew; n++; } } else if (event.lNetworkEvents&FD_READ) { //处理FD_READ通知消息 if (event.iErrorCode[FD_READ_BIT]==0) { char buf[256]; memset(buf,0,256); int nRecv=recv(sockArray[nIndex-WSA_WAIT_EVENT_0],buf,sizeof(buf),0); } } else if (event.lNetworkEvents&FD_CLOSE) { //处理FD_CLOSE通知消息 if (event.iErrorCode[FD_CLOSE_BIT]==0) { closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]); WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); } else { if (event.iErrorCode[FD_CLOSE_BIT]==10053) { closesocket(sockArray[nIndex-WSA_WAIT_EVENT_0]); WSACloseEvent(eventArray[nIndex-WSA_WAIT_EVENT_0]); } } for (int j=nIndex-WSA_WAIT_EVENT_0;j<n-1;j++) { sockArray[j]=sockArray[j+1]; eventArray[j]=eventArray[j+1]; } n--; } } } //关闭socket closesocket(s); //卸载库 WSACleanup(); return 0; }
WSAEventSelec模型可以在没有窗口的条件下使用,但受到WSAWaitForMultipleEvents限制,只能接收64个socket的限制。如果和多线程配合使用,能够使接收数目增加,但由于核心对象的增加,性能会下降,故不推荐在服务器端使用。
(版权所有,转载时请注明作者和出处http://blog.csdn.net/arau_sh/article/details/13769753)