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的数组及数组大小
WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};
SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS] = {0};
int nEventTotal = 0;
// 请空序号为nIndex的数组元素:Socket以及Event
void 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以及Event
void ClearIndex(int nIndex)
{
// 参数有效性检查 0 =< nIndex <= WSA_MAXIMUM_WAIT_EVENTS-1
if (nIndex < 0
|| nIndex >= WSA_MAXIMUM_WAIT_EVENTS
|| nIndex >= nEventTotal)
return;
// 清空最后一个元素,则直接赋0
if (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;
}