使用poll实现聊天服务
一、概述
poll函数介绍:
int poll(struct pollfd *fds, nfds_t nfds, int timeout); 函数说明: 跟select类似, 监控多路IO, 但poll不能跨平台. 参数说明: fds: 传入传出参数, 实际上是一个结构体数组 fds.fd: 要监控的文件描述符 fds.events: POLLIN---->读事件 POLLOUT---->写事件 fds.revents: 返回的事件 nfds: 数组实际有效内容的个数 timeout: 超时时间, 单位是毫秒. -1:永久阻塞, 直到监控的事件发生 0: 不管是否有事件发生, 立刻返回 >0: 直到监控的事件发生或者超时 返回值: 成功:返回就绪事件的个数 失败: 返回-1 若timeout=0, poll函数不阻塞,且没有事件发生, 此时返回-1, 并且errno=EAGAIN, 这种情况不应视为错误. struct pollfd { int fd; /* file descriptor */ 监控的文件描述符 short events; /* requested events */ 要监控的事件---不会被修改 short revents; /* returned events */ 返回发生变化的事件 ---由内核返回 }; 说明: 1 当poll函数返回的时候, 结构体当中的fd和events没有发生变化, 究竟有没有事件发生由revents来判断, 所以poll是请求和返回分离. 2 struct pollfd结构体中的fd成员若赋值为-1, 则poll不会监控. 3 相对于select, poll没有本质上的改变; 但是poll可以突破1024的限制. 在/proc/sys/fs/file-max查看一个进程可以打开的socket描述符上限. 如果需要可以修改配置文件: /etc/security/limits.conf 加入如下配置信息, 然后重启终端即可生效. * soft nofile 1024 软限制不能超过硬件限制,即:soft nofile的值不能超过hard nofile的值 * hard nofile 100000 soft和hard分别表示ulimit命令可以修改的最小限制和最大限制
案例:使用poll函数实现高并发服务器
二、代码示例
//IO多路复用技术poll函数的使用 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #include <arpa/inet.h> #include <netinet/in.h> #include <poll.h> #include "wrap.h" int main(){ int i;//for循环的索引 int n;//读取数据的个数 int lfd;//监控文件描述符 int cfd;//通讯文件描述符 int ret; int nready;//就绪的时间个数 int maxfd;//最大文件描述符 char buf[1024];//读写缓冲区大小 socklen_t len;// int sockfd;//通信文件描述符 fd_set tmpfds,rdfs; struct sockaddr_in svraddr,cliaddr; //创建socket,并返回监听文件描述符 lfd = Socket(AF_INET,SOCK_STREAM,0); //允许端口复用 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); ret = Bind(lfd,(struct sockaddr *)&svraddr,sizeof(struct sockaddr_in)); //监听 ret = Listen(lfd,128); //填充要监控的结构体数组client[i].fd=-1表示内核不监控 struct pollfd client[1024]; for(i=0;i<1024;i++){ client[i].fd = -1; } //将监听文件描述符委托给内核监控----监控读事件 client[0].fd = lfd; client[0].events = POLLIN;//表示读事件 maxfd = 0;//maxfd表示内核监控的范围 while(1){ nready = poll(client,maxfd+1,-1);//监控多路IO,返回就绪的事件个数 if(nready<0){//如果事件个数小于0说明出错了,直接退出进程即可 perror("poll error"); exit(1); } //有客户端连接请求(监听文件描述符=lfd,并且事件类型是读事件) if(client[0].fd = lfd &&(client[0].revents&POLLIN)){ //接收客户端连接并返回一个通讯文件描述符 cfd = Accept(lfd,NULL,NULL); //寻找client数组中可用位置(找到位置后将cfd加入数据交给内核监控) for(i=1;i<1024;i++){ if(client[i].fd ==-1){ client[i].fd = cfd; client[i].events = POLLIN; break; } } //如果没有可用位置则关闭连接 if(i==1024){ Close(cfd); continue; } if(maxfd<i){//可用就绪的文件描述符的最大索引 maxfd = i; } if(--nready==0){//如果就一个就绪的文件描述符,那就不用往下执行了 continue; } } //下面是有数据到来的情况(只用循环可用的文件描述符的最大索引即可) for(i=1;i<=maxfd;i++){ //若fd为-1 表示连接已经关闭或者没有连接 if(client[i].fd == -1){ continue; } sockfd = client[i].fd; memset(buf,0x00,sizeof(buf)); n = Read(sockfd,buf,sizeof(buf));//从内核中读取缓冲区数据 if(n<=0){ printf("read error or client closed,n==[%d]\n",n); Close(sockfd); client[i].fd = -1;//fd为-1,表示不再让内核监控 }else{ printf("read over ,n=[%d],buf=[%s]\n",n,buf); write(sockfd,buf,n);//给客户端回复数据 } if(--nready==0){ break; } } } Close(lfd);//最后要关闭监听文件描述符 return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
2020-12-15 Android onInterceptTouchEvent的move和up事件不执行
2020-12-15 RelativeLayout中layout_centerInParent不起作用
2014-12-15 Android 由 android:launchMode="singleInstance“引发的界面无法返回的情况