高性能网络编程 - select、 poll 、epoll 、libevent

文章目录

  • 概述
  • 优缺点
    • Select
    • Poll
    • Epoll
    • LibEvent

在这里插入图片描述


概述

  1. Select(选择):
    • Select 是一种传统的 I/O 多路复用机制,用于在类 Unix 操作系统(如 Linux)中同时管理多个文件描述符(如网络套接字或文件)。
    • 它允许程序监视多个 I/O 源以检测可读性或可写性,并在数据可读或可写时触发事件。
    • Select 相对简单,但在处理大量文件描述符时性能和可扩展性有限。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>int main()
{// 1.创建套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(0);}// 2. 绑定 ip, portstruct sockaddr_in addr;addr.sin_port = htons(10000);addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));if(ret == -1){perror("bind");exit(0);}// 3. 监听ret = listen(lfd, 100);if(ret == -1){perror("listen");exit(0);}// 4. 等待连接 -> 循环// 检测 -> 读缓冲区, 委托内核去处理// 数据初始化, 创建自定义的文件描述符集fd_set rdset, tmp; FD_ZERO(&rdset);FD_SET(lfd, &rdset);int maxfd = lfd;while(1){// 委托内核检测tmp = rdset;ret = select(maxfd+1, &tmp, NULL, NULL, NULL);if(ret == -1){perror("select");exit(0);}// 检测的度缓冲区有变化// 有新连接if(FD_ISSET(lfd, &tmp)){// 接收连接请求struct sockaddr_in sockcli;int len = sizeof(sockcli);// 这个accept是不会阻塞的int connfd = accept(lfd, (struct sockaddr*)&sockcli, &len);// 委托内核检测connfd的读缓冲区FD_SET(connfd, &rdset);maxfd = connfd > maxfd ? connfd : maxfd;}// 通信, 有客户端发送数据过来for(int i=lfd+1; i<=maxfd; ++i){// 如果在集合中, 说明读缓冲区有数据if(FD_ISSET(i, &tmp)){char buf[128];int ret = read(i, buf, sizeof(buf));if(ret == -1){perror("read");exit(0);}else if(ret == 0){printf("对方已经关闭了连接...\n");FD_CLR(i, &rdset);close(i);}else{printf("客户端say: %s\n", buf);write(i, buf, strlen(buf)+1);}}}}close(lfd);return 0;
}
  1. Poll(轮询):
    • Poll 是另一种在类 Unix 系统中可用的 I/O 多路复用机制。在性能和可扩展性方面优于 select。
    • 与 select 类似,poll 允许程序监视多个文件描述符,但它可以更高效地处理大量文件描述符。
    • Poll 仍然广泛使用,但像 epoll 这样的更现代替代方案因性能更好而备受青睐。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <poll.h>int main()
{// 1.创建套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(0);}// 2. 绑定 ip, portstruct sockaddr_in addr;addr.sin_port = htons(10000);addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));if(ret == -1){perror("bind");exit(0);}// 3. 监听ret = listen(lfd, 100);if(ret == -1){perror("listen");exit(0);}// 4. 等待连接 -> 循环// 检测 -> 读缓冲区, 委托内核去处理// 数据初始化, 创建自定义的文件描述符集struct pollfd fds[1024];// 初始化for(int i=0; i<1024; ++i){fds[i].fd = -1;fds[i].events = POLLIN;}fds[0].fd = lfd;int maxfd = 0;while(1){// 委托内核检测ret = poll(fds, maxfd+1, -1);if(ret == -1){perror("select");exit(0);}// 检测的度缓冲区有变化// 有新连接if(fds[0].revents & POLLIN){// 接收连接请求struct sockaddr_in sockcli;int len = sizeof(sockcli);// 这个accept是不会阻塞的int connfd = accept(lfd, (struct sockaddr*)&sockcli, &len);// 委托内核检测connfd的读缓冲区int i;for(i=0; i<1024; ++i){if(fds[i].fd == -1){fds[i].fd = connfd;break;}}maxfd = i > maxfd ? i : maxfd;}// 通信, 有客户端发送数据过来for(int i=1; i<=maxfd; ++i){// 如果在集合中, 说明读缓冲区有数据if(fds[i].revents & POLLIN){char buf[128];int ret = read(fds[i].fd, buf, sizeof(buf));if(ret == -1){perror("read");exit(0);}else if(ret == 0){printf("对方已经关闭了连接...\n");close(fds[i].fd);fds[i].fd = -1;}else{printf("客户端say: %s\n", buf);write(fds[i].fd, buf, strlen(buf)+1);}}}}close(lfd);return 0;
}
  1. Epoll(事件轮询):
    • Epoll(事件轮询)是一种较新且高效的 I/O 事件通知机制,主要用于 Linux
    • 与 select 和 poll 不同,epoll 专为高性能 I/O 事件处理而设计。它可以有效地管理大量文件描述符而不会显著降低性能。
    • Epoll 特别适用于构建可扩展和高性能的网络服务器和应用程序。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/epoll.h>int main()
{// 1.创建套接字int lfd = socket(AF_INET, SOCK_STREAM, 0);if(lfd == -1){perror("socket");exit(0);}// 2. 绑定 ip, portstruct sockaddr_in addr;addr.sin_port = htons(10000);addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));if(ret == -1){perror("bind");exit(0);}// 3. 监听ret = listen(lfd, 100);if(ret == -1){perror("listen");exit(0);}// 创建epoll树int epfd = epoll_create(1000);if(epfd == -1){perror("epoll_create");exit(0);}// 将监听lfd添加到树上struct epoll_event ev;// 检测事件的初始化ev.events = EPOLLIN ;ev.data.fd = lfd;epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);struct epoll_event events[1024];// 开始检测while(1){int nums = epoll_wait(epfd, events, sizeof(events)/sizeof(events[0]), -1);printf("numbers = %d\n", nums);// 遍历状态变化的文件描述符集合for(int i=0; i<nums; ++i){int curfd = events[i].data.fd;// 有新连接if(curfd == lfd){struct sockaddr_in clisock;int len = sizeof(clisock);int connfd = accept(lfd, (struct sockaddr*)&clisock, &len);if(connfd == -1){perror("accept");exit(0);}// 将通信的fd挂到树上//ev.events = EPOLLIN | EPOLLOUT;ev.events = EPOLLIN;ev.data.fd  = connfd;epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);}// 通信else{// 读事件触发, 写事件触发if(events[i].events & EPOLLOUT) {continue;}char buf[128];int count = read(curfd, buf, sizeof(buf));if(count == 0){printf("client disconnect ...\n");close(curfd);// 从树上删除该节点epoll_ctl(epfd, EPOLL_CTL_DEL, curfd, NULL);}else if(count == -1){perror("read");exit(0);}else{// 正常情况printf("client say: %s\n", buf);write(curfd, buf, strlen(buf)+1);}}}}close(lfd);return 0;
}
  1. Libevent(事件库):
    • Libevent 是一个提供了简单和一致的事件通知机制 API 的 C 库,包括 select、poll、epoll 等多种机制。
    • 它允许开发人员编写可移植且高效的网络和事件驱动软件。
    • Libevent 抽象了不同平台和事件通知机制之间的差异,使开发人员能够更容易地编写能够在不同系统上运行而无需担心底层细节的代码。

