IO多路復用(select/epoll)

目錄

一、概念

二、語法

1.select

1.1 select函數的語法

1.2 文件描述符集合操作

1.3 select函數的優缺點

2.epoll

2.1 epoll語法

2.2 epoll的工作模式

2.3 epoll的優缺點

三、select服務端代碼

四、epoll服務端代碼

五、客戶端代碼


?

一、概念

IO多路復用是一種同步的I/O模型,它允許一個進程同時監視多個文件描述符,一旦某個文件描述符就緒(可以進行I/O操作),就能夠通知程序進行相應的讀寫操作。

IO多路復用有三種實現方式:select、poll、epoll。


?

二、語法

1.select

select是最早的I/O多路復用技術之一,它使用fd_set數據結構來存儲和跟蹤文件描述符。

select是基于線性方式處理待檢測集合的,因此每次都要遍歷集合;對于返回的集合,還需要判斷文件描述符是否就緒。

?

1.1 select函數的語法

select ( int nfds,

? ? ? ? ? ? fd_set *readfds,
? ? ? ? ? ? fd_set *writefds,
? ? ? ? ? ? fd_set *exceptfds,
? ? ? ? ? ? struct timeval *timeout );

  • nfds:監控的文件描述符集里最大文件描述符加1。
  • readfds:文件描述符集合,內核會監視此集合中的文件是否有數據可讀,傳入傳出參數。
  • writefds:文件描述符集合,內核會監視此集合中的文件是否有數據可寫,傳入傳出參數。
  • exceptfds:文件描述符集合,用于監視此集合中的文件是否有異常情況,傳入傳出參數。
  • timeout:設置為NULL:阻塞,若檢測到就緒的文件描述符則返回其數量;設置時間:等待固定時間,若沒有就緒的文件描述符則返回0。

?

1.2 文件描述符集合操作

  • FD_ZERO(fd_set *set):清空文件描述符集合。
  • FD_SET(int fd, fd_set *set):將文件描述符fd添加到集合set中。
  • FD_CLR(int fd, fd_set *set):從集合set中移除文件描述符fd。
  • FD_ISSET(int fd, fd_set *set):檢查文件描述符fd是否在集合set中。

?

1.3 select函數的優缺點

  • 優點:適用于多種操作系統,具有較好的兼容性。
  • 缺點:當文件描述符數量較多時,效率會比較低。此外,select有一個固定的文件描述符數量限制,通常為1024。

?

2.epoll

epoll是一種高效且可擴展的I/O多路復用技術。epoll是基于紅黑樹來處理待檢測集合的,使用回調機制,處理效率高;其返回的集合中的文件描述符都是就緒的,無需再次進行檢測。

epoll主要通過以下三個函數來實現其功能:epoll_create、epoll_ctl、epoll_wait。

?

2.1 epoll語法

①epoll_create函數

epoll_create (int size);

創建一個epoll對象,返回一個文件描述符,用于后續的操作,參數size大于0即可。

②epoll_ctl函數

epoll_ctl ( int epfd, int op, int fd,
? ? ? ? ? ? ? ?struct epoll_event *event );

向epoll對象中添加、修改或刪除文件描述符。

  • epfd:epoll_create函數返回的epoll文件描述符。
  • op:操作類型,EPOLL_CTL_ADD(添加)、EPOLL_CTL_MOD(修改)或EPOLL_CTL_DEL(刪除)。
  • fd:要監聽的文件描述符。
  • event:指向epoll_event結構體的指針,用于指定要監聽的事件類型。
epoll_event和epoll_data:

struct epoll_event
{
? uint32_t events;
? epoll_data_t data;
};

typedef union epoll_data
{
? void *ptr;
? int fd;
? uint32_t u32;
? uint64_t u64;
};

epoll_event結構體用于描述事件,成員events表示事件類型:

  • EPOLLIN:讀事件,接收數據,檢測讀緩沖區,如果可讀則該文件描述符已就緒。
  • EPOLLOUT:寫事件,發送數據,檢測寫緩沖區,如果可寫則該文件描述符已就緒。
  • EPOLLET:設置為邊沿模式,默認是水平模式。

epoll_data為聯合體,通常使用int類型的fd,用于存儲發生對應事件的文件描述符。

?③epoll_wait函數

epoll_wait ( int epfd, struct epoll_event *events,
? ? ? ? ? ? ?? int maxevents, int timeout );

等待epoll對象中的事件發生,返回發生事件的文件描述符集合。

  • epfd:epoll_create函數返回的epoll文件描述符。
  • events:一個數組,用來存儲就緒的事件信息。
  • maxevents:數組events的大小,即最多可以返回多少個就緒的文件描述符。
  • timeout:表示epoll_wait阻塞等待的時間(毫秒)。若為負數則會阻塞,直到有事件就緒;若為0則不阻塞,立即返回當前已就緒的事件。

