使用epoll实现聊天服务
一、概述
1.epoll函数及相关结构体介绍
多路IO-epoll 将检测文件描述符的变化委托给内核去处理, 然后内核将发生变化的文件描述符对应的事件返回给应用程序. (通俗点讲就是我们不用关心文件描述符的变化了,内核帮我们干了,并且内核把那些有变化的具体的文件描述符都会返回回来) 函数介绍: int epoll_create(int size); 函数说明: 创建一个树根 参数说明: size: 最大节点数, 此参数在linux 2.6.8已被忽略, 但必须传递一个大于0的数. 返回值: 成功: 返回一个大于0的文件描述符, 代表整个树的树根. 失败: 返回-1, 并设置errno值. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 函数说明: 将要监听的节点在epoll树上添加, 删除和修改 参数说明: epfd: epoll树根 op: EPOLL_CTL_ADD: 添加事件节点到树上 EPOLL_CTL_DEL: 从树上删除事件节点 EPOLL_CTL_MOD: 修改树上对应的事件节点 fd: 事件节点对应的文件描述符 event: 要操作的事件节点 typedef union epoll_data { void *ptr; int fd;//文件描述符,可能是监听也可能是通讯 uint32_t u32; uint64_t u64; } epoll_data_t; struct epoll_event { uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; event.events常用的有: EPOLLIN: 读事件 EPOLLOUT: 写事件 EPOLLERR: 错误事件 EPOLLET: 边缘触发模式 event.data.fd: 要监控的事件对应的文件描述符 int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 函数说明:等待内核返回事件发生 参数说明: epfd: epoll树根 events: 传出参数, 其实是一个事件结构体数组 maxevents: 数组大小 timeout: -1: 表示永久阻塞 0: 立即返回 >0: 表示超时等待事件 返回值: 成功: 返回发生事件的个数 失败: 若timeout=0, 没有事件发生则返回; 返回-1, 设置errno值, epoll_wait的events是一个传出参数, 调用epoll_ctl传递给内核什么值, 当epoll_wait返回的时候, 内核就传回什么值,不会对struct event的结构体变量的值做任何修改.
2.案例:使用epoll编写一个简单的聊天服务器,要求:客户端发送什么,服务端就回复什么,如果服务端发送的是消息字母则转换为大写。
二、代码示例
//EPOLL高并发服务器编写(只有Linux系统支持epoll) #include <sys/epoll.h> #include <ctype.h> #include "wrap.h" int main(){ //0.定义变量 int ret; int n; int i; int k; int nready; int lfd; int cfd; int sockfd; char buf[1024]; socklen_t socklen; struct sockaddr_in svraddr; struct epoll_event ev; struct epoll_event events[1024]; //1.创建socket lfd = Socket(AF_INET,SOCK_STREAM,0); //2.绑定端口 int opt; setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));//设置端口复用 svraddr.sin_family = AF_INET; svraddr.sin_port = htons(8888); svraddr.sin_addr.s_addr = htonl(INADDR_ANY); Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in)); //3.监听 Listen(lfd,128); //4.创建一棵树 int epfd = epoll_create(1024); if(epfd<0){ perror("create epoll error"); return -1; } //5.将监听文件描述符上树 ev.data.fd = lfd; ev.events = EPOLLIN; epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev); //6.while(1)中epoll_wait监控文件描述符变化 while(1){ nready = epoll_wait(epfd,events,1024,-1); if(nready<0){ perror("epoll_wait error"); if(errno=EINTR){//如果是信号中断就不视为错误 continue; } break; } for(i=0;i<nready;i++){ //7.判断是监听文件描述符还是通讯文件描述符 sockfd = events[i].data.fd; if(sockfd==lfd){//8.如果是监听文件描述符调用Accept创建通讯文件描述符并上树 cfd = Accept(lfd,NULL,NULL); ev.data.fd = cfd; ev.events = EPOLLIN; epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&ev); continue; } //9.如果是通讯文件描述符,就进行读写操作 memset(buf,0x00,sizeof(buf)); n = Read(sockfd,buf,sizeof(buf)); if(n<0){ printf("n=[%d],buf=[%s]\n",n,buf); close(sockfd); //将sockfd对应的事件节点从epoll树上删除 epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL); }else{ printf("n=[%d],buf=[%s]\n",n,buf); for(k=0;k<n;k++){ buf[k] = toupper(buf[k]); } Write(sockfd,buf,n); } } } //10.最后关闭监听文件描述符 close(epfd);//关闭epoll根文件描述符 close(lfd); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
2020-12-15 Android onInterceptTouchEvent的move和up事件不执行
2020-12-15 RelativeLayout中layout_centerInParent不起作用
2014-12-15 Android 由 android:launchMode="singleInstance“引发的界面无法返回的情况