国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Linux 多路轉(zhuǎn)接 —— select

這篇具有很好參考價值的文章主要介紹了Linux 多路轉(zhuǎn)接 —— select。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

傳統(tǒng)藝能??

小編是雙非本科大二菜鳥不贅述,歡迎米娜桑來指點江山哦
Linux 多路轉(zhuǎn)接 —— select
1319365055

????非科班轉(zhuǎn)碼社區(qū)誠邀您入駐????
小伙伴們,滿懷希望,所向披靡,打碼一路向北
一個人的單打獨斗不如一群人的砥礪前行
這是和夢想合伙人組建的社區(qū),誠邀各位有志之士的加入!!
社區(qū)用戶好文均加精(“標兵”文章字數(shù)2000+加精,“達人”文章字數(shù)1500+加精)
直達: 社區(qū)鏈接點我


Linux 多路轉(zhuǎn)接 —— select

select??

回顧一下上一篇,select 是系統(tǒng)提供的一個多路轉(zhuǎn)接接口

select 系統(tǒng)調(diào)用可以讓我們的程序同時監(jiān)視多個文件描述符的事件是否就緒。我們知道select 的核心工作就是等,當監(jiān)視的多個文件描述符中有一個或多個事件就緒時,select 才會成功返回并告知調(diào)用者

函數(shù)原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds 為需要監(jiān)視的文件描述符中最大的文件描述符值 +1;readfds 即輸入輸出型參數(shù),調(diào)用時用戶告知內(nèi)核需要監(jiān)視哪些文件描述符的讀事件是否就緒,返回時內(nèi)核告知用戶哪些事件已經(jīng)就緒;同理 writefds 為寫時間對應(yīng)的參數(shù);exceptfds 為用戶告知內(nèi)核需要監(jiān)視哪些文件描述符的異常事件是否就緒,返回時內(nèi)核告知用戶哪些事件已經(jīng)就緒;timeout 為輸入輸出型參數(shù),由用戶設(shè)置 select 等待時間,返回時表示 timeout 的剩余時間。

參數(shù) timeout 的取值:

  1. NULL/nullptr:select 調(diào)用后進行阻塞等待,直到被監(jiān)視的某個事件就緒。
  2. 0:selec 調(diào)用后進行非阻塞等待,無論被監(jiān)視的事件是否就緒,select 檢測后都會立即返回。
  3. 特定的時間值:select 在指定的時間內(nèi)進行阻塞等待,如果被監(jiān)視的文件描述符上一直沒有事件就緒,過時進行超時返回。

函數(shù)調(diào)用成功,則返回有事件就緒的文件描述符個數(shù);如果 timeout 時間耗盡,則返回 0。調(diào)用失敗則返回 -1,同時錯誤碼會被設(shè)置。

select 調(diào)用失敗時,錯誤碼可能被設(shè)置為

EBADF:文件描述符為無效的或該文件已關(guān)閉
EINTR:此調(diào)用被信號所中斷
EINVAL:nfds 參數(shù)為負值
ENOMEM:核心內(nèi)存不足

fd_set 結(jié)構(gòu)??

fd_set 結(jié)構(gòu)與 sigset_t 結(jié)構(gòu)類似,fd_set 本質(zhì)也是一個位圖,用位圖中對應(yīng)的位來表示要監(jiān)視的文件描述符:

Linux 多路轉(zhuǎn)接 —— select

Linux 多路轉(zhuǎn)接 —— select
調(diào)用 select 之前就需要用 fd_set 定義出對應(yīng)的文件描述符集,然后將需要監(jiān)視的文件描述符添加到文件描述符集當中,這個添加動作本質(zhì)是進行位操作,但這個位操作不需要用戶自己進行,系統(tǒng)提供了一組專門的接口,用于 fd_set 位圖進行各種操作:

void FD_CLR(int fd, fd_set *set); //清除描述詞組set中相關(guān)fd的位
int FD_ISSET(int fd, fd_set *set); //測試描述詞組set中相關(guān)fd的位是否為真Linux 多路轉(zhuǎn)接 —— select

