多路轉接select服務器

目錄

select函數原型

select服務器

select的缺點


前面介紹過多路轉接就是能同時等待多個文件描述符,這篇文章介紹一下多路轉接方案中的select的使用

select函數原型

#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set
*exceptfds, struct timeval *timeout);

先介紹一下各個參數以及返回值

多路轉接需要等待多個文件描述符的事件就緒,所以用戶勢必需要告訴操作系統,他關心的是哪些文件描述符,以及關心這些文件描述符上的讀事件還是寫事件。讀事件就緒就是這個文件描述符的緩沖區不為空有數據能讀,寫事件就緒就是緩沖區不為滿可以寫入。除了這兩種常見的事件外,還可以關心某個文件描述符的異常事件。

再來看select的參數,nfds是一個整數,可以告訴操作系統需要關心哪些文件描述符,具體來說,nfds是需要關心的文件描述符的最大值 + 1,可以預想到select函數會遍歷小于等于nfds - 1的文件描述符,查看是否有事件就緒

struct timeval {time_t      tv_sec;  /* Seconds */          //秒suseconds_t tv_usec; /* Microseconds */     //毫秒
};

timeout表示select的等待時間,timeout也可為空,表示阻塞等待直到某個文件描述符發生事件,timeout為0表示不等待事件發生,其他自定義值表示若在這段時間內沒有事件發生,則超時返回。

返回值為0表示超時返回;為-1表示有錯誤發生,并設置錯誤碼errno;為正數表示在timeout時間內事件就緒的文件描述符個數

為了介紹剩下的三個參數,先介紹一下fd_set

我們已經通過fds告訴操作系統要關心哪些文件描述符,timeout設置了等待時間,現在還需要告訴操作系統要關心哪些文件描述符的讀事件或寫事件

從抽象的層面上理解,fd_set是一個集合,是一個文件描述符的集合,readfds是關心讀事件的文件描述符集合,writefds是關心寫事件的文件描述符集合,exceptfds是關心異常事件的文件描述符集合。

還需要指出,這三個參數還是輸出型參數,操作系統會將等待后事件就緒的文件描述符加入集合,

比如關心4,5,6的讀事件,若就緒了4和5,集合就會變成4,5,這也為寫代碼帶來了麻煩

從具體實現上來看,fd_set是一個位圖,有若干個比特位表示文件描述符,值為1表示關心這個文件描述符,為0表示不關心,舉個例子

00011111001

下標從0開始的話,這個位圖表示關心3,4,5,6,7,10號文件描述符,其余的都不關心

