1. 程式人生 > >網路伺服器程式設計——事件選擇模型

網路伺服器程式設計——事件選擇模型

4.3.3事件選擇模型

事件選擇模型將每個套接字和每個WSAEVENT物件對應起來,並且在註冊的時候指定需要關注的哪些網路事件。

缺陷:不能同時處理多個套接字,只能同時處理一個事件對應的套接字;一個執行緒中處理的套接字有限,一般為64;應用程式中要處理大於64個套接字,必須額外建立執行緒。

//事件選擇TCP伺服器端

#include <iostream>

#include <WinSock2.h>

 

#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 <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的結構指標。