目录

poll是对select缺点进行一个改进(没有解决内核态和用户态转变的问题,只是解决了1024比特位的限制以及不能重用每次都需要重置的问题。

rbr红黑树+rdlist双链表
- #include
- int epoll_create(int size);
创建一个新的epoll实例。在内核中创建了一个数据,这个数据中有两个比较重要的数据,一个是需要检测的文件描述符的信息(红黑树),还有一个是就绪列表,存放检测到数据发送改变的文件描述符信息(双向链表)。
- 参数:
size : 目前没有意义了。随便写一个数,必须大于0
- 返回值:
-1 : 失败
> 0 : 文件描述符,操作epoll实例的
- struct epoll_event {
- uint32_t events; /* Epoll events */
- epoll_data_t data; /* User data variable */
- };
常见的Epoll检测事件:
- EPOLLIN
- EPOLLOUT
- EPOLLERR
- EPOLLET
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
- 参数:
- epfd : epoll实例对应的文件描述符
- op : 要进行什么操作 EPOLL_CTL_ADD: 添加 EPOLL_CTL_MOD: 修改 EPOLL_CTL_DEL: 删除
- fd : 要检测的文件描述符
- event : 检测文件描述符什么事情
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
- 参数:
- epfd : epoll实例对应的文件描述符
- events : 传出参数,保存了发送了变化的文件描述符的信息
- maxevents : 第二个参数结构体数组的大小
- timeout : 阻塞时间 - 0 : 不阻塞 - -1 : 阻塞,直到检测到fd数据发生变化,解除阻塞 - > 0 : 阻塞的时长(毫秒)
- 返回值: - 成功,返回发送变化的文件描述符的个数 > 0 - 失败 -1
- #include
- #include
- int fcntl(int fd, int cmd, ... );
参数:
fd:表示需要操作的文件描述符
cmd:表示对文件描述符进行如何操作
1.F_DUPFD:复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符
int ret = fcntl(fd,F_DUPFD);
2.F_GETFL:获取指定的文件描述符文件状态flag
获取的flag和我们通过open函数传递的flag是一个东西
3.F_SETFL:设置文件描述符文件状态flag
必选项:O_RDONLY,O_WRONDLY,O_RDWR 不可以被修改
可选项:O_APPEND,O_NONBLOCK
O_APPEND 表示追加数据
O_NONBLOCK 设置成非阻塞
假设委托内核检测读事件 -> 检测fd的读缓冲区 读缓冲区有数据 - > epoll检测到了会给用户通知
a.用户不读数据,数据一直在缓冲区,epoll 会一直通知
b.用户只读了一部分数据,epoll会通知
c.缓冲区的数据读完了,不通知
LT(level - triggered)是缺省的工作方式,并且同时支持 block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的。
服务器案例
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main()
- {
- //1.创建socket
- int lfd = socket(AF_INET,SOCK_STREAM,0);
- if(lfd==-1){
- perror("socket");
- exit(-1);
- }
- printf("创建socket成功\n");
-
- //2.绑定
- struct sockaddr_in saddr;
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(9999);
- saddr.sin_addr.s_addr=INADDR_ANY;
- int ret=bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if(ret==-1){
- perror("bind");
- exit(-1);
- }
- printf("绑定成功\n");
-
- //3.监听
- ret=listen(lfd,10);
- if(ret==-1){
- perror("listen");
- exit(-1);
- }
- printf("监听成功\n");
-
- //4.创建epoll实例
- int epfd=epoll_create(1);
- if(epfd==-1){
- perror("epoll_create");
- exit(-1);
- }
- printf("创建epoll实例成功\n");
-
- //5.加入监听的文件描述相关信息
- struct epoll_event epev;
- epev.events=EPOLLIN;
- epev.data.fd=lfd;
- epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);
-
- struct epoll_event epevs[1024];
- while(1){
- ret = epoll_wait(epfd,epevs,1024,-1);
- if(ret == -1){
- perror("epoll_wait");
- exit(-1);
- }
- printf("ret = %d\n",ret);
- for(int i=0;i
- int curfd=epevs[i].data.fd;
- if(curfd==lfd){
- struct sockaddr_in cliaddr;
- int len = sizeof(cliaddr);
- int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
- epev.events=EPOLLIN;
- epev.data.fd=cfd;
- epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
- }else {
- if(epevs[i].events&EPOLLOUT){
- continue;
- }
- //有数据到达需要通信
- char buf[1024]={0};
- int len = read(curfd,buf,sizeof(buf));
- if(len==-1){
- perror("read");
- exit(-1);
- }
- else if(len==0){
- printf("client closed...\n");
- epoll_ctl(epfd,EPOLL_CTL_DEL,curfd,NULL);
- close(curfd);
- }
- else if(len>0){
- printf("read buf = %s\n",buf);
- memset(buf,0,sizeof (buf));
- sprintf(buf,"recv");
- write(curfd,buf,strlen(buf)+1);
- }
- }
- }
- }
- close(lfd);
- close(epfd);
- return 0;
- }
客户端案例
- #include
- #include
- #include
- #include
// read write close - #include
- int main()
- {
- //1.创建套接字
- int cfd=socket(AF_INET,SOCK_STREAM,0);
- if(cfd==-1){
- perror("socket");
- exit(0);
- }
- printf("1.客户端创建套接字成功\n");
-
- //2.连接服务器
- struct sockaddr_in saddr;
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(9999);
- inet_pton(AF_INET,"192.168.108.128",&saddr.sin_addr);//服务器的ip地址
- /*
- p:点分十进制的IP字符串,n:表示network,网络字节序的整数
- int inet_pton(int af, const char *src, void *dst);
- af:地址族: AF_INET AF_INET6
- src:需要转换的点分十进制的IP字符串
- dst:转换后的结果保存在这个里面
- */
- int ret=connect(cfd,(struct sockaddr*)&saddr,sizeof(saddr));
-
- if(ret==-1){
- perror("connect");
- exit(-1);
- }
- printf("2.客户端服务器连接成功\n");
-
- //3.通信
- char recvBuf[1024]={0};
- while(1){
- printf("请输入你想要发给服务端的内容\n");
- memset(recvBuf,0,sizeof(recvBuf));
- scanf("%s",recvBuf);
- write(cfd,recvBuf,strlen(recvBuf)+1);
- memset(recvBuf,0,sizeof(recvBuf));
- int data=read(cfd,recvBuf,sizeof(recvBuf));
- if(data>0)
- {
- printf("服务端返回数据:%s\n",recvBuf);
- }
- else if(data==0){
- printf("服务端断开连接\n");
- }else if(data<0){
- perror("read");
- exit(-1);
- }
- }
- close(cfd);
- return 0;
- }
ET模式(边沿触发模式)
假设委托内核检测读事件 -> 检测fd的读缓冲区 读缓冲区有数据 - > epoll检测到了会给用户通知
a.用户不读数据,数据一致在缓冲区中,epoll下次检测的时候就不通知了
b.用户只读了一部分数据,epoll不通知
c.缓冲区的数据读完了,不通知
ET(edge - triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。ET 模式在很大程度上减少了epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
服务器案例:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int main(){
-
- int lfd = socket(AF_INET,SOCK_STREAM,0);
- if(lfd==-1){
- perror("socket");
- exit(-1);
- }
- printf("服务器创建socket成功\n");
-
- struct sockaddr_in saddr;
- saddr.sin_addr.s_addr=INADDR_ANY;
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(9999);
- int ret=bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if(ret == -1){
- perror("bind");
- exit(-1);
- }
- printf("socket绑定成功\n");
-
- ret = listen(lfd,8);
- if(ret == -1){
- perror("listen");
- exit(-1);
- }
- printf("监听套接字成功\n");
-
- int epfd = epoll_create(1);
- if(epfd==-1){
- perror("epoll_create");
- exit(-1);
- }
- struct epoll_event epev;
- epev.events=EPOLLIN;
- epev.data.fd=lfd;
- ret = epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&epev);
-
- struct epoll_event epevs[1024];
- while(1){
- ret = epoll_wait(epfd,epevs,1024,-1);
- if(ret == -1){
- perror("epoll_wait");
- exit(-1);
- }
-
- printf("ret = %d\n",ret);
- for(int i=0;i
- int curfd=epevs[i].data.fd;
- if(curfd==lfd){
- struct sockaddr_in cliaddr;
- int len = sizeof(cliaddr);
- int cfd = accept(lfd,(struct sockaddr*)&cliaddr,&len);
-
- //这里是设置cfd属性非阻塞 因为et模式只支持文件非阻塞
- int flag = fcntl(cfd,F_GETFL);
- flag |= O_NONBLOCK;
-
- epev.events=EPOLLIN|EPOLLET;//设置et模式
- epev.data.fd=cfd;
- epoll_ctl(epfd,EPOLL_CTL_ADD,cfd,&epev);
- }else{
- if(epevs[i].events&EPOLLOUT){
- continue;
- }
-
- //循环读取所有的数据
- char buf[5];
- int len=0;
- while((len=read(curfd,buf,sizeof(buf)))>0){
- //打印数据
- write(STDOUT_FILENO,buf,len);
- write(curfd,buf,len);
- }
- puts("");
- if(len==0){
- printf("连接断开\n");
- }else if(len==-1){
- if(errno==EAGAIN){
- printf("数据过多\n");
- }else{
- perror("read");
- exit(-1);
- }
- }
- }
- }
- }
- close(lfd);
- close(epfd);
- return 0;
- }
-
相关阅读:
apple pencil一定要买吗?Ipad电容笔推荐品牌
数据分享|SAS数据挖掘EM贷款违约预测分析:逐步Logistic逻辑回归、决策树、随机森林...
Unity DOTS中的baking(五)prefabs
Python线程(thread)
我的创作纪念日 - 一周年 【2022-09-17】
Blob把html导出为excel文件
[Python]Open CV 基础知识学习
阿里云原生应用平台架构师田伟:应用架构的规划、治理与演进
环境变量简单解释
MATLAB主窗口与编辑器窗口分开为两个界面的解决办法
-
原文地址:https://blog.csdn.net/l_ethan/article/details/125988385