多路轉接Poll

? ? ? ? 在之前我們講過select是最古老的多路轉接方案,古老就意味著他不是很方便使用,他需要用戶手動保存fd_set這個位圖結構,來表示讀寫事件的關注與否或者就緒性。

? ? ? ? 而且由于fd_set的大小是固定的,這就意味著他能管理的套接字文件描述符是有限的。針對這兩個問題,人們研究出來了poll,他一方面不需要用戶手動保存位圖了,還能讓poll管理的文件描述符是無限多的,只要你的內存足夠大。

一、Poll的函數原型

參數說明:
  1. fds
    • 類型:struct pollfd *
    • 作用:指向?pollfd?結構體的數組,每個元素描述一個要監控的文件描述符及其關注的事件。正是由于這傳的參數是一個結構體數組指針,所以理論上只要你的數組足夠大,那么我poll能管理的文件描述符也就越多。
  2. nfds
    • 類型:nfds_t(通常是無符號整數)
    • 作用:數組?fds?中的結構體個數,即要監控的文件描述符數量。
  3. timeout
    • 類型:int
    • 作用:超時時間(毫秒)。
    • 這是一個純粹的輸入型參數,和之前的select不同。
      • timeout > 0:最多等待?timeout?毫秒。
      • timeout == 0:非阻塞模式,立即返回。
      • timeout < 0:無限等待,直到有事件發生。
返回值:
  • > 0:表示有事件發生,返回值為就緒的文件描述符數量。
  • == 0:超時,無任何事件發生。
  • < 0:發生錯誤,返回?-1?并設置?errno

二、Poll的第一個參數是如何做到不用手動保存的呢?

????????可以看到pollfd結構體的成員是這樣的,其中第一個成員表示的是文件描述符的值,而第二個第三個成員則是用戶需要讓內核監控哪些事件、內核返還給用戶哪些事件已經就緒了。

? ? ? ? 之前我們在使用select的時候,fd_set分為讀、寫、和異常,他們三個位圖就管理了所有的文件描述符,所以造就了其復雜性。但是這里把文件描述符的值和事件分開了,一個pollfd就對應一個文件描述符及其關注的事件。從而讓不同文件描述符不用相同的資源,而做到解耦。

三、使用poll的示例

? ? ? ? 這個示例和select的大同小異,僅僅更改select為poll

