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

Linux 多路轉接 —— poll

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

傳統(tǒng)藝能??

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

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


Linux 多路轉接 —— poll

poll??

poll 也是系統(tǒng)提供的一個多路轉接接口。poll 系統(tǒng)調(diào)用也可以讓程序同時監(jiān)視多個文件描述符上的事件是否就緒,和 select 定位是一樣的,適用場景也是一樣的。

p o l l 函數(shù)的函數(shù)原型如下: \color{red} {poll 函數(shù)的函數(shù)原型如下:} poll函數(shù)的函數(shù)原型如下:

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

fds 為一個 poll 函數(shù)監(jiān)視的結構列表,每一個元素包含三部分內(nèi)容:文件描述符、監(jiān)視的事件集合、就緒的事件集合。nfds 為 fds 數(shù)組長度。timeout 為poll函數(shù)的超時時間,單位是毫秒(ms)。

timeout 可取值:

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

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

在 poll 調(diào)用失敗后會設置錯誤碼,具體分為四種錯誤碼:

EFAULT:fds數(shù)組不包含在調(diào)用程序的地址空間中。
EINTR:此調(diào)用被信號所中斷。
EINVAL:nfds值超過RLIMIT_NOFILE值。
ENOMEM:核心內(nèi)存不足。

struct pollfd??

struct pollfd 結構當中包含三個成員:

fd:特定的文件描述符,若設置為負值則忽略events字段并且revents字段返回0。
events:需要監(jiān)視該文件描述符上的哪些事件。
revents:poll函數(shù)返回時告知用戶該文件描述符上的哪些事件已經(jīng)就緒。
Linux 多路轉接 —— poll
events 和 revents 的取值:

Linux 多路轉接 —— poll
這些取值實際都是以宏的方式進行定義的,它們的二進制序列中有且僅有一個比特位是1,且為 1 的比特位是各不相同的

Linux 多路轉接 —— poll

因此在調(diào)用poll函數(shù)之前,可以通過 | 運算符將要監(jiān)視的事件添加到 events 中。poll 返回后,可以通過 & 運算符檢測 revents 成員中是否包含特定事件,以得知對應文件描述符的特定事件是否就緒

poll 服務器??

poll 工作流程和 select 是基本類似的,這里我們也實現(xiàn)一個簡單poll服務器,該服務器也只是讀取客戶端發(fā)來的數(shù)據(jù)并進行打印。

PollServer類??

PollServer類當中只需要包含監(jiān)聽套接字和端口號兩個成員變量,在 poll 服務器綁定時直接將 IP 地址設置為INADDR_ANY即可。

在構造 PollServer 對象時,需要指明 poll 服務器端口號,當然也可以在初始化 poll 服務器的時候指明。
在初始化 poll 服務器的時候調(diào)用 Socket 類中的函數(shù),依次進行套接字的創(chuàng)建、綁定和監(jiān)聽即可,這里的 Socket 類和之前實現(xiàn)的一樣。
在析構函數(shù)中可以選擇調(diào)用 close 函數(shù)將監(jiān)聽套接字進行關閉,但實際也可以不進行該動作,因為服務器運行后一般是不退出的。
代碼如下:

#pragma once

#include "socket.hpp"
#include <poll.h>

#define BACK_LOG 5

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

運行服務器??

服務器初始化完后開始運行,而 poll 服務器要做的就是不斷調(diào)用 poll 函數(shù),當事件就緒時執(zhí)行動作即可。

首先,在 poll 服務器開始死循環(huán)調(diào)用 poll 函數(shù)之前,需要定義一個 fds 數(shù)組,該數(shù)組當中的每個位置都是一個struct pollfd結構,后續(xù)調(diào)用 poll 函數(shù)時會作為參數(shù)進行傳入。先將 fds 數(shù)組當中每個位置初始化為無效,并將監(jiān)聽套接字添加到 fds 數(shù)組當中,表示服務器剛開始運行時只需要監(jiān)視監(jiān)聽套接字的讀事件。

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

#pragma once

#include "socket.hpp"
#include <poll.h>

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

class PollServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void Run()
	{
		struct pollfd fds[NUM];
		ClearPollfds(fds, NUM, DFL_FD); //清空數(shù)組中的所有位置
		SetPollfds(fds, NUM, _listen_sock); //將監(jiān)聽套接字添加到數(shù)組中,并關心其讀事件
		for (;;){
			switch (poll(fds, NUM, -1)){
			case 0:
				std::cout << "timeout..." << std::endl;
				break;
			case -1:
				std::cerr << "poll error" << std::endl;
				break;
			default:
				//正常的事件處理
				//std::cout<<"有事件發(fā)生..."<<std::endl;
				HandlerEvent(fds, NUM);
				break;
			}
		}
	}