sever

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <event2/event.h>int main()
{// 1. 创建事件处理框架struct event_base* base = event_base_new();// 打印支持的IO转接函数const char** method = event_get_supported_methods();for(int i=0; method[i] != NULL; ++i){printf("%s\n", method[i]);}printf("current method: %s\n", event_base_get_method(base));// 创建子进程pid_t pid = fork();if(pid == 0){// 子进程中event_base也会被复制,在使用这个base时候要重新初始化event_reinit(base); }// 2. 释放资源event_base_free(base);return 0;
}

client

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <event2/bufferevent.h>// read缓冲区的回调
void read_cb(struct bufferevent* bev, void* arg)
{printf("arg value: %s\n", (char*)arg);// 读缓冲区的数据char buf[128];int len = bufferevent_read(bev, buf, sizeof(buf));printf("read data: len = %d, str = %s\n", len, buf);// 回复数据bufferevent_write(bev, buf, len);printf("数据发送完毕...\n");
}// 写缓冲区的回调
// 调用的时机: 写缓冲区中的数据被发送出去之后, 该函数被调用
void write_cb(struct bufferevent* bev, void* arg)
{printf("arg value: %s\n", (char*)arg);printf("数据已经发送完毕...xxxxxxxxxxxx\n");
}// 事件回调
void events_cb(struct bufferevent* bev, short event, void* arg)
{if(event & BEV_EVENT_ERROR){printf("some error happened ...\n");}else if(event & BEV_EVENT_EOF){printf("server disconnect ...\n");}// 终止连接bufferevent_free(bev);
}void send_msg(evutil_socket_t fd, short ev, void * arg)
{// 将写入到终端的数据读出char buf[128];int len = read(fd, buf, sizeof(buf));// 发送给服务器struct bufferevent* bev = (struct bufferevent*)arg;bufferevent_write(bev, buf, len);
}int main()
{struct event_base * base = event_base_new();// 1. 创建通信的套接字struct bufferevent* bufev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);// 2. 连接服务器struct sockaddr_in addr;addr.sin_family = AF_INET;addr.sin_port = htons(9898);    // 服务器监听的端口inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr.s_addr);// 这个函数调用成功, == 服务器已经成功连接bufferevent_socket_connect(bufev, (struct sockaddr*)&addr, sizeof(addr));// 3. 通信// 给bufferevent的缓冲区设置回调bufferevent_setcb(bufev, read_cb, write_cb, events_cb, (void*)"hello, world");bufferevent_enable(bufev, EV_READ);// 创建一个普通的输入事件struct event* myev = event_new(base, STDIN_FILENO, EV_READ|EV_PERSIST, send_msg, bufev);event_add(myev, NULL);event_base_dispatch(base);event_free(myev);event_base_free(base);return 0;
}