void FD_SET(int fd, fd_set *set); //設(shè)置描述詞組set中相關(guān)fd的位
void FD_ZERO(fd_set *set); //清除描述詞組set的全部位

timeval 結(jié)構(gòu)??

select 函數(shù)的最后一個參數(shù)是 timeout,這是一個指向 timeval 結(jié)構(gòu)的指針,timeval 用于描述一段時間長度,該結(jié)構(gòu)當中包含兩個成員,其中 tv_sec 表示的是秒,tv_usec 表示的是微秒:

Linux 多路轉(zhuǎn)接 —— select

socket 就緒條件??

讀條件??

  1. 接收緩沖區(qū)中的字節(jié)數(shù),≥ 水位標記 SO_RCVLOWAT,此時可以無阻塞的讀取該文件描述符,并且返回值大于0。
  2. socket TCP通信中,對端關(guān)閉連接,此時對該 socket 讀,則返回 0。
  3. 監(jiān)聽的socket上有新的連接請求。
  4. socket 上有未處理的錯誤。

寫就緒??

  1. 發(fā)送緩沖區(qū)中的可用字節(jié)數(shù),大于等于低水位標記SO_SNDLOWAT,此時可以無阻塞的寫,并且返回值大于0。
  2. socket 的寫操作被關(guān)閉(close或者shutdown),對一個關(guān)閉寫操作的 socket 進行寫操作,會觸發(fā)SIGPIPE信號。
  3. socket 使用非阻塞connect連接成功或失敗之后。
  4. socket 上有未讀取的錯誤。

異常就緒??

socket 上收到帶外數(shù)據(jù)(帶外數(shù)據(jù)和 TCP 緊急模式相關(guān),TCP報頭當中 URG 標志位和 16位 緊急指針搭配使用,就能夠發(fā)送/接收帶外數(shù)據(jù))

select 工作流程??

如果想實現(xiàn)一個 select 服務(wù)器,那么我們可以大概知道他的工作流程:

  1. 先初始化服務(wù)器,創(chuàng)建套接字,綁定和監(jiān)聽套接字
  2. 定義一個 fd_array 數(shù)組來保存監(jiān)聽套接字以及已經(jīng)和客戶端建立鏈接的套接字,監(jiān)聽套接字一開始就要加入數(shù)組
  3. 服務(wù)器循環(huán)進行 select 監(jiān)測讀事件是否就緒,如果就緒就可以執(zhí)行對應(yīng)操作
  4. 在 select 之前還應(yīng)該創(chuàng)建一個 readfds 文件描述符集,將 fd_array 的文件描述符放入 readfds ,select 就會對這些文件描述符對應(yīng)的讀事件進行監(jiān)視,在最后就會看到 在 readfds 里面會有一條條的記錄
  5. select 檢測到讀事件就緒就會將其對應(yīng)的文件描述符放進 readfds ,我們就能知道哪些事件已經(jīng)就緒
  6. 如果是讀事件的監(jiān)聽套接字就緒了,就用 accept 從底層全連接隊列獲取已建立的連接,并將對應(yīng)連接的套接字添加到 fd_array 里
  7. 如果是讀事件和客戶端建立連接的套接字,就調(diào)用 read 將讀取到的信息打印出來
  8. 如果讀事件是和客戶端建立連接的套接字就緒,也可能是因為客戶端關(guān)閉了連接,此時服務(wù)器應(yīng)該調(diào)用 close 關(guān)閉套接字,并將該套接字從 fd_array 數(shù)組中清除,因為下一次不需要再監(jiān)視該讀事件了。

因為傳入 select 的 readfds、writefds 和 exceptfds都是輸入輸出型參數(shù),當 select 返回時這些參數(shù)中的值已經(jīng)修改了,因此每次調(diào)用 select 都需要重新設(shè)置,timeout 也是同理。

