將伺服器公升級為select模型處理多個客戶端

2021-10-23 04:37:07 字數 4077 閱讀 6862

之前寫的網路模型,都是客戶端和服務端1對1阻塞的網路程式

每次服務端想要接收新的客戶端連線的時候都必須要有乙個客戶端向server傳送連線,才能返回執行下一步

想要recv的時候也必須要客戶端send真正傳送之後才能執行下一步

因為傳送的速度比較快所以感覺不出來,

像這樣的程式就必須等到誰執行才能執行下一步,這樣就不能執行其他業務,被動的去等待資料,返回,處理業務

如果需要多個客戶端連線處理業務就需要使用到select模型

select

(// _in_ 作用是被傳入用的

_in_ int nfds,

// 在win下這個值是沒有意義的 在linux/mac os系統下是用來最大描述符的 這個描述符指的就是socket

// 上面的集合最大的單位就是這個fd_set,用來操作 nfds 最大的值的socket +1

// _inout_ 傳入傳出,傳入乙個readfds需要查詢的套接字集合,哪些可讀

_inout_opt_ fd_set far

* readfds,

// readfds 去查詢可讀的socket

_inout_opt_ fd_set far

* writefds,

// writefds 查詢可寫的socket

_inout_opt_ fd_set far

* exceptfds,

// exceptfds 異常socket

_in_opt_ const struct timeval far

* timeout // 如果什麼都不傳select是阻塞的,傳入乙個時間就會在指定的時間內沒有東西就會直接返回 非阻塞

);

**修改:

#define win32_lean_and_mean

#define _winsock_deprecated_no_warnings

#include

#include

#include

#include

#pragma comment

(lib,

"ws2_32.lib"

)using namespace std;

enum

cmd;

struct dataheader

;struct login :

public dataheader

char username[32]

; char password[32]

;};struct loginresult :

public dataheader

int result;};

struct logout :

public dataheader

char username[32]

;};struct logoutresult :

public dataheader

int result;};

// 定義乙個vector來儲存連線的客戶端

vector<

socket

> g_clients;

int processor

(socket _client_sock)

; int ret =

recv

(_client_sock, szrecv,

sizeof

(dataheader),0

);dataheader* header =

(dataheader*

)szrecv;

if(ret <=0)

switch

(header-

>cmd)

; // 就不能這樣初始化了 因為是乙個類了

loginresult ret;

send

(_client_sock,

(char*

)&ret,

sizeof

(loginresult),0

);}break

;case

cmd_logout

:break

;default

: dataheader header =

;send

(_client_sock,

(char*

)&header,

sizeof

(dataheader),0

);break;}

}int main()

; _sin.sin_family =

af_inet

; _sin.sin_port =

htons

(8888);

_sin.sin_addr.s_un.s_addr =

inaddr_any;if

(socket_error

==bind

(_sock,

(sockaddr*

)&_sin,

sizeof

(_sin)))

cout <<

"繫結網路埠成功...."

<< endl;if(

socket_error

==listen

(_sock,5)

) cout <<

"監聽網路埠成功...."

<< endl;

while

(true

)// nfds 是乙個整數值,是指fd_set集合中所有描述符(socket)的範圍,而不是數量

// 即是所有檔案描述符最大值+1,在win中這個引數無所謂可以寫0

/* select 現在是阻塞的情況

如果是做乙個純應答型的網路服務程式那麼這樣就可以足夠的使用

如果除了相應客戶端的訊息以外,還要主動的去給客戶端推送一些訊息這樣就有些不方便

*/int ret =

select

(_sock +1,

&fdread,

&fdwrite,

&fdexp,

null);

if(ret <0)

// 判斷是否在集合中if(

fd_isset

(_sock,

&fdread));

int n_addr_len =

sizeof

(_client_addr)

;socket _client_sock =

invalid_socket

; _client_sock =

accept

(_sock,

(sockaddr*

)&_client_addr,

&n_addr_len);if

(invalid_socket

== _client_sock)

cout <<

"accept 成功.... ip = "

<<

inet_ntoa

(_client_addr.sin_addr)

<< endl;

// 使用vector存起連線進來的客戶端

g_clients.

push_back

(_client_sock);}

/* 因為我們本是伺服器建立的socket已經處理過了

所以只需要用來接收資料就可以了accept

*/// 迴圈處理陣列裡面的客戶端

for(size_t n =

0; n < fdread.fd_count; n++)}

}}// 清理

for(size_t n = g_clients.

size()

-1; n >=0;

--n)

// 6 關閉套接字

closesocket

(_sock)

;// 清理win socket環境

wsacleanup()

;getchar()

;return0;

}

-------------------------------- the end ----------------------------------

將伺服器select模型設定為非阻塞,處理更多業務

如果除了相應客戶端的訊息以外,還要主動的去給客戶端推送一些訊息這樣就有些不方便 server 設定select為阻塞狀態的時候,那麼絕大部分的時間都用在等待客戶端連線上面 如果是做乙個純應答型的網路服務程式那麼這樣就可以足夠的使用 如果除了相應客戶端的訊息以外,還要主動的去給客戶端推送一些訊息這樣就...

伺服器IO模型之Select

阻塞與非阻塞 select模型目的 主要是避免在套接字呼叫上阻塞的應用程式有能力管理多個套接字,即是單一執行緒模式下只能處理乙個套接字的問題,這樣可以避免執行緒膨脹。select模型函式 int select in int nfds,inout fd set readfds,inout fd set...

客戶端公升級為select模型

這裡我們為了讓客戶端有時間去處理其它業務邏輯,因此我們需要在客戶端也引入select模型。define win32 lean and mean include include include pragma comment lib,ws2 32.lib enum cmd struct datahead...