?

2.2 epoll的工作模式

①水平觸發(LT)模式:

水平觸發是epoll的默認工作模式。在這種模式下,當文件描述符上的事件就緒時,epoll_wait函數會返回,并且如果該事件沒有被處理,epoll_wait函數會在下一次調用時再次返回,直到該事件被處理。例如,當一個套接字上有數據可讀時,epoll_wait會返回,并且如果數據沒有被讀取,epoll_wait會在下一次調用時再次返回,直到數據被讀取。

②邊沿觸發(ET)模式:

邊沿觸發模式下,epoll_wait函數只會在文件描述符的狀態發生變化時返回。例如,當一個套接字上有數據可讀時,epoll_wait會返回,但是如果數據沒有被讀取,epoll_wait不會在下一次調用時再次返回,直到有新的數據到達。因此在邊沿觸發模式下,程序需要在一次epoll_wait調用返回后,立即處理所有就緒的事件。

③總結:
  • 邊沿觸發模式通常比水平觸發模式的性能更高,因為它調用epoll_wait函數的次數比較少。
  • 水平觸發模式更適合處理阻塞式I/O,而邊沿觸發模式更適合處理非阻塞式I/O。

epoll在邊沿模式下,必須將套接字設置為非阻塞模式,此時需要循環讀取讀緩沖區的數據,讀取完后recv函數會返回-1,此時需要特殊處理。

?

2.3 epoll的優缺點

  • 優點:高效的事件驅動模型,支持大量并發連接,沒有固定的文件描述符數量限制。
  • 缺點:僅適用于Linux系統,不具備跨平臺性。

?

三、select服務端代碼

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>using namespace std;int main(int argc, char* argv[])
{int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = INADDR_ANY;if (bind(lfd, (struct sockaddr*)&local, sizeof(local)) == -1) {cerr << "error" << endl;return -1;}if (listen(lfd, 128) == -1) {cerr << "error" << endl;return -1;}fd_set readset;FD_ZERO(&readset);FD_SET(lfd, &readset);int maxfd = lfd;while (true) {fd_set tmp = readset;int ret = select(maxfd + 1, &tmp, NULL, NULL, NULL);if (FD_ISSET(lfd, &tmp)) {int cfd = accept(lfd, NULL, NULL);FD_SET(cfd, &readset);maxfd = max(maxfd, cfd);cout << "connect: " << cfd << endl;}for (int i = 0; i <= maxfd; i++) {if (i != lfd && FD_ISSET(i, &tmp)) {char buffer[1024] = { 0 };int len = recv(i, buffer, sizeof buffer, 0);if (len == -1) {cerr << "error" << endl;return -1;}else if (len == 0) {cout << "client disconnect: " << i << endl;FD_CLR(i, &readset);close(i);break;}cout << buffer << endl;for (int i = 0; i < len; i++) {buffer[i] = toupper(buffer[i]);}len = send(i, buffer, strlen(buffer) + 1, 0);if (len == -1) {cerr << "error" << endl;return -1;}}}}close(lfd);return 0;
}

?

四、epoll服務端代碼

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/epoll.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>using namespace std;int main(int argc, char* argv[])
{int lfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in local;local.sin_family = AF_INET;local.sin_port = htons(8080);local.sin_addr.s_addr = INADDR_ANY;if (bind(lfd, (struct sockaddr*)&local, sizeof(local)) == -1) {cerr << "error" << endl;return -1;}if (listen(lfd, 128) == -1) {cerr << "error" << endl;return -1;}int epfd = epoll_create(1);struct epoll_event event;event.events = EPOLLIN;event.data.fd = lfd;epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);struct epoll_event events[1024];while (true) {int fds = epoll_wait(epfd, events, 1024, -1);for (int i = 0; i < fds; i++) {int fd = events[i].data.fd;if (fd == lfd) {int cfd = accept(lfd, NULL, NULL);struct epoll_event client_event;int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;fcntl(cfd, F_SETFL, flag);client_event.events = EPOLLIN | EPOLLET;client_event.data.fd = cfd;epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &client_event);cout << "connect: " << cfd << endl;}else {while(true) {char buffer[128] = { 0 };int len = recv(fd, buffer, sizeof buffer, 0);if (len == 0) {cout << "client disconnect: " << fd << endl;epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);close(fd);break;}else if (len > 0) {cout << buffer << endl;for (int i = 0; i < len; i++) {buffer[i] = toupper(buffer[i]);}len = send(fd, buffer, strlen(buffer) + 1, 0);if (len == -1) {cerr << "error" << endl;return -1;}}else if (len == -1) {if (errno = EAGAIN) {cout << "Data read complete." << endl;break;}else {cerr << "error" << endl;return -1;}}}}}}close(lfd);return 0;
}