总之,这些是用于编程的工具和库,用于高效地处理多个 I/O 操作,特别是在网络通信的背景下。Select 和 poll 是较旧、性能较低的选项,而 epoll 是一种高性能的替代方案。Libevent 是一个库,简化了使用这些机制的工作,同时提供了跨不同平台的可移植性。


优缺点

以下是每种方案的优点和缺点:

Select

优点:

  • 简单易用,易于理解和实现。
  • 在小规模连接数的情况下,性能通常足够。
  • 跨平台兼容性较好。

缺点:

  • 性能不够高,随着连接数的增加,性能会下降。
  • 需要维护大量文件描述符集合,开销较大。
  • 对于大规模并发连接,存在效率问题。

Poll

优点:

  • 性能相对于Select有所提升,可以处理更多文件描述符。
  • 在某些场景下,仍然是一个可行的选择。

缺点:

  • 仍然存在性能问题,特别是在大规模并发连接的情况下。
  • 对于每个事件的轮询会导致不必要的开销。

Epoll

优点:

  • 高性能:Epoll 针对大规模并发连接进行了优化,性能较高。
  • 有效地管理大量文件描述符,不会随连接数增加而降低性能。
  • 支持边缘触发模式,只在事件发生时通知应用程序,减少了不必要的处理开销。
  • 仅在Linux系统上可用。

缺点:

  • 不具备跨平台兼容性,只能在Linux上使用。
  • 相对于Select和Poll,编写代码可能稍微复杂一些。

LibEvent

优点:

  • 提供统一的事件通知 API,能够适应不同操作系统和事件通知机制。
  • 简化了跨平台开发,使代码更具可移植性。
  • 在性能方面,可以利用底层高性能机制,如Epoll,以提高性能。

缺点:

  • 与直接使用底层机制相比,可能引入轻微的性能开销。
  • 需要学习Libevent的API和概念。

