/*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;线程创…