?

五、客戶端代碼

#include<iostream>
#include<cstring>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>using namespace std;int main(void)
{int client_socket = socket(AF_INET, SOCK_STREAM, 0);if (client_socket == -1) {cerr << "error" << endl;return -1;}struct sockaddr_in target;target.sin_family = AF_INET;target.sin_port = htons(8080);inet_pton(AF_INET, "127.0.0.1", &target.sin_addr.s_addr);if (connect(client_socket, (struct sockaddr*)&target, sizeof target) == -1) {cerr << "error" << endl;close(client_socket);return -1;}while (true) {char buffer1[1024] = { 0 };cout << "enter: ";cin >> buffer1;send(client_socket, buffer1, strlen(buffer1), 0);char buffer2[1024] = { 0 };int ret = recv(client_socket, buffer2, sizeof buffer2, 0);if (ret <= 0) {cout << "server disconnect." << endl;}cout << buffer2 << endl;}close(client_socket);return 0;
}

?

參考內容:
愛編程的大丙

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

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

相關文章

android stdudio環境: gradle一直安裝失敗

一、一直顯示如下錯誤 The specified Gradle distribution file:/home/wangqingyuan/.gradle/wrapper/dists/gradle-8.6-bin/gradle-8.6-bin.zip does not exist. 經分析&#xff0c;是因為應用本身設置了gradle版本的地址為本地&#xff1a; 應用目錄&#xff1a;gradle/gra…

解決PS 撤銷卡頓

1. 關閉Windows Ink - 打開觸控筆設置 - 禁用Windows Ink功能 2. 創建 PSUserConfig.txt&#xff08;注意Win10/11 可能隱藏文件擴展名&#xff09; - 位置&#xff1a;C:\Users\[用戶名]\AppData\Roaming\Adobe\Adobe Photoshop CC 2019\Adobe Photoshop CC 2019 Se…

spring默認線程池SimpleAsyncTaskExecutor特點為什么要盡量避免使用

在 Spring Boot 中&#xff0c;默認的線程池配置由 TaskExecutionAutoConfiguration 類提供&#xff0c;使用的是 SimpleAsyncTaskExecutor。 SimpleAsyncTaskExecutor特點 每次調用創建新線程&#xff1a; SimpleAsyncTaskExecutor 每次執行任務時都會創建一個新線程&#xf…

軟件測試 Linux 服務器監控命令的基本知識

Linux 服務器因其高效、穩定、開源等優勢&#xff0c;廣泛用于網絡服務、數據庫管理、應用開發等領域。而為了確保服務器的正常運行和性能&#xff0c;我們必須不斷監控服務器的狀態。這就需要我們熟悉一些基本的監控命令。 本文將詳細介紹多種監控命令的使用方法及其應用。同…

Spring 的不同事務傳播行為

目錄 Spring 的不同事務傳播行為 PROPAGATION_REQUIRES_NEW事務傳播行為什么情況下會使用? 一、PROPAGATION_REQUIRES_NEW的含義 二、使用場景 三、注意事項 PROPAGATION_NESTED事務傳播行為什么情況下會使用? 一、PROPAGATION_NESTED的含義 二、使用場景 三、嵌套事…

【Linux】進度條

本文中&#xff0c;我們來寫一個進度條。 本文大綱&#xff1a; 寫一個命令行版的進度條。 1.回車換行 2.緩沖區問題&#xff08;本文不深究&#xff09; ? 2.1測試代碼 3.寫一個什么樣的進度條&#xff1f; ? version1 ? version2 回車換行 這倆不是一個概念&…

SLAM/數字圖象處理基礎

概念 視差&#xff1a;相同特征的不同深度估計的偏差 BoW&#xff0c;DBoW&#xff0c;DBoW2的區別是什么 Bag of Words (BoW)、DBoW&#xff08;Dynamic Bag of Words&#xff09;和DBoW2是用于圖像處理和計算機視覺中的不同特征表示和匹配方法。它們之間的主要區別如下&am…

UE5材質節點SimpleGrassWind

SimpleGrassWind節點可以模擬樹葉擾動&#xff0c;或小草晃動效果 用來做風格化樹、風格化草效果很好 主要節點 前三個節點分別用來控制&#xff0c;風強度&#xff0c;風重力&#xff0c;風速度&#xff0c;WPO是世界位置偏移

WeNet:面向生產的流式和非流式端到端語音識別工具包

這篇文章介紹了WeNet&#xff0c;一個面向生產的開源端到端&#xff08;E2E&#xff09;語音識別工具包。WeNet的主要特點和貢獻如下&#xff1a; 統一流式和非流式識別&#xff1a;提出了一種名為U2的兩階段框架&#xff0c;能夠在單一模型中同時支持流式和非流式語音識別&…

