26-LINUX--I/O復用-select

一.I/O復用概述

????????/O復用使得多個程序能夠同時監聽多個文件描述符,對提高程序的性能有很大幫助。以下情況適用于I/O復用技術:

? TCP 服務器同時要處理監聽套接字和連接套接字。
? 服務器要同時處理 TCP 請求和 UDP 請求。
? 程序要同時處理多個套接字。
? 客戶端程序要同時處理用戶輸入和網絡連接。
? 服務器要同時監聽多個端口
? ? ? ??需要指出的是,I/O 復用雖然能同時監聽多個文件描述符,但它本身是阻塞的。并且當
多個文件描述符同時就緒時,如果不采取額外的措施,程序就只能按順序依處理其中的每一
個文件描述符,這使得服務器看起來好像是串行工作的。如果要提高并發處理的能力,可以
配合使用多線程或多進程等編程方法

二.select機制

1.select接口介紹

????????select 系統調用的用途是:在一段指定時間內,監聽用戶感興趣的文件描述符的可讀、
可寫和異常等事件。
????????select 系統調用的原型如下:
 #include <sys/select.h>int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct ti
meval *timeout);/*select 成功時返回就緒(可讀、可寫和異常)文件描述符的總數。如果在超時時間內
沒有任何文件描述符就緒,select 將返回 0。select 失敗是返回-1.如果在 select 等待
期間,程序接收到信號,則 select 立即返回-1,并設置 errno 為 EINTR。maxfd 參數指定的被監聽的文件描述符的總數。它通常被設置為 select 監聽的所
有文件描述符中的最大值+1readfds、writefds 和 exceptfds 參數分別指向可讀、可寫和異常等事件對應的文件
描述符集合。應用程序調用 select 函數時,通過這 3 個參數傳入自己感興趣的文件
描述符。select 返回時,內核將修改它們來通知應用程序哪些文件描述符已經就緒
fd_set 結構如下:#define __FD_SETSIZE 1024typedef long int __fd_mask;#define __NFDBITS (8 * (int) sizeof (__fd_mask))typedef struct{#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];# define __FDS_BITS(set) ((set)->fds_bits)#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];# define __FDS_BITS(set) ((set)->__fds_bits)#endif} fd_set;通過下列宏可以訪問 fd_set 結構中的位:FD_ZERO(fd_set *fdset); // 清除 fdset 的所有位FD_SET(int fd, fd_set *fdset); // 設置 fdset 的位 fdFD_CLR(int fd, fd_set *fdset); // 清除 fdset 的位 fdint FD_ISSET(int fd, fd_set *fdset);// 測試 fdset 的位 fd 是否被設置timeout 參數用來設置 select 函數的超時時間。它是一個 timeval 結構類型的指針,采用指針參數是因為內核將修改它以告訴應用程序 select 等待了多久。timeval結構的定義如下:struct timeval{long tv_sec; //秒數long tv_usec; // 微秒數};//struct timeval tv = {5,0};如果給 timeout 的兩個成員都是 0,則 select 將立即返回。如果 timeout 傳遞
NULL,則 select 將一直阻塞,直到某個文件描述符就緒*/

2.設計思路圖解

3.測試代碼

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/select.h>
#include<time.h>#define STDIN 0
int main()
{int fd = STDIN;//鍵盤fd_set fdset;//集合,收集描述符while(1)//因為不止檢測一次{FD_ZERO(&fdset);//清空集合,每個位置0:FD_ZEROFD_SET(fd,&fdset);//將描述符fd添加到集合fdsetstruct timeval tv = {5,0};//超時時間int n = select(fd+1,&fdset,NULL,NULL,&tv);//可能阻塞if(n ==-1)//select執行失敗{printf("select err\n");}else if(n==0)//超市,沒有找到可用事件描述符{printf("tme out\n");}else{if(FD_ISSET(fd,&fdset)){char buff[128]={0};int num = read(fd,buff,127);printf("num=%d,buff=%s\n",num,buff);}}}
}
~                                                                                                                                                                                            
~                                                                                                                                                                                            
~                         

4.tcp通過select實現并發連接

SER.C

