Winsock提供了另一種有用的非同步事件通知 I/O 模型——WSAEventSelect模型。這個模型與WSAAsyncSelect模型類似,允許應用程式在一個或者多個通訊端上接收基於事件的網路通知。它與 WSAAsyncSelect模型類似是因為它也接收 FD_XXX 類型的網路事件,不過並不是依靠Windows 的訊息驅動機制,而是經由事件物件控點通知。 以下樣本還是實現和前兩篇文章(之一&之二&之三)一樣的功能:Client端串連Server端,向Server端發送資料。Server端接受Client端發送過來的資料並輸出。Server端還是單線程處理多Client端串連的情況。
Server.cpp-----------------------------
#include <iostream>#include <winsock2.h>#include <windows.h>using namespace std;#pragma comment(lib, "Ws2_32.lib")#define PORT_NO 6000#define BACKLOG 10// 記錄Socket及對應Event的數組及數組大小WSAEVENTeventArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};SOCKETsockArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};int nEventTotal = 0;// 請空序號為nIndex的數組元素:Socket以及Eventvoid ClearIndex(int nIndex);int main(int argc, char* argv[]){WSADATA wsaData;int ret;WORD wVersionRequested = MAKEWORD(2, 2);SOCKET sockSrv;SOCKADDR_IN addrSrv;// 初始化Windows Socket------ret = WSAStartup(wVersionRequested, &wsaData);if (ret != 0) {cout << "WSAStartup() failed:" << WSAGetLastError() << endl;return -1;}// 建立Socket------sockSrv = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == sockSrv){cout << "socket() failed:" << WSAGetLastError() << endl;WSACleanup();return -1;}addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(PORT_NO);// Bind Socket------ret = bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));if (SOCKET_ERROR == ret){cout << "bind() failed:" << WSAGetLastError() << endl;closesocket(sockSrv);WSACleanup();return -1;}// 監聽------ret = listen(sockSrv, BACKLOG);if (SOCKET_ERROR == ret){cout << "listen() failed:" << WSAGetLastError() << endl;closesocket(sockSrv);WSACleanup();return -1;}cout<< "Server started......" << endl;// 建立與sockSrv對應的WSAEvent並加入數組------WSAEVENT wsaEvt = WSACreateEvent();WSAEventSelect(sockSrv, wsaEvt, FD_ACCEPT | FD_CLOSE);eventArray[nEventTotal] = wsaEvt;sockArray[nEventTotal] = sockSrv;nEventTotal++;// Socket事件響應------while (1){int nIndex = WSAWaitForMultipleEvents(nEventTotal, eventArray, FALSE, WSA_INFINITE, FALSE);if (WSA_WAIT_FAILED == nIndex){cout << "WSAWaitForMultipleEvents failed:" << WSAGetLastError() << endl;break;}nIndex = nIndex - WSA_WAIT_EVENT_0;SOCKET sock = sockArray[nIndex];WSAEVENT wsaEvt = eventArray[nIndex];WSANETWORKEVENTS netWorkEvts;WSAEnumNetworkEvents(sock, wsaEvt, &netWorkEvts);if(netWorkEvts.lNetworkEvents & FD_ACCEPT) // ---FD_ACCEPT{if(netWorkEvts.iErrorCode[FD_ACCEPT_BIT] == 0){SOCKADDR_IN addrClient;int len = sizeof(SOCKADDR);SOCKET sockConn = accept(sock, (SOCKADDR*)&addrClient, &len);if (INVALID_SOCKET == sockConn) { cout << "accept() failed:" << WSAGetLastError() << endl; continue; }cout << "接受到新串連:" << inet_ntoa(addrClient.sin_addr) << endl;if(nEventTotal >= WSA_MAXIMUM_WAIT_EVENTS){cout << "Too many connections!" << endl;closesocket(sockConn);continue;}WSAEVENT sockConnEvt = WSACreateEvent();WSAEventSelect(sockConn, sockConnEvt, FD_READ | FD_CLOSE | FD_WRITE);eventArray[nEventTotal] = sockConnEvt;sockArray[nEventTotal] = sockConn;nEventTotal++;}}else if(netWorkEvts.lNetworkEvents & FD_READ)// ---FD_READ{if(netWorkEvts.iErrorCode[FD_READ_BIT] == 0){char recvBuffer[MAX_PATH] = {0}; int ret = recv(sock, recvBuffer, sizeof(recvBuffer), 0); if (ret == 0) {cout << "Connection has been gracefully closed." << endl; closesocket(sock); CloseHandle(wsaEvt);ClearIndex(nIndex);continue; } else if (ret == SOCKET_ERROR) { cout << "Connection has been closed ungracefully." << endl; closesocket(sock); CloseHandle(wsaEvt);ClearIndex(nIndex);continue; } cout << "Receive Data from Client:" << recvBuffer << endl; }}else if(netWorkEvts.lNetworkEvents & FD_CLOSE)// ---FD_CLOSE{if(netWorkEvts.iErrorCode[FD_CLOSE_BIT] == 0){cout << "Connection has been gracefully closed." << endl;}else{cout << "Connection has been closed ungracefully." << endl;}closesocket(sock); CloseHandle(wsaEvt);ClearIndex(nIndex);}else if(netWorkEvts.lNetworkEvents & FD_WRITE)// ---FD_WRITE{}}// 清理資源------for (int i=0; i<nEventTotal; ++i){closesocket(sockArray[i]); CloseHandle(eventArray[i]);}WSACleanup();cout << "exit..." << endl;return 0;}// 請空序號為nIndex的數組元素:Socket以及Eventvoid ClearIndex(int nIndex){// 參數有效性檢查 0 =< nIndex <= WSA_MAXIMUM_WAIT_EVENTS-1if (nIndex < 0 || nIndex >= WSA_MAXIMUM_WAIT_EVENTS || nIndex >= nEventTotal)return;// 清空最後一個元素,則直接賦0if (nIndex == nEventTotal - 1){eventArray[nIndex] = 0;sockArray[nIndex] = 0;nEventTotal--;return;} // 往前移for (int i=nIndex; i<=nEventTotal-2; ++i){eventArray[i] = eventArray[i+1];sockArray[i] = sockArray[i+1];nEventTotal--;}}
Client.cpp-----------------------------
#include <iostream>#include <windows.h>using namespace std;#pragma comment(lib, "Ws2_32.lib")#define PORT_NO 6000#define SRV_IP_ADDR "127.0.0.1"int main(int argc, char* argv[]){int ret;WSADATA wsaData;WORD wVersionRequested = MAKEWORD(2, 2);SOCKET sockClient;SOCKADDR_IN addrSrv;// 初始化Windows Socket------ret = WSAStartup(wVersionRequested, &wsaData);if (ret != 0) {cout << "WSAStartup() failed:" << WSAGetLastError() << endl;return -1;} // 建立Socket------sockClient = socket(AF_INET, SOCK_STREAM, 0);if (INVALID_SOCKET == sockClient){cout << "socket() failed:" << WSAGetLastError() << endl;WSACleanup();return -1;}addrSrv.sin_addr.S_un.S_addr = inet_addr(SRV_IP_ADDR);addrSrv.sin_family = AF_INET;addrSrv.sin_port = htons(PORT_NO);// 串連------ret = connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));if (SOCKET_ERROR == ret){cout << "connect() failed:" << WSAGetLastError() << endl;closesocket(sockClient);WSACleanup();return -1;}else{cout << "connect() successfully." << endl;}// 發送資料------char sendBuf[MAX_PATH] = {0};while (1){cin.getline(sendBuf, sizeof(sendBuf));if (strcmp(sendBuf, "exit") == 0){break;}ret = send(sockClient, sendBuf, strlen(sendBuf)+1, 0);if (SOCKET_ERROR == ret){cout << "send() failed:" << WSAGetLastError() << endl;closesocket(sockClient);WSACleanup();return -1;}}// 清理資源-------closesocket(sockClient);WSACleanup();cout << "exit..." << endl;return 0;}