進行重新設(shè)置,就需要定義一個 fd_array 數(shù)組保存與客戶端已經(jīng)建立的若干連接和監(jiān)聽套接字,實際 fd_array 數(shù)組當中的文件描述符就是需要讓 select 監(jiān)視讀事件的文件描述符。select 服務(wù)器只是讀取客戶端發(fā)來的數(shù)據(jù),因此只需要讓 select 幫我們監(jiān)視特定文件描述符的讀事件,如果要讓 select 同時幫我們監(jiān)視讀事件和寫事件,則需要分別定義 readfds 和 writefds,并定義兩個數(shù)組分別保存需要被監(jiān)視的文件描述符,便于每次調(diào)用 select 前對readfds 和 writefds 進行重新設(shè)置。

服務(wù)器剛開始運行時,fd_array 數(shù)組當中只有監(jiān)聽套接字,因此 select 第一次只需要告知監(jiān)聽套接字的讀事件是否就緒,但每次調(diào)用accept 獲取到新連接后,都會將對應(yīng)的套接字添加到 fd_array 當中,后續(xù) select 就需要監(jiān)視監(jiān)聽套接字和連接套接字的讀事件是否就緒。

由于調(diào)用 select 時還需要傳入被監(jiān)視的文件描述符中最大文件描述符值+1,因此每次在遍歷 fd_array 對 readfds 進行重新設(shè)置時,還需要記錄最大文件描述符值。

select 服務(wù)器實現(xiàn)??

socket 類??

首先編寫一個 Socket 類,對套接字相關(guān)的接口進行一定程度的封裝,為了能夠直接調(diào)用 Socket 類中封裝的函數(shù),我們將這些函數(shù)定義成靜態(tài)成員函數(shù):

#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>

class Socket{
public:
	//創(chuàng)建套接字
	static int SocketCreate()
	{
		int sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0){
			std::cerr << "socket error" << std::endl;
			exit(2);
		}
		//設(shè)置端口復(fù)用
		int opt = 1;
		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
		return sock;
	}
	//綁定
	static void SocketBind(int sock, int port)
	{
		struct sockaddr_in local;
		memset(&local, 0, sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = htons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		socklen_t len = sizeof(local);

		if (bind(sock, (struct sockaddr*)&local, len) < 0){
			std::cerr << "bind error" << std::endl;
			exit(3);
		}
	}
	//監(jiān)聽
	static void SocketListen(int sock, int backlog)
	{
		if (listen(sock, backlog) < 0){
			std::cerr << "listen error" << std::endl;
			exit(4);
		}
	}
};

SelectServer 類??

因為當前使用的是云服務(wù)器,所以編寫的 select 服務(wù)器在綁定時不需要顯示綁定IP地址,直接將IP地址設(shè)置為 INADDR_ANY 就行了,所以類當中只包含監(jiān)聽套接字和端口號兩個成員變量。

在構(gòu)造 SelectServer 對象時,需要指明 select 服務(wù)器的端口號,當然也可以在初始化 select 服務(wù)器的時候指明。初始化 select 服務(wù)器的時候調(diào)用 Socket 類當中的函數(shù),依次進行套接字的創(chuàng)建、綁定和監(jiān)聽即可。在析構(gòu)函數(shù)中可以選擇調(diào)用 close 函數(shù)將監(jiān)聽套接字進行關(guān)閉,但實際也可以不用,因為服務(wù)器運行后一般是不退出的:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5

class SelectServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	SelectServer(int port)
		: _port(port)
	{}
	void InitSelectServer()
	{
		_listen_sock = Socket::SocketCreate();
		Socket::SocketBind(_listen_sock, _port);
		Socket::SocketListen(_listen_sock, BACK_LOG);
	}
	~SelectServer()
	{
		if (_listen_sock >= 0){
			close(_listen_sock);
		}
	}
};

運行服務(wù)器??

服務(wù)器初始化完畢就應(yīng)該周期性的執(zhí)行某種動作了,而服務(wù)器要做的就是不斷調(diào)用 select ,當事件就緒時執(zhí)行對應(yīng)某種動作即可。

首先,在 select 服務(wù)器開始死循環(huán)調(diào)用 select 函數(shù),需要先定義一個 fd_array 數(shù)組,先把所有位置初始化為無效,并將監(jiān)聽套接字添加到該數(shù)組當中,fd_array 數(shù)組中保存的就是需要被select監(jiān)視讀事件是否就緒的文件描述符