/* fd_set for select and pselect.  */
typedef struct{/* XPG4.2 requires this member name.  Otherwise avoid the namefrom the global namespace.  */
#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_set操作,操作系統也提供了相關的接口

// 將文件描述符fd從集合set中刪除
void FD_CLR(int fd, fd_set *set);
// 判斷文件描述符fd是否在集合set中
int  FD_ISSET(int fd, fd_set *set);
// 將fd放入集合set中
void FD_SET(int fd, fd_set *set);
// 清空集合set
void FD_ZERO(fd_set *set);

select服務器

到這里,select已經可以等待多個文件描述符的一些事件了,可以來搭一個簡單的服務器,接收多個用戶的消息,回顯在屏幕上

這里只給出select_server的代碼,其他文件的代碼對理解select不重要,只需要了解套接字的使用便可輕松看懂,若要查看其他文件的代碼,詳見rokobo/wsl_code - Gitee.com

在這份代碼中,需要等待的事件有等待客戶端的連接和等待客戶端發消息,由于連接建立后會創建文件描述符,文件描述符會變多,需要一個數據結構把這些文件描述符管理起來,這里選擇了原生數組,因為可以直觀的感受到select的缺點之一,存在大量遍歷,性能不夠高。

#include "socket.hpp"
#include "Log.hpp"
#include <sys/select.h>
#include <memory>
#include <cstring>
#include <cerrno>
using namespace SocketModule;
using namespace LogModule;class select_server
{
// sizeof可以得到底層數組的字節數,乘8得到比特數
static const int NUM = sizeof(fd_set) * 8;
public:select_server():_listen_sock(std::make_shared<TcpSocket>()),_is_running(false){}void init(int port){_listen_sock->BuildTcpSocketMethod(port);for(int i=0;i<NUM;++i){fds[i] = -1;}//初始只需要關心_listen_sock這一個文件描述符fds[0] = _listen_sock->Fd();}void loop(){_is_running = true;int listenfd = _listen_sock->Fd();fd_set readset;while(_is_running){//readset作為輸出參數,select返回后可能被修改,需要清空后重新設置FD_ZERO(&readset);int max_fd = 0;for(int i=0;i<NUM;++i){if(fds[i] != -1){max_fd = fds[i] > max_fd ? fds[i] : max_fd;FD_SET(fds[i], &readset);}}struct timeval timeout = {2, 0};int ret = select(max_fd + 1, &readset, nullptr, nullptr, &timeout);if(ret == -1){LOG(LogLevel::ERROR) << "Error message: " << strerror(ret);continue;}else if(ret == 0){LOG(LogLevel::INFO) << "Time out\n";continue;}else{LOG(LogLevel::INFO) << "Dispatch begin\n";// 給不同種類的文件描述符分發不同的任務dispatcher(readset);}}  }void accepter(int fd){InetAddr client;auto client_sock = _listen_sock->Accepter(&client);if(client_sock == nullptr){LOG(LogLevel::ERROR) << "Accept error";return;}int client_fd = client_sock->Fd();if(client_fd < 0){LOG(LogLevel::ERROR) << "Client fd error";return;}//將client_fd加入到fds中//如果fds滿了,關閉連接int i=0;for(i=0;i<NUM;++i){if(fds[i] == -1){fds[i] = client_fd;LOG(LogLevel::INFO) << "Accept success: " << client_sock->Fd() << " " << client.Addr();break;}}if(i == NUM){LOG(LogLevel::ERROR) << "Too many connections";client_sock->Close();return;}}void recver(int who){int fd = fds[who];std::string buffer;auto client_sock = std::make_shared<TcpSocket>(fd);ssize_t ret = client_sock->Recv(&buffer);if(ret == -1){LOG(LogLevel::ERROR) << "Recv error" << strerror(errno);client_sock->Close();//將fd從fds中刪除fds[who] = -1;return;}else if(ret == 0){LOG(LogLevel::INFO) << "Client closed: " << client_sock->Fd();client_sock->Close();//將fd從fds中刪除fds[who] = -1;return;}else{LOG(LogLevel::INFO) << "Recv success: " << buffer;return;}}void dispatcher(fd_set &readset){//找到所有合法的fd,分發for(int i=0;i<NUM;++i){if(fds[i] == -1)continue;if(FD_ISSET(fds[i], &readset)){//分發給處理連接的函數if(fds[i] == _listen_sock->Fd()){accepter(fds[i]);}//分發給處理IO的函數else{recver(i);}}}}void stop(){}
private:std::shared_ptr<TcpSocket> _listen_sock;int fds[NUM];bool _is_running;
};

主函數

#include "select_server.hpp"
#include <string>
int main()
{select_server s_svr;s_svr.init(8080);s_svr.loop();return 0;
}

?

select的缺點

從代碼中大量的遍歷,甚至select底層還要遍歷,可以感受到select有太多遍歷,效率不高,而且fd_set的底層數組是靜態的無法擴容,能同時關心的文件描述符有限,而且需要用戶自己去定義數據結構管理需要關心的文件描述符,更是增加了編碼的復雜性,每次調用select,都需要把fd_set從用戶態拷貝到內核態,這個拷貝的開銷在fd很多時開銷很大

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

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

相關文章

QT6 源(45):分隔條 QSplitter 允許程序的用戶修改布局,程序員使用 IDE時,就是分隔條的用戶,以及其 QSplitter 源代碼

&#xff08;1&#xff09; &#xff08;2&#xff09;本類的繼承關系如下&#xff0c;所以說分隔條屬于容器&#xff1a; &#xff08;3&#xff09;本類的屬性&#xff1a; &#xff08;4&#xff09; 這是一份 QSplitter 的舉例代碼&#xff0c;注意其構造函數時候的傳參&am…

VSCode PIO使用Jlink SWD燒錄Stm32

一、背景 PIO的編譯速度比Arduino快很多&#xff0c;同樣支持Arduino的語法。VScode的自動補全和插件也能夠幫助快速開發目前使用JLINK SWD的方式連接STM32 二、配置 在ini配置文件中&#xff0c;添加如下內容 [env:genericSTM32F103C8] platform ststm32 board genericS…

JavaScript 渲染內容爬取:Puppeteer 入門

在現代網絡應用中&#xff0c;許多網頁內容是通過 JavaScript 渲染生成的&#xff0c;傳統的爬蟲工具往往難以獲取這些動態內容。Puppeteer 作為一種強大的瀏覽器自動化工具&#xff0c;為這一問題提供了優雅的解決方案。本文將帶你入門 Puppeteer&#xff0c;介紹如何安裝、啟…

卷積神經網絡:視覺煉金術士的數學魔法

引言&#xff1a;當數學遇見視覺煉金術 在人工智能的奇幻世界里&#xff0c;卷積神經網絡&#xff08;CNN&#xff09;猶如掌握視覺奧秘的煉金術士&#xff0c;將原始像素的"鉛塊"淬煉成認知的"黃金"。這種融合數學嚴謹性與生物靈感的算法架構&#xff0c…

Android Cordova 開發 - Cordova 快速入門(Cordova 環境配置、Cordova 第一個應用程序)

一、Cordova 1、Cordova 概述 Cordova 是使用 HTML&#xff0c;CSS 和 JavaScript 構建混合移動應用程序的平臺 2、Cordova 特征 &#xff08;1&#xff09;命令行界面&#xff08;Cordova CLI&#xff09; 這是可用于啟動項目&#xff0c;構建不同平臺的進程&#xff0c;…

ubuntu18.04啟動不了修復

參考: 虛擬機里的Ubuntu18.4啟動時進入到grub rescue救援模式&#xff08;無法正常進入到系統&#xff09;&#xff0c;ls查看后只有一個硬盤和分區&#xff0c;且無法找到/boot/grub文件【已解決】_ubuntu grub rescue-CSDN博客 本人fdisk錯誤使用,導致了grub啟動不了 第一步…

SpringBoot3設置maven package直接打包成二進制可執行文件

注意事項 SpringBoot普通native打包順序clean compile spring-boot:process-aot native:compile 使用以下配置只會的打包順序clean package&#xff08;注意&#xff1a;使用此配置以后打包會有編譯后的class文件、jar包、original源文件、二進制可執行文件【Linux是無后綴的包…

【華為】防火墻雙擊熱備-之-主備模式-單外網線路

FW1和FW2的業務接口都工作在三層&#xff0c;上行連接二層交換機。上行交換機連接運營商的接入點&#xff0c;運營商為企業分配的IP地址為100.100.100.2。現在希望FW1和FW2以主備備份方式工作。正常情況下&#xff0c;流量通過FW1轉發&#xff1b;當FW1出現故障時&#xff0c;流…

MYSQL之表的操作

1. 創建表 語法: CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校驗規則 engine 存儲引擎; field 表示列名, datatype 表示列的類型character set 字符集, 如果沒有指定字符集, 則以所在數據庫的字符集為…

RAG進階:Chroma開源的AI原生向量數據庫

一、Chroma 核心概念與優勢 1. 什么是 Chroma&#xff1f; Chroma 是一款開源的向量數據庫&#xff0c;專為高效存儲和檢索高維向量數據設計。其核心能力在于語義相似性搜索&#xff0c;支持文本、圖像等嵌入向量的快速匹配&#xff0c;廣泛應用于大模型上下文增強&#xff0…

店匠科技摘得 36 氪“2025 AI Partner 創新大獎”

全場景 AI 方案驅動跨境電商數智化躍遷 4 月 18 日,36 氪 2025 AI Partner 大會于上海盛大開幕。大會緊扣“Super App 來了”主題,全力探尋 AI 時代的全新變量,探索 AI 領域下一個超級應用的無限可能性。在此次大會上,跨境電商獨立站 SaaS 平臺店匠科技(Shoplazza)憑借“店匠跨…

SQL技術終極指南:從內核原理到超大規模應用

一、DDL核心應用場景與最佳實踐 1.1 表結構設計場景矩陣 業務場景核心語法要素典型實現案例電商用戶畫像JSON字段虛擬列索引CREATE TABLE users (id INT, profile JSON, AS (profile->>$.age) VIRTUAL, INDEX idx_age((profile->>$.age)))物聯網時序數據分區表壓…

吳恩達深度學習作業CNN之ResNet實現(Pytorch)

課程中認識許多CNN架構。首先是經典網絡&#xff1a; LeNet-5AlexNetVGG 之后是近年來的一些網絡&#xff1a; ResNetInceptionMobileNet 經典網絡 LeNet-5 LeNet-5是用于手寫數字識別&#xff08;識別0~9的阿拉伯數字&#xff09;的網絡。它的結構如下&#xff1a; 網絡…

FPGA入門學習Day1——設計一個DDS信號發生器

目錄 一、DDS簡介 &#xff08;一&#xff09;基本原理 &#xff08;二&#xff09;主要優勢 &#xff08;三&#xff09;與傳統技術的對比 二、FPGA存儲器 &#xff08;一&#xff09;ROM波形存儲器 &#xff08;二&#xff09;RAM隨機存取存儲器 &#xff08;三&…

SqlSugar與Entity Framework (EF)的SWOT分析

以下是基于 SWOT 分析法 對 SqlSugar 和 Entity Framework (EF) 的特性對比&#xff1a; SqlSugar 優勢 (Strengths) 高性能&#xff1a; SqlSugar 以輕量化設計著稱&#xff0c;執行速度更快&#xff0c;適合對性能要求較高的場景。在大數據量操作和復雜查詢中表現優異。 易…

學習記錄:DAY16

Maven 進階與前端實戰 前言 二輪考核的內容下來了&#xff0c;由整體項目構建轉為實現特定模塊的功能。對細節的要求更高了&#xff0c;而且有手搓線程池、手搓依賴注入等進階要求&#xff0c;又有得學力。嘻嘻&#xff0c;太簡單了&#xff0c;只要我手搓 Spring Boot 框架……

深度學習--卷積神經網絡調整學習率

文章目錄 前言一、學習率1、什么學習率2、什么是調整學習率3、目的 二、調整方法1、有序調整1&#xff09;有序調整StepLR(等間隔調整學習率)2&#xff09;有序調整MultiStepLR(多間隔調整學習率)3&#xff09;有序調整ExponentialLR (指數衰減調整學習率)4&#xff09;有序調整…

【消息隊列RocketMQ】四、RocketMQ 存儲機制與性能優化

一、RocketMQ 存儲機制詳解 1.1 存儲文件結構? RocketMQ 的存儲文件主要分布在store目錄下&#xff0c;該目錄是在broker.conf配置文件中通過storePathRootDir參數指定的&#xff0c;默認路徑為${user.home}/store 。主要包含以下幾種關鍵文件類型&#xff1a;? 1.1.1 Comm…

C++入門小館: 探尋vector類

嘿&#xff0c;各位技術潮人&#xff01;好久不見甚是想念。生活就像一場奇妙冒險&#xff0c;而編程就是那把超酷的萬能鑰匙。此刻&#xff0c;陽光灑在鍵盤上&#xff0c;靈感在指尖跳躍&#xff0c;讓我們拋開一切束縛&#xff0c;給平淡日子加點料&#xff0c;注入滿滿的pa…

CSS-跟隨圖片變化的背景色

CSS-跟隨圖片變化的背景色 獲取圖片的主要顏色并用于背景漸變需要安裝依賴 colorthief獲取圖片的主要顏色. 并丟給背景注意 getPalette并不是個異步方法 import styles from ./styles.less; import React, { useState } from react; import Colortheif from colorthief;cons…