網路伺服器程式設計——事件選擇模型
4.3.3事件選擇模型
事件選擇模型將每個套接字和每個WSAEVENT物件對應起來,並且在註冊的時候指定需要關注的哪些網路事件。
缺陷:不能同時處理多個套接字,只能同時處理一個事件對應的套接字;一個執行緒中處理的套接字有限,一般為64;應用程式中要處理大於64個套接字,必須額外建立執行緒。
//事件選擇TCP伺服器端
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
using namespace
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 <<
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.
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 <WinSock2.h>
#include <iostream>
#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的結構指標。