此后,服務(wù)器就不斷調(diào)用 select 監(jiān)視讀事件是否就緒,每次調(diào)用 select 之前都需要重新設(shè)置 readfds,具體設(shè)置過程就是遍歷 fd_array 數(shù)組的文件描述符添加到 readfds 中,并同時記錄最大的文件描述符值 maxfd,因為后續(xù)調(diào)用 select 時需要將maxfd+1作為第一個參數(shù)傳入。

當 select 函后,如果返回值為0,則說明 timeout 時間耗盡,直接進行下一次 select 調(diào)用即可;如果返回值為 -1,則說明 select 調(diào)用失敗,此時讓服務(wù)器準備進行下一次 select 調(diào)用,但實際應(yīng)該進一步判斷錯誤碼,根據(jù)錯誤碼來判斷是否應(yīng)該繼續(xù)調(diào)用 select 。如果 select 返回值大于 0,則說明 select 函數(shù)調(diào)用成功,此時已經(jīng)有文件描述符的讀事件就緒,接下來就應(yīng)該對就緒事件進行處理:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class SelectServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void Run()
	{
		fd_set readfds; //讀文件描述符集
		int fd_array[NUM]; //被監(jiān)視讀事件是否就緒的文件描述符
		ClearFdArray(fd_array, NUM, DFL_FD); //將數(shù)組中的所有位置設(shè)置為無效
		fd_array[0] = _listen_sock; //將監(jiān)聽套接字添加到fd_array數(shù)組中的第0個位置
		for (;;){
			FD_ZERO(&readfds); //清空readfds
			//將fd_array數(shù)組當中的文件描述符添加到readfds當中,并記錄最大的文件描述符
			int maxfd = DFL_FD;
			for (int i = 0; i < NUM; i++){
				if (fd_array[i] == DFL_FD) //跳過無效的位置
					continue;
				FD_SET(fd_array[i], &readfds); //將有效位置的文件描述符添加到readfds當中
				if (fd_array[i] > maxfd) //更新最大文件描述符
					maxfd = fd_array[i];
			}
			switch (select(maxfd + 1, &readfds, nullptr, nullptr, nullptr)){
				case 0:
					std::cout<<"timeout..."<<std::endl;
					break;
				case -1:
					std::cerr << "select error" << std::endl;
					break;
				default:
					//正常的事件處理
					std::cout<<"有事件發(fā)生..."<<std::endl;
					//HandlerEvent(readfds, fd_array, NUM);
					break;
			}//end switch
		}//end for
	}
private:
	void ClearFdArray(int fd_array[], int num, int default_fd)
	{
		for (int i = 0; i < num; i++){
			fd_array[i] = default_fd;
		}
	}
};

事件處理??

當 select 檢測到文件描述符的讀事件就緒并成功返回后,就應(yīng)該對事件進行處理了,這里編寫一個 HandlerEvent 函數(shù)

在處理時需要遍歷 fd_array 數(shù)組的文件描述符,依次判斷各個文件描述符對應(yīng)的讀事件是否就緒,如果就緒則需要進行事件處理。當一個讀事件就緒后,還需要進一步判斷該文件描述符是否是監(jiān)聽套接字,如果是監(jiān)聽套接字就應(yīng)該調(diào)用 accept 函數(shù)將底層的連接獲取上來。但是只調(diào)用 accept 將連接獲取上來還不夠,為了下一次調(diào)用 select 時能讓 select 幫我們監(jiān)視新連接的讀事件是否就緒,在連接獲取上來后還應(yīng)該將該連接對應(yīng)的文件描述符添加到 fd_array 數(shù)組當中,這樣在下一次調(diào)用 select 前對 readfds 重新設(shè)置時就能將該文件描述符添加進去了。