总的来说,选择哪种方案取决于你的应用需求。如果需要处理大规模并发连接,特别是在Linux上,Epoll通常是最佳选择。对于跨平台开发,Libevent可以提供便利。如果只需处理少量连接,Select和Poll也可以工作,但性能可能不如Epoll。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/184815.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java2 - 数据结构

5 数据类型 5.1 整数类型 在Java中&#xff0c;数据类型用于定义变量或表达式可以存储的数据的类型。Java的数据类型可分为两大类&#xff1a;基本数据类型和引用数据类型。 byte&#xff0c;字节 【1字节】表示范围&#xff1a;-128 ~ 127 即&#xff1a;-2^7 ~ 2^7 -1 sho…

Rust学习日记(二)变量的使用--结合--温度换算/斐波那契数列--实例

前言&#xff1a; 这是一个系列的学习笔记&#xff0c;会将笔者学习Rust语言的心得记录。 当然&#xff0c;这并非是流水账似的记录&#xff0c;而是结合实际程序项目的记录&#xff0c;如果你也对Rust感兴趣&#xff0c;那么我们可以一起交流探讨&#xff0c;使用Rust来构建程…

Camera Raw 16 v16.0.0

Camera Raw 16是一款允许摄影师处理原始图像文件的软件PS增效工具。原始图像文件是未经相机内部软件处理的数码照片&#xff0c;因此包含相机传感器捕获的所有信息。Camera Raw 为摄影师提供了一种在将原始文件转换为更广泛兼容的格式&#xff08;如 JPEG 或 TIFF&#xff09;之…

基于React使用swiperjs实现竖向滚动自动轮播

很多文章&#xff0c;都只提供了js部分&#xff0c;包括官方的文档也只有js部分&#xff0c;如果css设置不正确&#xff0c;会导致轮播图不自动播放。 使用的swiper版本&#xff1a;v11.0.3 文档 https://swiperjs.com/get-startedhttps://swiperjs.com/react 实现效果 使…

AD教程 (十)Value值的核对

AD教程 &#xff08;十&#xff09;Value值的核对 填写器件位号 直接根据原理图的原始编号进行更改 通过位号编辑器快速更改 点击工具&#xff0c;选择标注&#xff0c;选择原理图标注&#xff0c;进入位号编辑器 可以在位号编辑器中 设置处理顺序&#xff0c;从上往下还是从…

layui form 中input输入框长度的统一设置

Layui.form中使用class"layui-input-inline"就可轻松将元素都放到一行&#xff0c;但如果元素过多&#xff0c;就会自动换行。那就需要手动设置input框的长度。 像这种情况&#xff1a; 其实只需要添加css样式就可修改了 .layui-form-item .layui-input-inline {wid…

奔驰E Coupe 升级鼠标按键 操作简单 完美结合

人机交互系统正是汽车智能化发展的产物&#xff0c;它实现了人与车之间的互联。不知道大家有没有发现&#xff0c;在很多奔驰车的中央扶手箱前&#xff0c;有一块类似于“鼠标”的操作区&#xff0c;它并不是我们常见的换挡杆&#xff0c;而是奔驰研发的独立影音控制系统COMAND…

【Dockerfile镜像实战】构建LNMP环境并运行Wordpress网站平台

【Dockerfile镜像实战】构建LNMP环境并运行Wordpress网站平台 Nginx部署MySQL部署PHP部署 Nginx部署 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0[rootlocalhost ~]# docker pull centos:7[rootlocalhost ~]# docker imagescd /opt mkdir n…

从行车记录仪恢复已删除/丢失视频的方法

“我的车里有行车记录仪。几天前&#xff0c;当我下班回家时&#xff0c;一辆卡车不知从哪里冒出来撞向了我。我们的两辆车都损坏了&#xff0c;但幸运的是&#xff0c;没有人受伤。我曾与卡车司机就修理我的汽车进行过会面&#xff0c;但他说我有错。我需要查看我的行车记录仪…