private:
	void ClearPollfds(struct pollfd fds[], int num, int default_fd)
	{
		for (int i = 0; i < num; i++){
			fds[i].fd = default_fd;
			fds[i].events = 0;
			fds[i].revents = 0;
		}
	}
	bool SetPollfds(struct pollfd fds[], int num, int fd)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //該位置沒有被使用
				fds[i].fd = fd;
				fds[i].events |= POLLIN; //添加讀事件到events當中
			return true;
			}
		}
		return false; //fds數(shù)組已滿
	}
};

事件處理??

當 poll 檢測到有文件描述符的讀事件就緒,就會在其對應的 struct pollfd 結構中的 revents 成員中添加讀事件并返回,接下來 poll 服務器就應該對就緒事件進行處理了,事件處理過程如下:

首先遍歷 fds 數(shù)組中的每個 struct pollfd 結構,如果該結構當中的 fd 有效,且 revents 當中包含讀事件,則說明該文件描述符的讀事件就緒,接下來就需要進一步判斷該文件描述符是監(jiān)聽套接字還是與客戶端建立的套接字。

如果是監(jiān)聽套接字的讀事件就緒,則調(diào)用 accept 函數(shù)將底層建立好的連接獲取上來,并將獲取到的套接字添加到 fds 數(shù)組當中,表示下一次調(diào)用 poll 函數(shù)時需要監(jiān)視該套接字的讀事件。

如果是與客戶端建立的連接對應的讀事件就緒,則調(diào)用 read 函數(shù)讀取客戶端發(fā)來的數(shù)據(jù),并將讀取到的數(shù)據(jù)在服務器端進行打印。如果在調(diào)用 read 時發(fā)現(xiàn)客戶端將連接關閉或 read 調(diào)用失敗,則服務器也應關閉對應的連接,并將該連接對應的文件描述符從 fds 數(shù)組當中清除,表示下一次調(diào)用 poll 時無需再監(jiān)視該套接字的讀事件。

#pragma once

#include "socket.hpp"
#include <poll.h>

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

class PollServer{
private:
	int _listen_sock; //監(jiān)聽套接字
	int _port; //端口號
public:
	void HandlerEvent(struct pollfd fds[], int num)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //跳過無效的位置
				continue;
			}
			if (fds[i].fd == _listen_sock&&fds[i].revents&POLLIN){ //連接事件就緒
				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 (!SetPollfds(fds, NUM, sock)){ //將獲取到的套接字添加到fds數(shù)組中,并關心其讀事件
					close(sock);
					std::cout << "poll server is full, close fd: " << sock << std::endl;
				}
			}
			else if (fds[i].revents&POLLIN){ //讀事件就緒
				char buffer[1024];
				ssize_t size = read(fds[i].fd, buffer, sizeof(buffer)-1);
				if (size > 0){ //讀取成功
					buffer[size] = '\0';
					std::cout << "echo# " << buffer << std::endl;
				}
				else if (size == 0){ //對端連接關閉
					std::cout << "client quit" << std::endl;
					close(fds[i].fd);
					UnSetPollfds(fds, i); //將該文件描述符從fds數(shù)組中清除
				}
				else{
					std::cerr << "read error" << std::endl;
					close(fds[i].fd);
					UnSetPollfds(fds, i); //將該文件描述符從fds數(shù)組中清除
				}
			}
		}
	}
private:
	bool SetPollfds(struct pollfd fds[], int num, int fd)
	{
		for (int i = 0; i < num; i++){
			if (fds[i].fd == DFL_FD){ //該位置沒有被使用
				fds[i].fd = fd;
				fds[i].events |= POLLIN; //添加讀事件到events當中
				return true;
			}
		}
		return false; //fds數(shù)組已滿
	}
	void UnSetPollfds(struct pollfd fds[], int pos)
	{
		fds[pos].fd = DFL_FD;
		fds[pos].events = 0;
		fds[pos].revents = 0;
	}
};

因為這里將fds數(shù)組的大小是固定設置的,因此在將新獲取連接對應的文件描述符添加到fds數(shù)組時,可能會因為fds數(shù)組已滿而添加失敗,這時poll服務器只能將剛剛獲取上來的連接對應的套接字進行關閉。

服務器測試??

運行 poll 服務器時也需要先實例化出一個 PollServer 對象,對 poll 服務器進行初始化后就可以運行服務器了:

#include "poll_server.hpp"
#include <string>

static void Usage(std::string proc)
{
	std::cerr << "Usage: " << proc << " port" << std::endl;
}

int main(int argc, char* argv[])
{
	if (argc != 2){
		Usage(argv[0]);
		exit(1);
	}
	int port = atoi(argv[1]);
	PollServer* svr = new PollServer(port);
	svr->InitPollServer();
	svr->Run();
	
	return 0;
}

p o l l 的優(yōu)點: \color{red} {poll 的優(yōu)點:} poll的優(yōu)點:

  1. struct pollfd 結構當中包含了 events 和 revents,相當于將 select 的輸入輸出型參數(shù)進行分離,因此在每次調(diào)用 poll 之前,不需要像 select 一樣重新對參數(shù)進行設置。
  2. poll可監(jiān)控的文件描述符數(shù)量沒有限制。
  3. poll也可以同時等待多個文件描述符,能夠提高IO的效率。