如果是與客戶端建立連接的讀事件就緒,那么就應(yīng)該調(diào)用 read 函數(shù)讀取客戶端發(fā)來的數(shù)據(jù),讀取成功則進行打印。如果讀取失敗或者客戶端關(guān)閉連接,那么 select 服務(wù)器應(yīng)該調(diào)用 close 函數(shù)關(guān)閉對應(yīng)連接,但此時只關(guān)閉連接也是不夠的,還應(yīng)該將該連接對應(yīng)的文件描述符從 fd_array 數(shù)組當中清除,否則后續(xù)調(diào)用的 select 還會繼續(xù)監(jiān)視該連接的讀事件是否就緒。

代碼如下:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class SelectServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void HandlerEvent(const fd_set& readfds, int fd_array[], int num)
	{
		for (int i = 0; i < num; i++){
			if (fd_array[i] == DFL_FD){ //跳過無效的位置
				continue;
			}
			if (fd_array[i] == _listen_sock&&FD_ISSET(fd_array[i], &readfds)){ //連接事件就緒
				//獲取連接
				struct sockaddr_in peer;
				memset(&peer, 0, sizeof(peer));
				socklen_t len = sizeof(peer);
				int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
				if (sock < 0){ //獲取連接失敗
					std::cerr << "accept error" << std::endl;
					continue;
				}
				std::string peer_ip = inet_ntoa(peer.sin_addr);
				int peer_port = ntohs(peer.sin_port);
				std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;

				if (!SetFdArray(fd_array, num, sock)){ //將獲取到的套接字添加到fd_array當中
					close(sock);
					std::cout << "select server is full, close fd: " << sock << std::endl;
				}
			}
			else if (FD_ISSET(fd_array[i], &readfds)){ //讀事件就緒
				char buffer[1024];
				ssize_t size = read(fd_array[i], buffer, sizeof(buffer)-1);
				if (size > 0){ //讀取成功
					buffer[size] = '\0';
					std::cout << "echo# " << buffer << std::endl;
				}
				else if (size == 0){ //對端連接關(guān)閉
					std::cout << "client quit" << std::endl;
					close(fd_array[i]);
					fd_array[i] = DFL_FD; //將該文件描述符從fd_array中清除
				}
				else{
					std::cerr << "read error" << std::endl;
					close(fd_array[i]);
					fd_array[i] = DFL_FD;
				}
			}
		}
	}
private:
	bool SetFdArray(int fd_array[], int num, int fd)
	{
		for (int i = 0; i <num; i++){
			if (fd_array[i] == DFL_FD){ //該位置沒有被使用
				fd_array[i] = fd;
				return true;
			}
		}
		return false; //fd_array數(shù)組已滿
	}
};

當調(diào)用 accept 從底層獲取上來連接后,不能立即調(diào)用 read 讀取連接當中的數(shù)據(jù),因為此時新連接中的數(shù)據(jù)可能并沒有就緒,如果直接調(diào)用read 可能需要進行阻塞等待,我們應(yīng)該將等待過程交給 select 來完成,因此獲取完連接后應(yīng)該直接將該連接對應(yīng)的文件描述符添加到 fd_array 數(shù)組當中就行了,當該連接的讀事件就緒時 select 會告知我們,再進行數(shù)據(jù)讀取就不會被阻塞了。

添加文件描述符到 fd_array 數(shù)組當中,本質(zhì)就是遍歷 fd_array 數(shù)組,找到一個沒有被使用的位置將該文件描述符添加進去即可。但有可能 fd_array 數(shù)組中全部的位置都已經(jīng)被占用了,那么文件描述符就會添加失敗,此時就只能將剛剛獲取上來的連接對應(yīng)的套接字進行關(guān)閉,因為此時服務(wù)器沒有能力處理這個連接。

至此 select 服務(wù)器編寫完畢,用 telnet 工具連接我們的服務(wù)器,此時通過 telnet 向服務(wù)器發(fā)送的數(shù)據(jù)就能夠被服務(wù)器讀到并且打印輸出了。
此外,雖然當前 select 服務(wù)器是一個單進程的服務(wù)器,但它卻可以同時為多個客戶端提供服務(wù),根本原因就是因為 select 函數(shù)調(diào)用后會告知 select 服務(wù)器哪個客戶端對應(yīng)的連接事件就緒了,此時 select 服務(wù)器就可以讀取發(fā)來的數(shù)據(jù),讀取完后又會調(diào)用 select 等待某個客戶端連接的讀事件就緒。

