WSAEventSelect 就是 select的增强版;注意WSAEventSelect 是通知异步, 而不是传送数据异步;总的来说就是一个异步的阻塞模型;如果要与 select 做个比较的话 :select 在 需要进行或者可以进行io处理时 返回. 而WSAEventSelect 在返回时(WSAWaitForMultipleEvents) 与io状态无关;例如: select 在收到数据 并返回时 , 他监听此套接字并检查接受缓冲区, 等到缓冲区能读时再返回.而WSAEventSelect 的等待函数 WSAWaitForMultipleEvents 只要此套接字有事件发生就返回; 因此称为异步通知;另WSAEventSelect 与 nix下的 epoll 整体编程模型很像; epoll : epoll说明具体使用到的函数:WSACreateEvent 创建一个事件, 默认手动模式WSAEventSelect 让一个套接字与一个事件捆绑在一起 注册到操作系统, 无需像select 每次重置;WSAWaitForMultipleEvents (阻塞) 等待套接字对应的事件发生, 最多能监听WSA_MAXIMUM_WAIT_EVENTS 个事件;WSAEnumNetworkEvents 查看套接字对应的具体事件 ; 这也就是与select 返回时机的不同的原因; 注意:此函数将重置事件,因此无需调用WSAResetEvent;重要就这4个函数, 其他的函数调用查看msdn 即可;echo_serv.c#include “../utils.h”#define BUFF_SIZE 8192 //关闭套接字后 . 调整数组static void adjust_sockarr(SOCKET * sock_arr, int start_index, int total){ for (int i = start_index; i < total; ++i) sock_arr[i] = sock_arr[i + 1];} //关闭事件后, 调整数组static void adjust_eventarr(WSAEVENT * event_arr, int start_index, int total){ for (int i = start_index; i < total; ++i) event_arr[i] = event_arr[i + 1];} int _tmain(int argc, _TCHAR* argv[]){ unsigned short port = 0; scanf(" %hd", &port); WSADATA wsadata; WSAStartup(MAKEWORD(2,2),&wsadata); SOCKET listensock = socket(AF_INET, SOCK_STREAM, 0); SOCKADDR_IN serv_addr, cli_addr; memset(&serv_addr, 0, sizeof(serv_addr)); memset(&cli_addr, 0, sizeof(cli_addr)); serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(port); if (bind(listensock, (SOCKADDR*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR){ print_error(WSAGetLastError()); return 0; } if (listen(listensock, 5) == SOCKET_ERROR){ print_error(WSAGetLastError()); return 0; } //创建一个与监听套接字捆绑的事件 WSAEVENT wsaevent = WSACreateEvent(); //FD_ACCEPT :一旦连接发生, 此事件将发生 if (WSAEventSelect(listensock, wsaevent, FD_ACCEPT) == SOCKET_ERROR){ print_error(WSAGetLastError()); return 0; } int event_count = 0 , cli_len = sizeof(cli_addr); //准备2个数组,用于存放套接字与事件 SOCKET sock_arr[WSA_MAXIMUM_WAIT_EVENTS] = {0}; WSAEVENT event_arr[WSA_MAXIMUM_WAIT_EVENTS] = {0}; //把套接字与事件存放在数组里 , 并一一对应 sock_arr[event_count] = listensock; event_arr[event_count] = wsaevent; ++event_count; // 事件总数 == 套接字总数 DWORD pos , start_index ,event_index , strlen; WSANETWORKEVENTS netevents; SOCKET cli_socket; char buf[BUFF_SIZE]; while (1){ //等待事件发生,只要一个事件发生就返回, 具体参数查看msdn; pos = WSAWaitForMultipleEvents(event_count, event_arr, FALSE, WSA_INFINITE, FALSE); printf(“pos:%d , event_count:%d\n”, pos,event_count); start_index = pos - WSA_WAIT_EVENT_0; //循环的意义:在一个繁忙的服务器上有可能在一个事件发生的瞬间,又有一个事件发生了 for (int i = start_index; i < event_count; ++i){ //依次验证从这个已经返回的事件,以及之后的套接字事件有没有发生. //由于timeout参数是 0 ,所以非阻塞 event_index = WSAWaitForMultipleEvents(1, event_arr+i, TRUE, 0, FALSE); if (WSA_WAIT_FAILED == event_index || WSA_WAIT_TIMEOUT == event_index){ printf(“index:%d , timeout or failed\n”, i); continue; } //有事件发生了,查看套接字对应的具体事件是什么 if (WSAEnumNetworkEvents(sock_arr[i], event_arr[i], &netevents) == SOCKET_ERROR){ _tprintf(TEXT(“WSAEnumNetworkEvents error : index:%d \ndetail:”), i); print_error(WSAGetLastError()); continue; } strlen = sprintf(buf, “sock_arr[%d] occurs “, i); buf[strlen] = 0; if (netevents.lNetworkEvents & FD_CONNECT) strcat(buf, " connect “); if (netevents.lNetworkEvents & FD_ACCEPT) strcat(buf , " accept “); if (netevents.lNetworkEvents & FD_WRITE) strcat(buf , " write “); if (netevents.lNetworkEvents & FD_CLOSE) strcat(buf , " close “); puts(buf); //如果有连接 if (netevents.lNetworkEvents & FD_ACCEPT){ //如果有错 if (netevents.iErrorCode[FD_ACCEPT_BIT] != 0){ _tprintf(TEXT(“accept error , index:%d,detail:\n”),i); print_error(WSAGetLastError()); continue; } cli_len = sizeof(cli_addr); cli_socket = accept(sock_arr[i], (SOCKADDR*)&cli_addr, &cli_len); //给新的连接创建事件 wsaevent = WSACreateEvent(); //把套接子与事件注册进操作系统,用于监听 if (WSAEventSelect(cli_socket, wsaevent, FD_READ | FD_CLOSE) != SOCKET_ERROR){ //放进数组 sock_arr[event_count] = cli_socket; event_arr[event_count] = wsaevent; ++event_count; printf(“new client ip:%s, port:%d\n”, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); } } //如果套接字能读 if (netevents.lNetworkEvents & FD_READ){ if (netevents.iErrorCode[FD_READ_BIT] != 0){ _tprintf(TEXT(“read error , index:%d detail:\n”), i); print_error(WSAGetLastError()); continue; } strlen = recv(sock_arr[i], buf, BUFF_SIZE, 0); buf[strlen] = 0; strlen = send(sock_arr[i], buf, strlen, 0); printf(“recv len : %d, buf:%s\n”, strlen, buf); } //如果对端关闭了 if (netevents.lNetworkEvents & FD_CLOSE){ //关闭事件 WSACloseEvent(event_arr[i]); closesocket(sock_arr[i]); –event_count; //调整数组 adjust_sockarr(sock_arr, i, event_count); adjust_eventarr(event_arr, i, event_count); if (netevents.iErrorCode[FD_CLOSE_BIT] != 0){ _tprintf(TEXT(“close error , index:%d , error:%d .detail:\n”), i, netevents.iErrorCode[FD_CLOSE_BIT]); print_error(WSAGetLastError()); continue; } cli_len = sizeof(cli_addr); if (getpeername(sock_arr[i], (SOCKADDR*)&cli_addr, &cli_len) != SOCKET_ERROR){ printf(“peer ip:%s, port:%d\n”, inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port)); } printf(“sock_arr[%d] closed\n”, i); } } } WSACleanup(); return 0;}