一個基于 epoll 實現的多路復用 TCP 服務器程序,相比 select 和 poll 具有更高的效率

/*5 - 使用epoll實現多路復用 */
#include <stdio.h>          // 標準輸入輸出函數庫
#include <stdlib.h>         // 標準庫函數,包含exit等
#include <string.h>         // 字符串處理函數
#include <unistd.h>         // Unix標準函數,包含read, write等
#include <sys/socket.h>     // 套接字相關函數
#include <sys/types.h>      // 基本系統數據類型
#include <sys/epoll.h>      // epoll相關函數和結構體
#include <fcntl.h>          // 文件控制函數
#include <sys/stat.h>       // 文件狀態相關定義
#include <netinet/in.h>     // 互聯網地址族
#include <arpa/inet.h>      // 提供IP地址轉換函數
#include <signal.h>         // 信號處理函數
#include <linux/input.h>    // Linux輸入設備相關定義(未實際使用)#define FD_CNT 1000         // 定義最大監控的文件描述符數量int main(int argc,char *argv[])
{if(argc!=2){                    // 檢查命令行參數數量是否正確printf("Usage:%s port\n",argv[0]);  // 輸出程序使用方法:程序名 端口號exit(0);                    // 正常退出}//1.創建socketint sockfd = socket(AF_INET,SOCK_STREAM,0);  // 創建TCP套接字,AF_INET表示IPv4,SOCK_STREAM表示TCPif(sockfd==-1){                  // 檢查socket創建是否成功perror("socket");            // 輸出錯誤信息exit(-1);                    // 異常退出}//允許地址復用int optval = 1;                  // 選項值,1表示啟用// 設置套接字選項,允許地址復用,避免端口占用問題setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,&optval, sizeof(optval));//2.綁定ip和端口號(自己)struct sockaddr_in addr;         // 定義IPv4地址結構體addr.sin_family = AF_INET;       // 設置協議族為IPv4addr.sin_port = htons(atoi(argv[1]));  // 將命令行參數的端口號轉換為網絡字節序addr.sin_addr.s_addr = INADDR_ANY;  // 綁定到所有可用的網絡接口// 綁定套接字到指定地址和端口int res = bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));if(res==-1){                     // 檢查綁定是否成功perror("bind");              // 輸出錯誤信息exit(-1);                    // 異常退出}//3.監聽res = listen(sockfd,10);         // 開始監聽連接,最大等待隊列長度為10if(res==-1){                     // 檢查監聽是否成功perror("listen");            // 輸出錯誤信息exit(-1);                    // 異常退出}//準備epoll事件數組,用于存儲epoll_wait返回的活動事件struct epoll_event evt_arr[FD_CNT] = {};  // 事件數組初始化int maxfd,i;                     // maxfd未實際使用,i用于循環char msg[1024] = {};             // 用于存儲接收和發送的消息//1.創建epoll專用描述符// 參數為最大監控數量(>=1,Linux 2.6.8后忽略該參數)int epfd = epoll_create(1000);if(epfd==-1){                    // 檢查epoll創建是否成功perror("epoll_create");      // 輸出錯誤信息exit(-1);                    // 異常退出}//2.添加要監控的事件struct epoll_event evt = {       // 定義epoll事件結構體.events = EPOLLIN,           // 監控讀事件(有數據可讀)};evt.data.fd = 0;                 // 綁定標準輸入文件描述符(0)// 將標準輸入添加到epoll監控res = epoll_ctl(epfd,EPOLL_CTL_ADD,0,&evt);if(res==-1){                     // 檢查添加是否成功perror("epoll_ctl");         // 輸出錯誤信息exit(-1);                    // 異常退出}evt.data.fd = sockfd;            // 綁定服務器監聽套接字// 將監聽套接字添加到epoll監控res = epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&evt);if(res==-1){                     // 檢查添加是否成功perror("epoll_ctl");         // 輸出錯誤信息exit(-1);                    // 異常退出}//4.等待事件發生(客戶端連接、消息或輸入)while(1){                        // 主循環,持續運行服務器//調用epoll_wait等待事件,超時時間2000ms(2秒)// 參數:epoll描述符、接收事件的數組、最大事件數、超時時間if((res = epoll_wait(epfd,evt_arr,FD_CNT,2000))<=0){printf("timeout!\n");    // 超時或出錯時輸出信息continue;                // 繼續下一次循環}//處理活動的描述符(遍歷所有返回的事件)for(i=0;i<res;i++){//有鍵盤輸入(標準輸入有讀事件)if(evt_arr[i].data.fd==0 && (evt_arr[i].events&EPOLLIN)){memset(msg,0,sizeof(msg));  // 清空消息緩沖區read(0,msg,sizeof(msg));    // 從標準輸入讀取數據printf("輸入的內容為:%s\n",msg);  // 輸出讀取到的內容}//1.有客戶端連接請求(監聽套接字有讀事件)if(evt_arr[i].data.fd==sockfd && (evt_arr[i].events&EPOLLIN)){struct sockaddr_in cilent_addr;  // 存儲客戶端地址socklen_t len = sizeof(cilent_addr);  // 地址長度// 接受客戶端連接,返回新的套接字描述符int newfd = accept(sockfd,(struct sockaddr *)&cilent_addr,&len);if(newfd==-1){            // 檢查連接是否成功perror("accept");     // 輸出錯誤信息exit(-1);             // 異常退出}//將新連接的客戶端描述符添加到epoll監控evt.data.fd = newfd;      // 綁定新客戶端套接字// 添加新客戶端到epoll監控列表res = epoll_ctl(epfd,EPOLL_CTL_ADD,newfd,&evt);if(res==-1){              // 檢查添加是否成功perror("epoll_ctl");  // 輸出錯誤信息exit(-1);             // 異常退出}// 輸出客戶端IP地址,表示有新客戶端連接printf("%s到此一游!\n",inet_ntoa(cilent_addr.sin_addr));}//2.有客戶端發送消息(其他套接字有讀事件)// 排除標準輸入和監聽套接字,處理客戶端消息if(evt_arr[i].data.fd!=0 && evt_arr[i].data.fd!=sockfd && (evt_arr[i].events&EPOLLIN)){res = read(evt_arr[i].data.fd,msg,sizeof(msg));  // 從客戶端讀取消息if(res<=0){               // 如果讀取失敗或客戶端斷開連接// 客戶端斷開連接,將其從epoll監控中刪除res = epoll_ctl(epfd,EPOLL_CTL_DEL,evt_arr[i].data.fd,&evt_arr[i]);if(res==-1){          // 檢查刪除是否成功perror("epoll_ctl");  // 輸出錯誤信息}close(evt_arr[i].data.fd);  // 關閉套接字continue;             // 繼續下一次循環}//需要長時間通信可以開多任務printf("%s\n",msg);      // 輸出接收到的消息if(strcmp(msg,"byebye")==0){  // 如果客戶端發送"byebye"// 客戶端主動斷開連接,從epoll監控中刪除res = epoll_ctl(epfd,EPOLL_CTL_DEL,evt_arr[i].data.fd,&evt_arr[i]);if(res==-1){          // 檢查刪除是否成功perror("epoll_ctl");  // 輸出錯誤信息}close(evt_arr[i].data.fd);  // 關閉套接字continue;             // 繼續下一次循環}//原路發回消息(回聲功能)write(evt_arr[i].data.fd,msg,res);  // 將接收到的消息回發給客戶端}}}close(sockfd);                   // 關閉監聽套接字(實際不會執行到這里)return 0;                        // 程序正常退出(實際不會執行到這里)
}

程序功能說明:

這是一個基于?epoll?實現的多路復用 TCP 服務器程序,相比?select?和?poll?具有更高的效率,主要功能如下:

核心功能
  1. TCP 服務器基礎功能

    • 創建 TCP 套接字、設置地址復用、綁定端口、監聽連接
    • 支持多客戶端并發連接,通過?epoll?機制實現高效 I/O 多路復用
  2. 多路復用監控

    • 使用?epoll?同時監控三類 I/O 事件:
      • 標準輸入(鍵盤輸入):讀取并顯示鍵盤輸入內容
      • 服務器監聽套接字:接受新的客戶端連接請求
      • 已連接客戶端套接字:接收客戶端消息并提供回聲服務(消息原路返回)
  3. 連接管理

    • 新客戶端連接時自動將其套接字添加到?epoll?監控列表
    • 客戶端斷開連接(主動發送 "byebye" 或異常斷開)時,自動從?epoll?中移除并關閉套接字
    • 最大支持 1000 個并發連接(由?FD_CNT?定義)
技術特點
  1. epoll 機制優勢

    • 采用?epoll_create?創建專用描述符,epoll_ctl?管理監控對象,epoll_wait?等待事件
    • 僅返回活動的文件描述符,無需遍歷所有監控對象,效率更高(尤其在高并發場景)
    • 事件驅動模式,避免?select/poll?的性能瓶頸(如文件描述符數量限制、每次需重置集合)
  2. 事件處理流程

    • 主循環通過?epoll_wait?阻塞等待事件,超時時間設為 2 秒
    • 收到事件后遍歷活動事件數組,區分不同類型事件(鍵盤輸入、新連接、客戶端消息)
    • 針對不同事件類型執行對應處理邏輯,實現單進程高效處理多任務
適用場景
  • 高并發網絡服務器開發(相比?select/poll?更適合大量客戶端連接場景)
  • 演示 Linux 下高效 I/O 多路復用機制的實現
  • 可作為回聲服務器、聊天服務器等基礎框架擴展

該程序展示了?epoll?在網絡編程中的典型用法,是 Linux 平臺下高性能服務器的常用技術方案。

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

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

相關文章

元數據管理與數據治理平臺:Apache Atlas 通知和業務元數據 Notifications And Business Metadata

文中內容僅限技術學習與代碼實踐參考&#xff0c;市場存在不確定性&#xff0c;技術分析需謹慎驗證&#xff0c;不構成任何投資建議。Apache Atlas 框架是一套可擴展的核心基礎治理服務&#xff0c;使企業能夠有效、高效地滿足 Hadoop 中的合規性要求&#xff0c;并支持與整個企…

rem:CSS中的相對長度單位

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

【10】C#實戰篇——C# 調用 C++ dll(C++ 導出函數、C++導出類)

文章目錄1 導出C 類函數 、導出 C函數1.1 .h文件1.2 .cpp 文件1.3 C# 調用2 C與C#數據類型對應3 保姆級教程&#xff08;項目搭建、代碼、調用&#xff0c;圖文并茂&#xff09;1 導出C 類函數 、導出 C函數 C 生成動態庫.dll 詳細教程&#xff1a; C 生成動態庫.dll 及 C調用…

Flutter 與 Android NDK 集成實戰:實現高性能原生功能

Flutter 與 NDK 集成實現 Flutter 可以通過 Platform Channels 與原生代碼&#xff08;包括使用 NDK 編寫的 C/C 代碼&#xff09;進行交互。以下是實現 Flutter 與 NDK 集成的步驟&#xff1a; 基本步驟 1. 創建 Flutter 項目 flutter create flutter_ndk_example cd flutter_…

elementui cascader 遠程加載請求使用 選擇單項等

背景&#xff1a;小程序與后端使用自定義表單渲染視圖。發現若沒有全選&#xff08;如&#xff1a;省市縣全部選擇&#xff0c;指定的市3級&#xff09;在pc端就會無法渲染出已經選擇的區縣名稱。 解決方案&#xff1a;參考官方文檔&#xff0c;設置屬性可獨立勾選element ui c…

Unity WebGL打包后啟動方法,本地方法

引言&#xff1a;常見WebGL開啟方法常需要重新打包點擊Build and Run或者將游戲放到Unity的云服務器上&#xff0c;作為開發者而言這兩個方案一個為了開啟再次打包&#xff0c;另一個直接放到了公開環境都不太合適。所以我們需要一個能在本地開啟測試的WebGL的方法。 解決方案 …

安全引導功能及ATF的啟動過程(五)

安全引導功能及ATF的啟動過程&#xff08;五&#xff09; ATF中bl32的啟動 bl31中的runtime_svc_init函數會初始化OP-TEE對應的服務&#xff0c;通過調用該服務項的初始化函數來完成OP-TEE的啟動。對于OP-TEE的服務項會通過DECLARE_RT_SVC宏在編譯時被存放到rt_svc_des段中。該…

Numpy科學計算與數據分析:Numpy入門之多平臺安裝與基礎環境配置

Numpy環境搭建與基礎操作 學習目標 本課程將指導學員在Windows、macOS和Linux三種操作系統上安裝Numpy&#xff0c;并配置開發環境&#xff0c;包括使用Jupyter Notebook和Spyder等IDE的基本操作。通過本課程的學習&#xff0c;學員將能夠獨立搭建Numpy開發環境&#xff0c;并…

內存溢出的原因有哪些,如何排查線上問題?

1. java.lang.OutOfMemoryError: ......java heap space..... 堆棧溢出&#xff0c;代碼問題的可能性極大 2. java.lang.OutOfMemoryError: GC over head limit exceeded 系統處于高頻的GC狀態&#xff0c;而且回收的效果依然 不佳的情況&#xff0c;就會開始報這個錯誤&…

Cesium 無人機視角飛行漫游,截屏

1.實現Cesium模擬無人機離屏渲染&#xff0c;無人機視角飛行漫游。視錐體顯示 具體效果如下地址&#xff1a; 【CESIUM無人機視角飛行截屏】 https://www.bilibili.com/video/BV1zQ89zGE14/?share_sourcecopy_web&vd_source8239ec37df07d6a5d56c9ece00146783

vscode 打開設置

目錄 方法 1&#xff08;快捷鍵&#xff09;&#xff1a; 方法2&#xff0c;界面操作&#xff0c;有時沒有 方法 1&#xff08;快捷鍵&#xff09;&#xff1a; 按下&#xff1a;Cmd Shift P 輸入并選擇&#xff1a;Preferences: Open Settings (JSON) 方法2&#xff0c;…

繁花深處:花店建設的時代意義與多元應用—仙盟創夢IDE

花店當第一縷晨光透過花店的玻璃窗&#xff0c;落在帶著露水的玫瑰花瓣上時&#xff0c;這個空間便不再只是商品交易的場所。花店作為城市肌理中充滿生命力的細胞&#xff0c;承載著遠比銷售鮮花更豐富的社會意義。在快節奏的現代生活中&#xff0c;一束鮮花的綻放不僅是自然之…

AtomicStampedReference解決方案

1、通過引入版本戳(stamp)機制解決ABA問題&#xff1a; 每次修改時遞增版本號執行CAS時同時檢查值和版本號即使值相同但版本不同&#xff0c;操作也會失敗2、具體代碼實現 import java.util.concurrent.atomic.AtomicStampedReference;public class AtomicStampedReferenceDemo…

版本控制的詳細說明介紹(已有github賬號版)

說明 如果已經有一個GitHub賬號,這是一個很好的起點!版本控制是一個幫助你管理代碼或其他文件變化的工具,就像給你的項目加了一個“時間機器”,可以隨時回溯歷史、協作編輯,而不會亂套。下面我將從基礎開始,層層展開說明。整個內容分為幾個部分:介紹、原理、用途、操作…

基于Github Pages搭建個人博客站點:hexo環境搭建、本地預覽與發布

步驟確認 Hexo 博客的源文件在哪里安裝 Hexo 命令行工具&#xff1a;npm install -g hexo-cli在源文件目錄中使用 hexo new "文章標題" 創建新文章編輯生成的 Markdown 文件使用 hexo generate 生成靜態文件使用 hexo deploy 部署到這個 GitHub Pages 倉庫設置Hexo博…

Shell腳本實現自動封禁惡意掃描IP

iptables 簡介我們使用iptables工具實現功能iptables 是 Linux 系統上最常用的防火墻工具&#xff0c;可以指定策略。Shell文件創建首先我們先創建文件scanners.shvim /usr/local/bin/auto_block_ip.sh我的目標是每10分鐘自動掃描&#xff0c;再10分鐘內一個IP訪問50次以上就就…

LeetCode_哈希表

哈希表&#xff08;散列表&#xff09;一、哈希表二、有效的字母異位詞1、有效的字母異位詞(力扣242)2、贖金信(力扣383)3、字母異位詞分組(力扣49)4、找到字符串中所有字母異位詞(力扣438)三、兩個數組的交集1、兩個數組的交集(力扣349)2、兩個數組的交集 II(力扣350)三、其他…

2.變量和常量

1.變量2.2 變量的基本使用2.3 變量的本質 2.4 變量命名規則與規范 2.5 變量拓展-數組 1.數組的基本使用 2.常量

Java并發核心基礎解析

目錄 一、背景 二、Java線程模型 三、Synchronized實現原理 3.1 鎖的使用 3.2 解釋執行 3.3 JIT執行 3.4 鎖的狀態 3.5 monitorenter 3.5.1 偏向鎖 3.5.2 輕量級鎖 3.5.3 重量級鎖 3.6 monitorexit 3.6.1 偏向鎖 3.6.2 輕量級鎖 3.6.3 重量級 四、可見性的真相…

線程池111

線程池框圖C語言線程池詳解&#xff1a;從基礎到實現通俗理解線程池想象你開了一家快遞站&#xff0c;每天要處理很多包裹派送&#xff1a;?沒有線程池?&#xff1a;每來一個包裹就雇一個新快遞員&#xff0c;送完就解雇問題&#xff1a;頻繁招聘解雇成本高&#xff08;線程創…