一些問題??

當前的 select 服務(wù)器還存在一些問題:

select 服務(wù)器如果要向客戶端發(fā)送數(shù)據(jù),不能直接調(diào)用write函數(shù),因為調(diào)用write函數(shù)時實際也分為 “等” 和“拷貝” 兩步,我們也應(yīng)該將 “等” 的這個過程交給 select ,因此在每次調(diào)用 select 之前,除了需要重新設(shè)置 readfds 還需要重新設(shè)置writefds,并且還需要一個數(shù)組來保存需要被監(jiān)視寫事件是否就緒的文件描述符,就緒時我們才能夠調(diào)用 write 向客戶端發(fā)送數(shù)據(jù)。

其次沒有定制協(xié)議。代碼中讀取數(shù)據(jù)時并沒有按照某種規(guī)則進行讀取,此時就可能造成粘包問題,根本原因就是因為我們沒有定制協(xié)議,比如HTTP協(xié)議規(guī)定在讀取底層數(shù)據(jù)時讀取到空行就表明讀完了一個HTTP報頭,此時再根據(jù)HTTP報頭當中的 Content-Length 屬性得知正文的長度,最終就能夠讀取到一個完整的HTTP報文。

沒有對應(yīng)的輸入輸出緩沖區(qū)。代碼中直接將讀取的數(shù)據(jù)存儲到了字符數(shù)組 buffer 當中,這是不嚴謹?shù)模驗楸敬螖?shù)據(jù)讀取可能并沒有讀取到一個完整的報文,此時服務(wù)器就不能進行數(shù)據(jù)的分析處理,應(yīng)該將讀取到的數(shù)據(jù)存儲到一個輸入緩沖區(qū)當中,當讀取到一個完整的報文后再讓服務(wù)器進行處理。此外服務(wù)器的響應(yīng)數(shù)據(jù)也不應(yīng)該直接調(diào)用 write 發(fā)送給客戶端,應(yīng)該先存儲到一個輸出緩沖區(qū)當中,因為數(shù)據(jù)可能很龐大,無法一次發(fā)送完畢,可能需要進行分批發(fā)送。

select 優(yōu)點??

當然,這也是所有多路轉(zhuǎn)接接口的優(yōu)點:

  1. 可以同時等待多個文件描述符,并且只負責(zé)等待,實際的IO操作由 accept、read、write 等接口來完成,這些接口在進行IO操作時不會被阻塞。
  2. select 可同時等待多個文件描述符,將“等”的時間重疊提高了IO的效率。

select 缺點??

  1. 每次調(diào)用 select 都需要手動設(shè)置 fd 集合,非常不便。
  2. 每次調(diào)用 select 都需要把 fd 集合從用戶態(tài)拷貝到內(nèi)核態(tài),這個開銷在 fd 很多時會很大。
  3. 每次調(diào)用 select 都需要遍歷傳遞進來的所有 fd,這個開銷在 fd 很多時也很大。
  4. select 可監(jiān)控的文件描述符數(shù)量太少。

這里我細嗦一下 select 的文件描述符數(shù)量,調(diào)用 select 時的 readfds、writefds以及exceptfds 都是fd_set結(jié)構(gòu),fd_set 結(jié)構(gòu)本質(zhì)是一個位圖,它用每一個比特位來標記一個文件描述符,因此 select 可監(jiān)控的文件描述符個數(shù)是取決于 fd_set 的比特位個數(shù)。

我們可以計算一下 fd_size 的比特位大小:

#include <iostream>
#include <sys/types.h>

int main()
{
	std::cout << sizeof(fd_set)* 8 << std::endl;
	return 0;
}

結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為 1024 個 \color{red} {結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為 1024 個} 結(jié)果很明顯,可監(jiān)控的文件描述符數(shù)量為1024

Linux 多路轉(zhuǎn)接 —— select

適用場景??

多路轉(zhuǎn)接接口的 select,poll epoll 都有自己適用的場景,在不恰當?shù)膱龊鲜褂媚硞€接口只會適得其反。

