事件选择模型将每个套接字和每个WSAEVENT对象对应起来,并且在注册的时候指定需要关注的哪些网络事件。
缺陷:不能同时处理多个套接字,只能同时处理一个事件对应的套接字;一个线程中处理的套接字有限,一般为64;应用程序中要处理大于64个套接字,必须额外创建线程。
//事件选择TCP服务器端
#include
#include
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
int main(int argc, char ** argv)
{
//步骤1:当前应用程序和相应的socket库绑定
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0)
{
cout << "WSAStartup Failed!" << endl;
return -1;
}
//步骤2:创建监听套接字和服务器端IP/PORT
SOCKET sockListen = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addr_server;
memset(&addr_server, 0, sizeof(addr_server));
addr_server.sin_family = AF_INET;
addr_server.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY表示绑定电脑上所有网卡IP
addr_server.sin_port = htons(6000);//不能使用公认端口,即端口>= 1024
//步骤3:套接字绑定和监听
bind(sockListen, (SOCKADDR*)&addr_server, sizeof(addr_server));
listen(sockListen, 5);
//步骤4:创建新的事件
WSAEVENT newEvent;
newEvent = WSACreateEvent();
//步骤5:注册网络事件
WSAEventSelect(sockListen, newEvent, FD_ACCEPT | FD_CLOSE);
//步骤6:创建事件数组和套接字数组
WSAEVENT eventArray[WSA_MAXIMUM_WAIT_EVENTS];
SOCKET sockArray[WSA_MAXIMUM_WAIT_EVENTS];
int eventCount = 0;
eventArray[eventCount] = newEvent;
sockArray[eventCount] = sockListen;
eventCount++;
while (1)
{
//步骤7:等待事件发生
int nIndex = WSAWaitForMultipleEvents(eventCount, eventArray, false, WSA_INFINITE, false);
nIndex = nIndex - WSA_WAIT_EVENT_0;
//步骤8:查找发生在套接字上的网络事件,并清除系统内部的网络事件记录,重置事件对象
SOCKET sock = sockArray[nIndex];
WSANETWORKEVENTS Workevent;
WSAEnumNetworkEvents(sock, eventArray[nIndex], &Workevent);
//步骤9:处理网络事件
if (Workevent.lNetworkEvents & FD_ACCEPT)
{
if (Workevent.iErrorCode[FD_ACCEPT_BIT] == 0)
{
if (eventCount >= WSA_MAXIMUM_WAIT_EVENTS)
{
cout << "已达到最大连接数..." << endl;
continue;
}
SOCKADDR_IN addr_client;
int len = sizeof(SOCKADDR);
SOCKET sockClient = accept(sock, (SOCKADDR*)&addr_client, &len);
if (sockClient != INVALID_SOCKET)
{
cout << "客户端IP: " << inet_ntoa(addr_client.sin_addr) << ", 客户端PROT:" << ntohs(addr_client.sin_port) << endl;
WSAEVENT event = WSACreateEvent();
WSAEventSelect(sockClient, event, FD_READ | FD_CLOSE | FD_WRITE);
eventArray[eventCount] = event;
sockArray[eventCount] = sockClient;
eventCount++;
}
}
}
else if (Workevent.lNetworkEvents & FD_READ)
{
if (Workevent.iErrorCode[FD_READ_BIT] == 0)
{
char Buf[1024] = "\0";
int ret = recv(sock, Buf, 2500, 0);
if (ret > 0)
{
cout << "收到一个消息 :" << Buf << endl;
char sendBuf[] = "I recvived your message.";
send(sock, sendBuf, strlen(sendBuf)+1, 0);
}
}
}
else if (Workevent.lNetworkEvents & FD_CLOSE)
{
//关闭客户端的事件和套接字
WSACloseEvent(eventArray[nIndex]);
closesocket(sockArray[nIndex]);
cout << "一个客户端连接已经断开..." << endl;
//删除eventArray和sockArray数组中需要关闭的事件和套接字
for (int i = nIndex; i < eventCount - 1; i++)
{
eventArray[i] = eventArray[i + 1];
sockArray[i] = sockArray[i + 1];
}
eventCount--;
}
else if (Workevent.lNetworkEvents & FD_WRITE)
{
cout << "一个客户端连接允许写入数据..." << endl;
}
}
//步骤10:关闭套接字和库解绑
closesocket(sockListen);
WSACleanup();
return 0;
}
//事件选择TCP客户端:
#include
#include
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
// Initialize Windows socket library
WSADATA wsaData;
WSAStartup(0x0202, &wsaData);
// Create client socket
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// Connect to server
SOCKADDR_IN server;
memset(&server, 0, sizeof(SOCKADDR_IN));
server.sin_family = AF_INET;
server.sin_addr.S_un.S_addr = inet_addr("192.168.137.144");
server.sin_port = htons(6000);
connect(sockClient, (struct sockaddr *)&server, sizeof(SOCKADDR_IN));
while (1)
{
cout << "send:";
char Buf[1024] = "\0";
cin.getline(Buf, 1024);
// Send message
send(sockClient, Buf, strlen(Buf) + 1, 0);
// Receive message
recv(sockClient, Buf, 1024, 0);
printf("Received: '%s'\n", Buf);
}
// Clean up
closesocket(sockClient);
WSACleanup();
return 0;
}
WSAEventSelect(参数1,参数2,参数3):注册网络事件,并关联套接字。
参数1:关联的套接字;
参数2:需要注册的网络事件;
参数3:网络事件类型。
WSAWaitForMultipleEvents(参数1,参数2,参数3,参数4,参数5):等待网络事件发生。
参数1:网络事件的个数;
参数2:网络事件数组;
参数4:等待时间,WSA_INFINITE表示一直等待。
返回值:返回值Index减去WSA_WAIT_EVENT_0就是所发生网络事件在网络时间数组中的索引;但是在windows平台,WSA_WAIT_EVENT_0等于0。
WSAEnumNetworkEvents(参数1,参数2,参数3):查找发生在套接字上的网络事件,并清除系统内部的网络事件记录,重置事件对象。
参数1:发生网络事件的套接字;
参数2:被重置的网络事件;
参数3:指向WSANETWORKEVENTS的结构指针。