說明一下:

雖然代碼中將 fds 數(shù)組的元素個數(shù)定義為1024,但 fds 數(shù)組的大小是可以繼續(xù)增大的,poll 函數(shù)能夠幫你監(jiān)視多少個文件描述符是由傳入 poll 函數(shù)的第二個參數(shù)決定的。而 fd_set 類型只有1024個比特位,因此 select 函數(shù)最多只能監(jiān)視1024個文件描述符。

p o l l 的缺點: \color{red} {poll 的缺點:} poll的缺點:文章來源地址http://www.zghlxwxcb.cn/news/detail-497557.html

  1. 和 select 一樣,當 poll 返回后,需要遍歷 fds 數(shù)組來獲取就緒的文件描述符。
  2. 每次調(diào)用 poll,都需要把大量的 struct pollfd 結構從用戶態(tài)拷貝到內(nèi)核態(tài),這個開銷也會隨著 poll 監(jiān)視的文件描述符數(shù)目的增多而增大。
  3. 同時每次調(diào)用 poll 都需要在內(nèi)核遍歷傳遞進來的所有fd,這個開銷在 fd 很多時也很大。

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

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

領支付寶紅包贊助服務器費用

相關文章

  • 【Linux】IO多路轉接技術Epoll的使用

    【Linux】IO多路轉接技術Epoll的使用

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

    2024年04月27日
    瀏覽(15)
  • 【Linux】高級IO --- 多路轉接,select,poll,epoll

    【Linux】高級IO --- 多路轉接,select,poll,epoll

    所有通過捷徑所獲取的快樂,無論是金錢、性還是名望,最終都會給自己帶來痛苦 1. 后端服務器最常用的網(wǎng)絡IO設計模式其實就是Reactor,也稱為反應堆模式,Reactor是單進程,單線程的,但他能夠處理多客戶端向服務器發(fā)起的網(wǎng)絡IO請求,正因為他是單執(zhí)行流,所以他的成本就

    2024年02月09日
    瀏覽(26)
  • Linux網(wǎng)絡編程:多路I/O轉接服務器(select poll epoll)

    Linux網(wǎng)絡編程:多路I/O轉接服務器(select poll epoll)

    文章目錄: 一:select 1.基礎API? select函數(shù) 思路分析 select優(yōu)缺點 2.server.c 3.client.c 二:poll 1.基礎API? poll函數(shù)? poll優(yōu)缺點 read函數(shù)返回值 突破1024 文件描述符限制 2.server.c 3.client.c 三:epoll 1.基礎API epoll_create創(chuàng)建? ?epoll_ctl操作? epoll_wait阻塞 epoll實現(xiàn)多路IO轉接思路 epoll優(yōu)缺點

    2024年02月11日
    瀏覽(23)
  • 【網(wǎng)絡】多路轉接——poll | epoll

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

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

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

    多路轉接-epoll/Reactor(2)

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

    2024年04月13日
    瀏覽(18)
  • I/O多路轉接之select

    2023年04月10日
    瀏覽(16)
  • 【網(wǎng)絡】多路轉接——五種IO模型 | select

    【網(wǎng)絡】多路轉接——五種IO模型 | select

    ??作者:一只大喵咪1201 ??專欄:《網(wǎng)絡》 ??格言: 你只管努力,剩下的交給時間! 在學習系統(tǒng)部分的時候,本喵就講解過IO,當時我們學習的IO就是從文件中讀數(shù)據(jù)和寫數(shù)據(jù),到了后來學習網(wǎng)絡的時候,我們知道,從網(wǎng)絡中讀取和寫入數(shù)據(jù)也是IO,那么IO到底是什么呢?今

    2024年02月10日
    瀏覽(24)
  • 服務器(I/O)之多路轉接

    服務器(I/O)之多路轉接

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

    2024年02月09日
    瀏覽(13)
  • 多路轉接方案:select poll epoll 介紹和對比

    多路轉接方案:select poll epoll 介紹和對比

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

    2024年02月08日
    瀏覽(17)
  • I/O多路轉接——epoll服務器代碼編寫

    I/O多路轉接——epoll服務器代碼編寫

    目錄 一、poll? 二、epoll 1.epoll 2.epoll的函數(shù)接口 ①epoll_create ②epoll_ctl ③epoll_wait 3.操作原理 三、epoll服務器編寫 1.日志打印 2.TCP服務器 3.Epoll ①雛形 ②InitEpollServer 與 RunServer ③HandlerEvent 四、Epoll的工作模式 1.LT模式與ET模式 2.基于LT模式的epoll服務器 ①整體框架 ②處理BUG ③優(yōu)

    2024年02月02日
    瀏覽(25)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領取紅包

二維碼2

領紅包