Windows Socket编程TCP范例之四(WSAEventSelect)

来源:互联网 发布:php工厂模式代码 编辑:程序博客网 时间:2024/06/18 06:46
    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;}