怎么学编程效率高,编程练习网站编程软件下载,中文编程开发语言工具下载

怎么学编程效率高&#xff0c;编程练习网站编程软件下载&#xff0c;中文编程开发语言工具下载 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#xff0c;而且可以开发大型的…

Harbor私有仓库

Harbor私有仓库 文章目录 Harbor私有仓库Harbor简介&#xff1a;Harbor 提供了以下主要功能和特性&#xff1a;优缺点&#xff1a;环境说明&#xff1a;部署harbor1.永久关闭防火墙和seliux&#xff0c;配置阿里云源&#xff0c;添加映射关系2.安装docker&#xff0c;开启docke…

【论文阅读】Generating Radiology Reports via Memory-driven Transformer (EMNLP 2020)

资料链接 论文原文&#xff1a;https://arxiv.org/pdf/2010.16056v2.pdf 代码链接&#xff08;含数据集&#xff09;&#xff1a;https://github.com/cuhksz-nlp/R2Gen/ 背景与动机 这篇文章的标题是“Generating Radiology Reports via Memory-driven Transformer”&#xf…

基于单片机GP2D12测距-proteus仿真-源程序

基于51单片机红外测距-proteus仿真-源程序 一、系统方案 本设计采用51单片机作为主控器&#xff0c;液晶1602显示&#xff0c;GP2D12采集距离值&#xff0c;按键设置报警阀值&#xff0c;测量值超过阀值&#xff0c;蜂鸣器报警。 二、硬件设计 原理图如下&#xff1a; 三、单…

Python进阶教程:pandas数据分析实践示例总结

文章目录 前言一、分析数据文件二、数据预处理关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前言 在近日的py…

相机滤镜软件Nevercenter CameraBag Photo mac中文版特点介绍

Nevercenter CameraBag Photo mac是一款相机和滤镜应用程序&#xff0c;它提供了一系列先进的滤镜、调整工具和预设&#xff0c;可以帮助用户快速地优化和编辑照片。 Nevercenter CameraBag Photo mac软件特点介绍 1. 滤镜&#xff1a;Nevercenter CameraBag Photo提供了超过2…

微信小程序:怎么在一个js中修改另一个js的数据(这里通过缓存进行实现)

实例&#xff1a;现有两个页面index.js和category.js,我现在想在index.js中修改category.js的数据 初始数据 category [{name: 物流配送,list: [{id: 1,job: 外卖骑手,checked: true}, {id: 2,job: 快递员,checked: false}, {id: 3,job: 司机,checked: false}, {id: 4,job: …

蓝桥杯每日一题203.11.7

题目描述 题目分析 使用dp思维&#xff0c;当前位置是否可行是有上一位置推来&#xff0c;计算出最大的可行位置即可 #include <stdio.h> #include <string.h>#define N 256 int f(const char* s1, const char* s2) {int a[N][N];int len1 strlen(s1);int len2 …

react组件通信

目录 前言&#xff1a; 父子组件通信 子父组件通信 兄弟组件通信 总结 前言&#xff1a; React是一种流行的JavaScript库&#xff0c;用于构建现代化的、高性能的Web应用程序。在React中&#xff0c;组件是代码的构建块。组件通信是React中一个非常重要的概念&#xff0c;…

[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子

[动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子 文章目录 [动态规划] (十四) 简单多状态 LeetCode LCR 091.粉刷房子题目解析解题思路状态表示状态转移方程初始化和填表顺序返回值 代码实现总结 LCR 091. 粉刷房子 题目解析 (1) 一排房子&#xff0c;共有n个 (2) 染…

世微 DC-DC降压恒注驱动芯片 LED汽车大灯 过EMC认证 AP2400

产品特点 宽输入电压范围&#xff1a;5V&#xff5e;100V 可设定电流范围&#xff1a;10mA&#xff5e;6000mA 固定工作频率&#xff1a;150KHZ 内置抖频电路&#xff0c;降低对其他设备的 EMI 干扰 平均电流模式采样&#xff0c;恒流精度更高 0-100%占空比控制&#…