多路轉(zhuǎn)接接口一般適用于多連接,且多連接中只有少部分連接比較活躍。因為少量連接比較活躍,也就意味著幾乎所有的連接在進行IO操作時,都需要花費大量時間來等待事件就緒,此時使用多路轉(zhuǎn)接接口就可以將這些等的事件進行重疊,提高IO效率。對于多連接中大部分連接都很活躍的場景,就不適合多路轉(zhuǎn)接了。因為每個連接都很活躍,也就意味著任何時刻事件基本都是就緒的,此時根本不需要動用多路轉(zhuǎn)接接口來幫我們進行等待,畢竟使用多路轉(zhuǎn)接接口需要花費系統(tǒng)時間和空間資源。

多連接中只有少量連接是比較活躍的,比如聊天工具,我們登錄QQ后大部分時間其實是沒有聊天的,此時服務(wù)端不可能調(diào)用一個 read 來阻塞等待讀事件就緒。

多連接中大部分連接都很活躍,比如企業(yè)當中進行數(shù)據(jù)備份時,兩臺服務(wù)器之間不斷在交互數(shù)據(jù),這時的連接是特別活躍的,幾乎不需要等的過程,也就沒必要使用多路轉(zhuǎn)接接口了。文章來源地址http://www.zghlxwxcb.cn/news/detail-482634.html

