网络服务器编程——事件选择模型

4.3.3事件选择模型

事件选择模型将每个套接字和每个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的结构指针。

你可能感兴趣的:(网络服务器编程——事件选择模型)