#pragma once
#include <iostream>
#include "socket.hpp"
#include <memory>
#include<poll.h>
#include "InetAddr.hpp"using namespace socket_ns;class PollServer
{const static int gdefaultfd = -1;const static int gnum=1024;public:PollServer(uint16_t port): _port(port), _listensock(std::make_unique<TcpSocket>()), _timeout(1000){}~PollServer() {}void InitServer(){_listensock->BuildListenSocket(_port);for(int i=0;i<gnum;i++){_events[i].fd=gdefaultfd;_events[i].events=0;_events[i].revents=0;}//把listen添加進來_events[0].fd=_listensock->sockfd();_events[0].events=POLLIN;//對讀事件關心}void Accepter(){// listen套接字得到一個新連接請求-----讀事件就緒// 因為已經就緒了,就不會被阻塞了,即accept不會再等了InetAddr client;SockPtr sock = _listensock->Accepter(&client);if (sock->sockfd() > 0){LOG(DEBUG, "get a new link,client info %s:%d\n", client.Ip().c_str(), client.Port());// 處理(但是這里不能直接處理,如果客戶端不發消息那我仍然阻塞了)// 那么如何得知fd底層的數據是否就緒了呢?仍然是select!這些fd都要由select管理起來// 所以select中的文件描述符會越來越多// 只需要將新獲得的連接套接字放入到fd_array中即可bool flag = false;// 看輔助數組中有沒有空余位置給新fd使用,有則插入for (int pos = 1; pos < gnum; pos++){if (_events[pos].fd == gdefaultfd){flag = true;//將新獲得的套接字,加入到poll管理的pollfd數組中_events[pos].fd = sock->sockfd();_events[pos].events=POLLIN;_events[pos].revents=0;LOG(INFO, "add %d to fd_array success!\n", sock->sockfd());break;}}// 遍歷完成發現是滿的if (flag == false){LOG(WARNING, "Server is Full!\n");// 因為處理不了了,所以直接關閉剛剛獲得到的連接的套接字::close(sock->sockfd());}}}void Handler_IO(int i){char buffer[1024];// 這里讀就不會阻塞了,因為select已經等過了ssize_t n = ::recv(_events[i].fd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;std::cout << "client say#" << buffer << std::endl;// 回復的時候也需要用select找到就緒的,但任何一個sockfd被創建的時候,他的讀寫緩沖區一定是空的,我們之前關心讀事件,是關心// 讀緩沖區有沒有數據,所以讀天然就是不就緒的,但是寫天然是就緒的//  std::string echo_str="[server echo info]";//  echo_str+=buffer;std::string content = "<html><body></h1>hello world</body></html>";std::string echo_str = "HTTP/1.0 200 OK\r\n";echo_str += "Content-Type: text/html\r\n";echo_str += "Cotent-Length:" + std::to_string(content.size()) + "\r\n\r\n";echo_str += content;::send(_events[i].fd, echo_str.c_str(), echo_str.size(), 0);}// 對方把連接關了else if (n == 0){LOG(INFO, "client quit...\n");// 把該文件描述符從fd_array中拿出來::close(_events[i].fd);_events[i].events=0;_events[i].revents=0;_events[i].fd = gdefaultfd;}else{LOG(ERROR, "recv error!\n");}}// 一定有大量的fd就緒,可能是普通sockfd套接字,也可能是linsten套接字void HandlerEvent(){//變量查看合法的fdfor(int i=0;i<gnum;i++){if(_events[i].fd==gdefaultfd){continue;}//合法的文件描述符,判斷是否就緒int fd=_events[i].fd;short revents=_events[i].revents;if(revents&POLLIN){//讀就緒//判斷是listen套接字就緒還是普通文件就緒,進行任務派發if(fd==_listensock->sockfd()){Accepter();}else{Handler_IO(i);}}if(revents&POLLOUT){//寫就緒}}}void Loop(){while (1){// 3.調用selectstruct timeval timeout = {3, 0};// 由于select是一個輸入輸出型參數,所以必須要有一個輔助數組來保存fd信息,用來重置參數int n = ::poll(_events,gnum,_timeout);switch (n){case 0:LOG(DEBUG, "time out\n");break;case -1:LOG(ERROR, "select error\n");break;default:// // 如果事件已經就緒了,但是我沒有做處理,則底層會一直通知我,告訴我有文件描述符就緒了,所以下一次調用select就不會再判斷了,直接通知LOG(INFO, "have event ready,n: %d\n", n);// 處理HandlerEvent();PrintDebug();break;}// sleep(1);}}void PrintDebug(){std::cout << "fd list:" << std::endl;for (int i = 0; i < gnum; i++){if (_events[i].fd=gdefaultfd){continue;}std::cout << _events[i].fd << " ";std::cout << std::endl;}}private:uint16_t _port;std::unique_ptr<Socket> _listensock;struct pollfd _events[gnum];int _timeout;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73392.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73392.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73392.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

多層感知機的簡潔實現

《動手學深度學習》-4.3-筆記 import torch from torch import nn from d2l import torch as d2l 導入必要的庫和模塊 net nn.Sequential(nn.Flatten(),nn.Linear(784, 256),nn.ReLU(),nn.Linear(256, 10))def init_weights(m):if type(m) nn.Linear:nn.init.normal_(m.we…

【GoLang】調用llm時提示詞prompt的介紹以及使用方式

介紹 提示詞是一種與大模型交互的對話格式&#xff0c;它以 JSON 格式定義了一個消息列表&#xff08;messages&#xff09;&#xff0c;包含了系統消息和用戶消息。 我們向AI提問時&#xff0c;其實發給AI的都是提示詞&#xff0c;別看我們只是簡單輸入了一句話&#xff0c;…

內核編程十二:打印task_struct中的數據

在Linux內核中&#xff0c;current 是一個宏&#xff0c;用于獲取當前正在執行的進程的 task_struct 結構體指針。current 宏返回一個指向當前正在運行的進程的 task_struct 結構體的指針。通過這個指針&#xff0c;內核代碼可以訪問和修改當前進程的各種屬性和狀態。 打印單個…

區間端點(java)(貪心問題————區間問題)

deepseek給了一種超級簡單的做法 我是真的想不到 貪心的思路是 局部最優——>全局最優 這種我是真的沒有想到&#xff0c;這樣的好處就是后面便利的時候可以通過foreach循環直接便利qu的子元素也就是對應的某一個區間, 將一個二維數組變成一維數組&#xff0c;每一個一維…

Qt事件處理(處理鼠標事件、鍵盤事件、定時器事件、窗口移動和大小變化事件)

事件處理 事件是應用程序內部或者外部產生的事情或者動作的統稱。 在 Qt 中&#xff0c;事件是用一個對象來管理一個事件的。所有的事件對象都繼承自抽象類 QEvent 。事件包括鼠標事件、鍵盤事件等&#xff0c;發出自 Qt 或操作系統本身。 處理事件一般通過重寫相關的 Event 函…

Apache Hive:基于Hadoop的分布式數據倉庫

Apache Hive 是一個基于 Apache Hadoop 構建的開源分布式數據倉庫系統&#xff0c;支持使用 SQL 執行 PB 級大規模數據分析與查詢。 主要功能 Apache Hive 提供的主要功能如下。 HiveServer2 HiveServer2 服務用于支持接收客戶端連接和查詢請求。 HiveServer2 支持多客戶端…

利用 @eslint/eslintrc 實現 ESLint9的適配

深度解析&#xff1a;利用 eslint/eslintrc 實現 ESLint 的高效配置管理 在前端開發領域&#xff0c;代碼質量和一致性是至關重要的。ESLint 作為一款流行的代碼檢查工具&#xff0c;幫助開發者發現代碼中的潛在問題并保持代碼風格的一致性。而隨著項目的復雜度增加和團隊規模…

cfca 申請國密證書流程

之前給某銀行開發項目&#xff0c;需要用到cfca國密雙證證書&#xff0c;證書類型為企業雙證的作為接口加密的密鑰。 因為是第一次對接&#xff0c;其中走了不少的彎路&#xff0c;現將申請的流程發布出來做下記錄 1、需要找到cfca的相關人員進行測試證書的申請 2、大概1天的…

基于Spring Boot的鄉村養老服務管理系統的設計與實現(LW+源碼+講解)

專注于大學生項目實戰開發,講解,畢業答疑輔導&#xff0c;歡迎高校老師/同行前輩交流合作?。 技術范圍&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬蟲、數據可視化、安卓app、大數據、物聯網、機器學習等設計與開發。 主要內容&#xff1a;…

數字孿生技術如何為制造業開辟新天地?

1. 數字孿生在制造業的崛起背景 1.1 數字孿生的概念演進 “數字孿生”(Digital Twin)一詞最早由美國密歇根大學Michael Grieves博士在2002年提出,但當時并未稱之為“數字孿生”,而是以“信息鏡像模型”描述數字世界與物理世界的映射關系。直到2010年前后,美軍、NASA等在…

學一個前端 UI 框架,要學些什么內容?

假如你現在要自學 React/Vue 框架&#xff0c;怎么學&#xff1f; 絕大部分同學可能是這樣學的&#xff1a; 直接去看官方文檔&#xff0c;或者是找一些視頻看一遍&#xff0c;學會這個框架的一些基礎語法&#xff0c;特性功能等等參考一些例子上手編寫 demo&#xff0c;簡單…

asp.net core mvc模塊化開發

razor類庫 新建PluginController using Microsoft.AspNetCore.Mvc;namespace RazorClassLibrary1.Controllers {public class PluginController : Controller{public IActionResult Index(){return View();}} }Views下Plugin下新建Index.cshtml {ViewBag.Title "插件頁…

2024年MathorCup數學建模C題物流網絡分揀中心貨量預測及人員排班解題全過程文檔加程序

2024年第十四屆MathorCup高校數學建模挑戰賽 C題 物流網絡分揀中心貨量預測及人員排班 原題再現&#xff1a; 電商物流網絡在訂單履約中由多個環節組成&#xff0c;圖1是一個簡化的物流網絡示意圖。其中&#xff0c;分揀中心作為網絡的中間環節&#xff0c;需要將包按照不同流…

鴻蒙Flutter開發故事:不,你不需要鴻蒙化

在華為牽頭下&#xff0c;Flutter 鴻蒙化如火如荼進行&#xff0c;當第一次看到一份上百個插件的Excel 列表時&#xff0c;我也感到震驚&#xff0c;排名前 100 的插件赫然在列&#xff0c;這無疑是一次大規模的軍團作戰。 然后&#xff0c;參戰團隊魚龍混雜&#xff0c;難免有…

Unity音頻混合器如何暴露參數

音頻混合器是Unity推薦管理音效混音的工具&#xff0c;那么如何使用代碼對它進行管理呢&#xff1f; 首先我在AudioMixer的Master組中創建了BGM和SFX的分組&#xff0c;你也可以直接用Master沒有問題。 這里我以BGM為例&#xff0c;如果要在代碼中進行使用就需要將參數暴露出去…

Vue項目與云管平臺Nginx部署筆記

Vue項目與云管平臺Nginx部署筆記 一、項目架構說明 footAdmin云管前端 Vue2 Webpack 構建&#xff0c;部署路徑&#xff1a;/usr/share/nginx/html/footAdmin 使用npm run build生成/dist目錄&#xff0c;然后將dist目錄下面的所有文件&#xff0c;上傳到虛擬機/usr/share/n…

java常用數據轉換

1. List與數組互轉 ArrayList<String> list new ArrayList<>(); String[] array list.stream().toArray(String[]::new); String[] array1 {"apple", "banana", "orange"}; List<String> list1 Arrays.stre…

JAVA學習--java數組--打印稀疏數組和稀疏數組的還原

1.題目描述 2.代碼實現 打印二維數組 public class test04 {public static void main(String args[]){//1.創建一個二維數組11*11&#xff0c;0代表沒有棋子&#xff0c;1代表黑&#xff0c;2代表白棋int[][] array1new int[11][11];array1[1][2]1;array1[2][3]2;//輸出原…

Java 標準注解(內置注解+元注解)的詳細說明及使用場景

以下是 Java 標準注解&#xff08;內置注解&#xff09;的詳細說明及使用場景&#xff1a; 1. 核心標準注解 (1) Override 用途&#xff1a;標記一個方法覆蓋父類的方法或實現接口的抽象方法。約束&#xff1a; 若方法未正確覆蓋/實現&#xff0c;編譯器會報錯。不能用于字段…

使用Python調用Jenkins Api之獲取構建日志使用說明文檔

簡介 通過 Python 腳本自動化獲取 Jenkins 構建日志&#xff0c;可以實現日志分析、錯誤監控、報告生成等功能。本文檔將介紹以下方法&#xff1a; Python-Jenkins 庫&#xff1a;官方推薦的 Python 客戶端庫 日志分頁與流式處理&#xff1a;應對大日志文件場景 準備工作 …