到了這里,關(guān)于Linux 多路轉(zhuǎn)接 —— select的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 多路轉(zhuǎn)接方案:select poll epoll 介紹和對比

    多路轉(zhuǎn)接方案:select poll epoll 介紹和對比

    內(nèi)存和外設(shè)的交互叫做IO,網(wǎng)絡(luò)IO就是將數(shù)據(jù)在內(nèi)存和網(wǎng)卡間拷貝。 IO本質(zhì)就是等待和拷貝,一般等待耗時往往遠高于拷貝耗時。所以提高IO效率就是盡可能減少等待時間的比重。 IO模型 簡單對比解釋 阻塞IO 阻塞等待數(shù)據(jù)到來 非阻塞IO 輪詢等待數(shù)據(jù)到來 信號驅(qū)動 信號遞達時

    2024年02月08日
    瀏覽(17)
  • 多路轉(zhuǎn)接高性能IO服務(wù)器|select|poll|epoll|模型詳細實現(xiàn)

    多路轉(zhuǎn)接高性能IO服務(wù)器|select|poll|epoll|模型詳細實現(xiàn)

    那么這里博主先安利一下一些干貨滿滿的專欄啦! Linux專欄 https://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014.3001.5482 操作系統(tǒng)專欄 https://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482 手撕數(shù)據(jù)結(jié)構(gòu) https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482 去倉庫獲

    2024年02月15日
    瀏覽(26)
  • Linux 多路轉(zhuǎn)接 —— poll

    Linux 多路轉(zhuǎn)接 —— poll

    小編是雙非本科大二菜鳥不贅述,歡迎米娜桑來指點江山哦 1319365055 ????非科班轉(zhuǎn)碼社區(qū)誠邀您入駐???? 小伙伴們,滿懷希望,所向披靡,打碼一路向北 一個人的單打獨斗不如一群人的砥礪前行 這是和夢想合伙人組建的社區(qū),誠邀各位有志之士的加入?。?社區(qū)用戶好文

    2024年02月10日
    瀏覽(18)
  • 【Linux】多路轉(zhuǎn)接 -- epoll

    【Linux】多路轉(zhuǎn)接 -- epoll

    epoll系統(tǒng)調(diào)用和select以及poll是一樣的,都是可以讓我們的程序同時監(jiān)視多個文件描述符上的事件是否就緒。 epoll在命名上比poll多了一個poll,這個e可以理解為extend, epoll就是為了同時處理大量文件描述符而改進的poll。 epoll在2.5.44內(nèi)核中被引進,它幾乎具備了select和poll的所有

    2024年02月14日
    瀏覽(16)
  • 【Linux】I/O多路轉(zhuǎn)接技術(shù)

    【Linux】I/O多路轉(zhuǎn)接技術(shù)

    前面我們學(xué)習(xí)的非阻塞IO,雖然能夠在數(shù)據(jù)不就緒的時候處理其他事情,但是還是有一些不方便,而且每次都要為了一個文件描述符而進行等待,所以為了提高IO效率我們還要學(xué)習(xí)IO多路轉(zhuǎn)接技術(shù)。 select 是系統(tǒng)提供的一個多路轉(zhuǎn)接接口。 函數(shù)原型: 功能: select 系統(tǒng)調(diào)用可以

    2024年02月04日
    瀏覽(16)
  • 【Linux】IO多路轉(zhuǎn)接——poll接口

    【Linux】IO多路轉(zhuǎn)接——poll接口

    目錄 poll初識 poll函數(shù) poll服務(wù)器 poll的優(yōu)點 poll的缺點 poll也是系統(tǒng)提供的一個多路轉(zhuǎn)接接口。 poll系統(tǒng)調(diào)用也可以讓我們的程序同時監(jiān)視多個文件描述符上的事件是否就緒,和select的定位是一樣的,適用場景也是一樣的。 poll函數(shù) poll函數(shù)的函數(shù)原型如下: 參數(shù)說明: fds:一

    2024年02月12日
    瀏覽(18)
  • 【Linux】IO多路轉(zhuǎn)接技術(shù)Epoll的使用

    【Linux】IO多路轉(zhuǎn)接技術(shù)Epoll的使用

    ? 在學(xué)習(xí) epoll 之前,我們首先了解一下Linux中的多路復(fù)用技術(shù): 在Linux系統(tǒng)中, IO多路復(fù)用 是一種重要的技術(shù),它允許一個進程同時監(jiān)視多個文件描述符,一旦某個描述符準備好進行讀取(通常是讀就緒或?qū)懢途w),內(nèi)核會通知該進程進行相應(yīng)的讀寫操作。這樣,我們可以

    2024年04月27日
    瀏覽(15)
  • 【網(wǎng)絡(luò)】多路轉(zhuǎn)接——poll | epoll

    【網(wǎng)絡(luò)】多路轉(zhuǎn)接——poll | epoll

    ??作者:一只大喵咪1201 ??專欄:《網(wǎng)絡(luò)》 ??格言: 你只管努力,剩下的交給時間! 書接上文五種IO模型 | select。 poll 也是一種多路轉(zhuǎn)接的方案,它專門用來解決 select 的兩個問題: 等待fd有上限的問題。 每次調(diào)用都需要重新設(shè)置 fd_set 的問題。 如上圖所示便是 poll 系統(tǒng)調(diào)

    2024年02月10日
    瀏覽(24)
  • 多路轉(zhuǎn)接-epoll/Reactor(2)

    多路轉(zhuǎn)接-epoll/Reactor(2)

    上次說到了poll,它存在效率問題,因此出現(xiàn)了改進的poll----epoll。 目前epoll是公認的效率最高的多路轉(zhuǎn)接的方案。 ?epoll_create: 這個參數(shù)其實已經(jīng)被廢棄了。?這個值只要大于0就可以了。 ?這是用來創(chuàng)建一個epoll模型的。 創(chuàng)建成功了就返回一個文件描述符。失敗了返回-1 epo

    2024年04月13日
    瀏覽(18)
  • 服務(wù)器(I/O)之多路轉(zhuǎn)接

    服務(wù)器(I/O)之多路轉(zhuǎn)接

    1、阻塞等待:在內(nèi)核將數(shù)據(jù)準備好之前,系統(tǒng)調(diào)用會一直等待。所有的套接字,默認都是阻塞方式。 2、非阻塞等待:如果內(nèi)核沒有將數(shù)據(jù)準備好,系統(tǒng)調(diào)用仍然會返回,并且會返回EWUOLDBLOCK或者EAGAIN錯誤碼。 3、信號驅(qū)動:內(nèi)核將數(shù)據(jù)準備好的時候,使用SIGIO信號通知應(yīng)用程

    2024年02月09日
    瀏覽(13)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包