Ubuntu20.04安裝Foxit Reader 福昕閱讀器

Ubuntu20.04安裝Foxit Reader 福昕閱讀器 文章目錄 Ubuntu20.04安裝Foxit Reader 福昕閱讀器 先更新一下源 sudo apt update sudo apt upgrade下載Foxit Reader的穩定版本 wget https://cdn01.foxitsoftware.com/pub/foxit/reader/desktop/linux/2.x/2.4/en_us/FoxitReader.e…

2024年底關于期貨的工作總結

十幾年程序猿出身&#xff0c;因幾年前的懵懂無畏闖入期貨市場&#xff0c;盈了&#xff0c;感覺期貨太簡單&#xff0c;飄然裸辭&#xff0c;想當財務自由者&#xff0c;全職做交易。當深入學習時&#xff0c;卻虧了&#xff0c;原來市場是讓人敬畏的&#xff0c;也是反人性的…

c++入門——c++輸入cin和輸出cout的簡單使用

c輸入cin、輸出cout 1 cin2 cout3 cin和cout說明 c在c語言的輸入、輸出函數的基礎上進行了封裝。 1 cin c可以理解為控制臺&#xff0c;in可以理解為輸入。 參考代碼&#xff1a; void f(){int a;float b;double c;char d;cin>>a>>b>>c>>d;//這里和…

PlantUML 時序圖 基本例子

基本的例子 序列-> 用于繪制兩個參與者之間的信息。參與者不必明確聲明。 要有一個點狀的箭頭&#xff0c;就用--> 也可以用<- 和<-- 。這不會改變繪圖&#xff0c;但可能提高可讀性。注意&#xff0c;這只適用于順序圖&#xff0c;其他圖的規則不同。 plantum…

YOLOv10-1.1部分代碼閱讀筆記-utils.py

utils.py ultralytics\nn\modules\utils.py 目錄 utils.py 1.所需的庫和模塊 2.def _get_clones(module, n): 3.def bias_init_with_prob(prior_prob0.01): 4.def linear_init(module): 5.def inverse_sigmoid(x, eps1e-5): 6.def multi_scale_deformable_attn_py…

vue3使用video-player實現視頻播放(可拖動視頻窗口、調整大小)

1.安裝video-player npm install video.js videojs-player/vue --save在main.js中配置全局引入 // 導入視頻播放組件 import VueVideoPlayer from videojs-player/vue import video.js/dist/video-js.cssconst app createApp(App) // 視頻播放組件 app.use(VueVideoPlayer)2…

基于卷積神經網絡的甲狀腺結節識別系統,resnet50,mobilenet模型【pytorch框架+python源碼】

更多目標檢測、圖像分類識別、目標追蹤等項目可看我主頁其他文章 功能演示&#xff1a; 甲狀腺結節識別系統&#xff0c;卷積神經網絡&#xff0c;resnet50&#xff0c;mobilenet【pytorch框架&#xff0c;python源碼】_嗶哩嗶哩_bilibili &#xff08;一&#xff09;簡介 …

C++--類與對象

1.封裝 封裝是c面向對象的三大特性之一 將屬性和行為作為一個整體 將屬性和行為加以權限控制 語法&#xff1a; class 類名{ 訪問權限: 屬性/行為 }; 訪問權限 public 公共權限 類內類外均可以訪問 protected 保護權限 類內可以訪問&#xff0c;類外不可以訪問 pr…

區塊鏈期末復習3:跨鏈原子交換其他加密貨幣

參考教材&#xff1a;《區塊鏈&#xff1a;技術驅動金融》 一、跨鏈原子交換&#xff08;不可分割的交叉鏈互換&#xff09; 1.實施步驟 假設Alice要拿1BTC交換Bob的3BCY。Alice作為交易的發起者。 1&#xff09;Alice創建一個secret&#xff08;一個隨機數x), 并計算其哈希…

OCR實踐-Table-Transformer

前言 書接上文 OCR實踐—PaddleOCR Table-Transformer 與 PubTables-1M table-transformer&#xff0c;來自微軟&#xff0c;基于Detr&#xff0c;在PubTables1M 數據集上進行訓練&#xff0c;模型是在提出數據集同時的工作&#xff0c; paper PubTables-1M: Towards comp…

重裝操作系統后 Oracle 11g 數據庫數據還原

場景描述&#xff1a; 由于SSD系統盤損壞&#xff0c;更換硬盤后重裝了操作系統&#xff0c;Oracle數據庫之前安裝在D盤(另一個硬盤)&#xff0c;更換硬盤多添加一個盤符重裝系統后盤符從D變成E&#xff0c;也就是之前的D:/app/... 變成了現在的 E:/app/...&#xff0c;重新安裝…