#include<stdio.h>      // 標準輸入輸出庫
#include<stdlib.h>     // 標準庫,提供一些函數如malloc, free, rand等
#include<string.h>    // 字符串操作庫
#include<unistd.h>    // UNIX標準函數庫
#include<sys/select.h>// 選擇庫,提供select函數
#include<time.h>      // 時間庫
#include<sys/socket.h>// 套接字庫
#include<arpa/inet.h> // 提供inet_addr等函數
#include<netinet/in.h>// 提供一些網絡相關的宏#define MAXFD 10       // 定義最大文件描述符數量// 初始化socket函數
int socket_init();// 初始化文件描述符數組
void fds_init(int fds[]){for(int i=0; i<MAXFD; i++){fds[i] = -1; // 將所有文件描述符初始化為-1,表示未被使用}
}// 將新的文件描述符添加到數組中
void fds_add(int fds[], int fd){for(int i=0; i<MAXFD; i++){if(fds[i] == -1){ // 找到數組中第一個未使用的文件描述符位置fds[i] = fd;  // 添加文件描述符break;        // 退出循環}}
}// 從未使用的文件描述符數組中刪除指定的文件描述符
void fds_del(int fds[], int fd){for(int i=0; i<MAXFD; i++){if(fds[i] == fd){ // 找到要刪除的文件描述符fds[i] = -1;   // 將其設置為-1,表示未使用break;         // 退出循環}}
}// 接受客戶端連接請求并添加到文件描述符數組
void accept_client(int sockfd, int fds[]){int c = accept(sockfd, NULL, NULL); // 接受連接if(c < 0){return; // 如果返回-1,表示出錯}printf("accept c = %d\n", c);fds_add(fds, c); // 添加到文件描述符數組
}// 接收客戶端發送的數據
void recv_date(int c, int fds[]){char buff[128] = {0}; // 創建緩沖區int n = recv(c, buff, 127, 0); // 接收數據if(n < 0){printf("cli close\n");close(c);          // 如果接收失敗,關閉連接fds_del(fds, c);   // 從數組中刪除該文件描述符return;}if(n == 0){printf("time out(%d)\n", n); // 如果超時}printf("buff(c=%d)=%s\n", c, buff); // 打印接收到的數據send(c, "ok", 2, 0);             // 發送確認消息
}// 主函數
int main(){int sockfd = socket_init(); // 初始化socketif(sockfd == -1){exit(1); // 如果初始化失敗,退出程序}int fds[MAXFD]; // 文件描述符數組fds_init(fds);  // 初始化數組fds_add(fds, sockfd); // 將監聽的socket添加到數組fd_set fdset; // 創建文件描述符集合while(1){ // 無限循環等待事件FD_ZERO(&fdset); // 清空文件描述符集合int maxfd = -1; // 存儲最大的文件描述符// 遍歷文件描述符數組,將所有文件描述符添加到集合中for(int i=0; i<MAXFD; i++){if(fds[i] == -1){continue; // 如果文件描述符未使用,跳過}FD_SET(fds[i], &fdset); // 添加到集合if(fds[i] > maxfd){ // 更新最大文件描述符maxfd = fds[i];}}struct timeval tv = {5,0}; // 設置超時時間// 使用select等待直到有文件描述符準備好IO操作或超時int n = select(maxfd+1, &fdset, NULL, NULL, &tv);if(n == -1){printf("select err\n"); // 錯誤} else if(n == 0){printf("time out\n"); // 超時} else{// 遍歷文件描述符數組,檢查哪些文件描述符準備好了IO操作for(int i=0; i<MAXFD; i++){if(fds[i] == -1){continue; // 如果文件描述符未使用,跳過}if(FD_ISSET(fds[i], &fdset)){ // 檢查文件描述符是否被設置if(fds[i] == sockfd){ // 如果是監聽的socketaccept_client(sockfd, fds); // 接受新的連接} else{ // 如果是已連接的客戶端recv_date(fds[i], fds); // 接收數據}}}}}
}// 創建socket并綁定到端口
int socket_init(){int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建socketif(sockfd == -1){return -1; // 創建失敗返回-1}struct sockaddr_in saddr; // 服務器地址結構memset(&saddr, 0, sizeof(saddr)); // 清零saddr.sin_family = AF_INET; // 地址族saddr.sin_port = htons(6000); // 端口saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 綁定if(res == -1){printf("bind err\n");return -1; // 綁定失敗返回-1}if(listen(sockfd, 5) == -1){ // 開始監聽,設置隊列長度為5return -1; // 監聽失敗返回-1}return sockfd; // 返回socket文件描述符
}

CLI.C

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>int main()
{int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd == -1){exit(1);}struct sockaddr_in saddr;//代表服務器的端口memset(&saddr,0,sizeof(saddr));saddr.sin_family = AF_INET;saddr.sin_port = htons(6000);saddr.sin_addr.s_addr = inet_addr("127.0.0.1");int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));if(res == -1){printf("connct err\n");exit(1);}while(1){printf("input: ");char buff[128]={0};fgets(buff,128,stdin);if(strncmp(buff,"end",3)==0){break;}send(sockfd,buff,strlen(buff)-1,0);memset(buff,0,128);recv(sockfd,buff,127,0);printf("buff=%s\n",buff);}close(sockfd);exit(0);
}

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

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

相關文章

Facebook廣告素材如何測試?手把手教你!

廣告素材對Facebook廣告效果的影響是很大的&#xff0c;用對了素材&#xff0c;Facebook廣告的價值就越高。廣告主們通常會先通過廣告測試&#xff0c;根據數據反饋來挑選素材。今天就手把手教你做Facebook素材測試的技巧&#xff0c;讓你更有靈感和思路&#xff01; 創意測試 …

Hudi CLI 安裝配置總結

前言 上篇文章 總結了Spark SQL Rollback, Hudi CLI 也能實現 Rollback,本文總結下 Hudi CLI 安裝配置以及遇到的問題。 官方文檔 https://hudi.apache.org/cn/docs/cli/ 版本 Hudi 0.13.0(發現有bug)、(然后升級)0.14.1Spark 3.2.3打包 mvn clean package -DskipTes…

使用 Django 構建動態網頁

文章目錄 創建 Django 項目和應用程序創建 HTML 模板創建視圖函數配置 URL 路由運行 Django 服務器使用 Django 模板語言 Django 是一個流行的 Python Web 框架&#xff0c;它能夠幫助開發人員快速構建強大的 Web 應用程序。在 Django 中&#xff0c;HTML 是用于呈現網頁內容的…

Spring Boot 復習

2 3 5&#xff08;不考&#xff09; 9 (1)RestController 注解是一個組合注解&#xff0c;等同于Controller 和ResponseBody 兩個注解結合使用的效果。主要作用是將當前類作為控制層的組件添加到 Spring 容器中&#xff0c;同時該類的方法無法返回 JSP 頁面&#xff0c;而且…

Flutter 中的 RenderObjectToWidgetAdapter 小部件:全面指南

Flutter 中的 RenderObjectToWidgetAdapter 小部件&#xff1a;全面指南 Flutter 是一個功能強大的 UI 框架&#xff0c;由 Google 開發&#xff0c;允許開發者使用 Dart 語言構建跨平臺的移動、Web 和桌面應用。在 Flutter 的渲染體系中&#xff0c;RenderObjectToWidgetAdap…

MyBatis面試題系列三

1、#{}和${}的區別是什么&#xff1f; #{}是預編譯處理&#xff0c;${}是字符串替換。 Mybatis 在處理#{}時&#xff0c;會將 sql 中的#{}替換為?號&#xff0c;調用 PreparedStatement 的 set 方法來賦值&#xff1b; Mybatis 在處理${}時&#xff0c;就是把${}替換成變量的值…

SpringBoot項目啟動時“jar中沒有主清單屬性”異常

資料參考 Spring Boot 啟動時 “jar中沒有主清單屬性” 異常 - spring 中文網 (springdoc.cn) 實際解決 更詳細的參考以上&#xff0c;我這邊的話只需要在 pom文件 中加上 spring-boot-maven-plugin 插件就能解決該異常&#xff0c;具體如下&#xff1a; <build><p…

1. 計算機系統概述

1. 計算機系統概述 文章目錄 1. 計算機系統概述1.1 計算機的發展硬件的發展軟件的發展 1.2.1 計算機硬件的基本組成早期馮諾依曼的結構現代計算機的結構 1.2.2 各個硬件的工作原理主存儲器運算器控制器計算機工作過程 1.2.3 計算機系統的多級層次結構1.3 計算機的性能指標存儲器…

GD32如何配置中斷優先級分組以及中斷優先級

使用GD32 MCU的過程中&#xff0c;大家可能會有以下疑問&#xff1a;中斷優先級如何配置和使用&#xff1f; 本文將會為大家解析中斷優先級分組以及中斷優先級的配置使用&#xff1a; 中斷優先級分組配置 一個GD32 MCU系統需要大家明確系統中使用的中斷優先級分組&#xff0…

代駕公司在市場競爭中如何保持優勢?

在競爭激烈的市場中&#xff0c;代駕公司可以通過多種策略保持其競爭優勢&#xff0c;包括利用市場潛力、創新服務模式、提高服務效率以及加強品牌建設等。以下是具體的策略&#xff1a; 利用市場潛力 汽車產業空間巨大&#xff1a;隨著汽車保有量的增加&#xff0c;代駕行業…

掃地機器人:卷價格,不如卷技術

掃地機器人內卷的終點是技術和價值&#xff0c;價格只是附屬品。 一路上漲的價格&#xff0c;一路下跌的銷量 從價格飆升&#xff0c;到重新卷回價格&#xff0c;尷尬的背后是掃地機器人在骨感現實下的無奈抉擇。 根據數據顯示&#xff0c;2020中國掃地機器人線上市場零售均價…

通過可識別性和深度學習重建大腦功能網絡

摘要 本研究提出了一種新的方法來重建代表大腦動力學的功能網絡&#xff0c;該方法基于兩個腦區在同一認知任務中的共同參與會導致其可識別性或其動力學特性降低的觀點。這種可識別性是通過深度學習模型在監督分類任務中獲得的分數來估計的&#xff0c;因此不需要對這種協同參…

零、測試開發前置知識

文章目錄 1、什么是冒煙測試、回歸測試&#xff1f;2、設計測試用例的方法有哪些&#xff1f;3、對于404或500&#xff0c;你會如何分析定位&#xff1f;4、什么是敏捷開發&#xff1f;敏捷開發流程是怎么樣的&#xff1f;5、做接口測試過程中&#xff0c;下游接口需要上游數據…

Flink端到端的精確一次(Exactly-Once)

目錄 狀態一致性 端到端的狀態一致性 端到端精確一次&#xff08;End-To-End Exactly-Once&#xff09; Flink內部的Exactly-Once 輸入端保證 輸出端保證 冪等寫入 事務寫入 Flink和Kafka連接時的精確一次保證 整體介紹 需要的配置 案例 狀態一致性 流式計算本身就…

Java工作學習筆記

1、ConfigurationProperties注解是什么意思&#xff1f; ConfigurationProperties 可以將屬性文件與一個Java類綁定&#xff0c;將屬性文件中的變量值注入到該Java類的成員變量中 示例代碼&#xff1a; /*** SSP配置** author mua*/ Component Data ConfigurationProperties…

如何提高接口響應速度

在非大數據&#xff08;幾萬以上記錄&#xff09;的情況下&#xff0c;影響接口響應速度的因素中最大的是查詢數據庫的次數&#xff0c;其次才是數組遍歷和簡單數據處理&#xff08;如根據已有字段增加新的屬性&#xff0c;或計算值&#xff09;。 一般一次數據庫查詢需要50毫秒…

Java Web應用,IPv6問題解決

在Java Web程序中&#xff0c;如果使用Tomcat并遇到了IPv6相關的問題&#xff0c;可以通過以下幾種方式來解決&#xff1a; 1. 配置Tomcat以使用IPv4 默認情況下&#xff0c;Java可能會優先使用IPv6。如果你希望Tomcat使用IPv4&#xff0c;最簡單的方法是通過設置系統屬性來強…

無線麥克風哪個牌子性價比高?一文告訴你無線領夾麥克風怎么挑選

?當我們談論到演講、表演或者錄制視頻時&#xff0c;一個高質量的無線麥克風能夠使得整個體驗提升至一個全新的水平。它不僅能夠保證聲音的清晰度和真實度&#xff0c;還能夠讓使用者在演講或者表演時更加自信和舒適。基于對市場的深入研究和用戶體驗的考量&#xff0c;我挑選…

TypeScript 中的 tsconfig.json

什么是 tsconfig.json&#xff1f; tsconfig.json 是 TypeScript 編譯器的配置文件&#xff0c;用于指導編譯器如何編譯 TypeScript 代碼。在 TypeScript 項目中&#xff0c;如果存在這個文件&#xff0c;那么在執行 tsc 命令時&#xff0c;編譯器將會使用該文件中定義的配置選…

【Java】解決Java報錯:IllegalArgumentException

文章目錄 引言1. 錯誤詳解2. 常見的出錯場景2.1 非法的參數值2.2 空值或 null 參數2.3 非法的數組索引 3. 解決方案3.1 參數驗證3.2 使用自定義異常3.3 使用Java標準庫中的 Objects 類 4. 預防措施4.1 編寫防御性代碼4.2 使用注解和檢查工具4.3 單元測試 結語 引言 在Java編程…