windows socket网络编程--事件选择模型( 二 )


int WSAAPI WSAEnumNetworkEvents(  SOCKET             s,              //标识套接字  WSAEVENT           hEventObject,   //用于标识要重置的关联事件对象的可选句柄  LPWSANETWORKEVENTS lpNetworkEvents //指向 WSANETWORKEVENTS 结构的指针 , 该结构填充了发生的网络事件记录和任何关联的错误代码);返回值如果操作成功 , 则返回值为零 。否则 , 返回值SOCKET_ERROR作用枚举出与事件对象相关联的套接字发生了哪些信号 , 结果放在WSANETWORKEVENTS结构体中
工作原理流程大致是这样:

  1. 定义一个socket数组和event数组
  2. 每一个socket操作关联一个event对象
  3. 调用WSAWaitForMultipleEvents函数等待事件的触发
  4. 调用WSAEnumNetworkEvents函数查看是哪个一个事件 , 根据事件找到相应的socket , 然后进行相应的处理:比如数据显示等 , 同时 , 记得要将那个event重置为无信号状态 。
  5. 循环步骤3和4 , 直到服务器退出 。流程图
    windows socket网络编程--事件选择模型

    文章插图
代码实现服务端
UINT  CMFCWSAEventDlg::ThreadProc(LPVOID lparam) { // TODO: 在此添加控件通知处理程序代码 CMFCWSAEventDlg* p = (CMFCWSAEventDlg*)lparam; SocketInit socketInit; SOCKET socketServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (socketServer == INVALID_SOCKET) {AfxMessageBox(_T("套接字创建失败"));closesocket(socketServer);WSACleanup(); } sockaddr_in sock; sock.sin_family = AF_INET; sock.sin_port = htons(5678); sock.sin_addr.S_un.S_addr = INADDR_ANY; int n = sizeof(sock); if (bind(socketServer, (sockaddr*)&sock, sizeof(sock)) == SOCKET_ERROR) {AfxMessageBox(_T("监听失败"));closesocket(socketServer);WSACleanup(); } if (listen(socketServer, SOMAXCONN) == SOCKET_ERROR) {AfxMessageBox(_T("监听失败"));closesocket(socketServer);WSACleanup(); } p->showText.SetWindowText("开始监听\r\n"); // 创建事件对象 , 并关联到新的套节字 WSAEVENT event = ::WSACreateEvent(); ::WSAEventSelect(socketServer, event, FD_ACCEPT | FD_CLOSE); // 添加到表中 p->eventArray[p->nEventTotal] = event; p->sockArray[p->nEventTotal] = socketServer; p->nEventTotal++; CString str; sockaddr_in addrRemote; while (1){// 在所有事件对象上等待int nIndex = ::WSAWaitForMultipleEvents(p->nEventTotal, p->eventArray, FALSE, WSA_INFINITE, FALSE);// 对每个事件调用WSAWaitForMultipleEvents函数 , 以便确定它的状态nIndex = nIndex - WSA_WAIT_EVENT_0;for (int i = nIndex; i < p->nEventTotal; i++){nIndex = ::WSAWaitForMultipleEvents(1, &p->eventArray[i], TRUE, 1000, FALSE);if (nIndex == WSA_WAIT_FAILED || nIndex == WSA_WAIT_TIMEOUT){continue;}else{// 获取到来的通知消息 , WSAEnumNetworkEvents函数会自动重置受信事件WSANETWORKEVENTS event;::WSAEnumNetworkEvents(p->sockArray[i], p->eventArray[i], &event);if (event.lNetworkEvents & FD_ACCEPT)                // 处理FD_ACCEPT通知消息{if (event.iErrorCode[FD_ACCEPT_BIT] == 0){if (p->nEventTotal > WSA_MAXIMUM_WAIT_EVENTS){p->showText.SetSel(-1);p->showText.ReplaceSel("时间太长");continue;}int nAddrLen = sizeof(addrRemote);SOCKET sNew = ::accept(p->sockArray[i], (SOCKADDR*)&addrRemote, &nAddrLen);//MessageBox("已连接");int nLen = p->showText.GetWindowTextLengthA();//p->showText.SetWindowText()str.Format("%s建立连接\r\n", ::inet_ntoa(addrRemote.sin_addr));p->showText.SetSel(-1);p->showText.ReplaceSel(str);WSAEVENT event = ::WSACreateEvent();::WSAEventSelect(sNew, event, FD_READ | FD_CLOSE | FD_WRITE);// 添加到表中p->eventArray[p->nEventTotal] = event;p->sockArray[p->nEventTotal] = sNew;p->nEventTotal++;}}else if (event.lNetworkEvents & FD_READ)         // 处理FD_READ通知消息{if (event.iErrorCode[FD_READ_BIT] == 0){//char szText[256];char szText[1024] = { 0 };//memset(szText, 0, sizeof(szText));int nlen = strlen(szText);int nRecv = ::recv(p->sockArray[i], szText,1024, 0);//AfxMessageBox(nRecv);if (nRecv > 0){szText[nRecv] = '\0';str.Format("%s发来了一条消息:%s\r\n", ::inet_ntoa(addrRemote.sin_addr), szText);p->showText.SetSel(-1);p->showText.ReplaceSel(str);//szText[0] = '\0';// 向客户端发送数据char *sendText =  getallprime(1000);if (::send(p->sockArray[i], sendText, strlen(sendText), 0) > 0){p->showText.SetSel(-1);p->showText.ReplaceSel("已发送结果\r\n");}}}}else if (event.lNetworkEvents & FD_CLOSE)        // 处理FD_CLOSE通知消息{if (event.iErrorCode[FD_CLOSE_BIT] == 0){::closesocket(p->sockArray[i]);for (int j = i; j < p->nEventTotal - 1; j++){p->eventArray[j] = p->eventArray[j + 1];p->sockArray[j] = p->sockArray[j + 1];}p->nEventTotal--;}p->showText.SetSel(-1);p->showText.ReplaceSel("关闭连接\r\n");}}} }}

经验总结扩展阅读