首页 > 编程语言 >网络编程4 poll和epoll

网络编程4 poll和epoll

时间:2024-03-16 17:04:42浏览次数:24  
标签:epoll int 编程 fd 事件 poll events

网络编程4

了解多路复用IO 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: 直到监控的事件发生或者超时

poll返回值:
=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
* hard nofile 100000
soft和hard分别表示ulimit命令可以修改的最小限制和最大限制

poll开发服务器端思路

1 创建socket,得到监听文件描述符 lfd----socket()
2 设置端口复用---------setsocket()
3 绑定-----bind()
4 struct pollfd client[1024]
client[0].fd=lfd
client[0].events = POLLIN
for(i=1;i<1024i++)
client[i].fd=-1;
int maxi=0;
while(1)
{
nready = poll(client,maxi+1,-1);
if(nready<0)//异常情况
{
if(errnoEINTR)//信号被中断
continue;
break;
}
//有客户端连接请求到来
if(client[0].revents
POLLIN)
{
接受新客户端连接
cfd=accept(lfd,NULL,NULL)
寻找可用位置
for(i=0;i<1024;i++)
{
if(client[i].fd-1)
{
client[i].fd=cfd;
client[i].events=POLLIN;
break;
}
}
客户端连接数达到最大值
if(1024
i)
{
close(cfd);
continue;
}
修改数组大小
if(maxi <i)
maxi=i;

if(--nready0)
{continue;}
}
有客户端发送数据的情况
for(i=1;i<maxi;++i)
{
sockfd = client[i].fd;
如果client数组fd已经为-1,表示已经不再让你内核监控了,已经close
if(client[i].fd
-1)
continue;
if(client[i].revents==POLLIN)
{
read数据
n=read(sockfd,buf,sizeof(buf))
if(n<=0)
{关闭客户端连接
close(sockfd);
client[i].fd=-1;
}
else
{
发送给客户端
write(sockfd,buf,n)
}
}
}
}

熟练使用epoll多路IO模型

关于epoll函数介绍

#include<sys/epoll.h>

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.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的结构体变量的值做任何修改.

epoll模型服务器代码编写思路

1 创建socket,得到监听文件描述符 lfd----socket()
2 设置端口复用---------setsocket()
3 绑定-----bind()
4 监听-----listen()
5 创建epoll树
int epfd = epoll_create()
监听文件描述符上树
struct epoll_event ev
ev.evetns = EPOLLIN
ev.data.fd=lfd
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev)
while(1)
{
nready = epoll_wait(epfd,events,1024,-1)
if(nready<0)
{
if(errnoEINTR)
continue; //信号被中断
break
}
for(i=0;i<nready;i++)
{
sockfd=events[i].data.fd
有客户端连接请求到了
if(sockfd
lfd)
{
cfd=accept(lfd,NULL,NULL)
将cfd对应的读事件上epoll树
ev.data.fd=cfd
ev.evetns=EPOLLIN
epoll_ctl(epfd,EPOLL_CTL_ADD,lfd,&ev);
continue;
}
有客户端数据发送到了
n=read(sockfd,buf,sizeof(buf))
if(n<=0)发送异常
{
close(sockfd);
下树,将socket对应的事件节点从epoll树上删除
epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL)
perr("read error");
continue;
}
else
{
write(sockfd,buf,n)
}
}
}
关闭事件描述符
close(lfd)

epoll ET/LT触发模式并且实现

epoll的两种模式ET和LT模式

  • 水平触发Level Triggered (LT) : 高电平代表1
    epoll默认的模式,只要缓冲区中有数据, 就一直通知
  • 边缘触发Edge Triggered (ET) : 电平有变化就代表1
    缓冲区中有数据只会通知一次, 之后再有数据才会通知.(若是读数据的时候没有读完, 则剩余的数据不会再通知, 直到有新的数据到来)
    修改成ET模式
ev.evetns=EPOLLIN | EPOLLET;

如果在ET模式下把数据一口气读完?
循环读数据,直到读完,而且需要把通信描述符设置成非阻塞模式。

//将cfd设置为非阻塞
 int flag = fcntl(cfd, F_GETFL);
 flag |= O_NONBLOCK;
 fcntl(cfd, F_SETFL, flag);
...

//有客户端发送数据过来
			memset(buf, 0x00, sizeof(buf));
			while(1)
			{
				n = Read(sockfd, buf, 2);
				printf("n==[%d]\n", n);
				//读完数据的情况
				if(n==-1)
				{
					printf("read over, n==[%d]\n", n);
					break;
				}
				//对方关闭连接或者读异常
				if(n==0 || (n<0&&n!=-1))
				{
					printf("n==[%d], buf==[%s]\n", n, buf);
					close(sockfd);
					//将sockfd对应的事件就节点从epoll树上删除
					epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
					break;

				}
				else //正常读到数据的情况
				{
					printf("n==[%d], buf==[%s]\n", n, buf);
					for(k=0; k<n; k++)
					{
						buf[k] = toupper(buf[k]);
					}
					Write(sockfd, buf, n);
				}
			}
		

了解epoll反应堆模式

epoll反应堆实际上是应用了C++的封装思想,一个事件的产生会触发一系列连锁反应,事件产生之后最终调用的是回调函数。

epoll反应堆的主要用途是处理高并发的I/O事件。在网络编程中,当服务器需要同时处理来自多个客户端的连接请求或数据交换时,使用epoll反应堆可以显著提高服务器的性能和响应速度。通过监听多个文件描述符的状态变化,epoll反应堆能够在单个线程或进程内高效地处理多个并发事件,避免了传统多线程或多进程模型中的线程切换和同步开销。

标签:epoll,int,编程,fd,事件,poll,events
From: https://www.cnblogs.com/AndreaDO/p/18076229

相关文章

  • 网络编程笔记目录
    网络编程笔记基础概念socket编程https://www.cnblogs.com/AndreaDO/p/18072371三次握手和四次挥手高并发服务器(多进程与多线程)https://www.cnblogs.com/AndreaDO/p/18073716selecthttps://www.cnblogs.com/AndreaDO/p/18074786poll和epollhttps://www.cnblogs.com/And......
  • 将C++模板应用于多文件编程
    C++模板在将函数应用于多文件编程时,我们通常是将函数定义放在源文件(.cpp文件)中,将函数声明放在头文件(.h文件)中,使用函数时引入(#include命令)对应的头文件即可。编译是针对单个源文件的,只要有函数声明,编译器就能知道函数调用是否正确;而将函数调用和函数定义对应起来的过程,可以延迟到......
  • 【Linux系统编程】静态库与动态库
    静态库与动态库静态库的制作和使用编写库文件源代码和头文件。将所有需要做成库的源文件生成目标文件:gcc-c{filename}.c-o{filename}.o用ar工具将要做成库的目标文件打包:arrcslib{库名}.a{filename-1}.o...{filename-n}.o将使用库的源文件与库文件一起编译:gcc{......
  • 实验1_C语言输入输出和简单程序应用编程
    task11#include<stdio.h>2#include<stdlib.h>3intmain()4{56printf("o\to\n");7printf("<H>\t<H>\n");8printf("II\tII\n");910system("pa......
  • 网络编程3 端口复用-多路IO转接select
    网络编程3端口复用-多路IO转接TCP状态转换图端口复用防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中。解决端口复用......
  • QT网络编程之获取本机网络信息
    一.概述查询一个主机的MAC地址或者IP地址是网络应用中常用到的功能,Qt提供了QHostInfo和QNetworkInterface类可以用于此类信息的查询1.QHostInfo类(显示和查找本地的信息)2.QNetworkInterface类(获得应用程序上所在主机的所有网络接口,包括子网掩码和广播地址) 推荐一个不错......
  • Python爬虫实战系列3:今日BBNews编程新闻采集
    一、分析页面打开今日BBNews网址https://news.bicido.com,下拉选择【编程】栏目1.1、分析请求F12打开开发者模式,然后点击Network后点击任意一个请求,Ctrl+F开启搜索,输入标题ApacheDoris2.1.0版本发布,开始搜索搜索结果显示直接返回的json格式,那就soeasy了,直接copycurl,......
  • KTL 一个支持C++14编辑公式的K线技术工具平台 - 第九版,数据分析工具。支持通达信日线
    K,K线,Candle蜡烛图。T,技术分析,工具平台L,公式Language语言使用c++14,Lite小巧简易。项目仓库:https://github.com/bbqz007/KTL国内仓库:https://gitee.com/bbqz007/KTL CoreAnimationforWindows: https://github.com/bbqz007/xwzqt5 一个超简单的Qt5窗口语法: https://gith......
  • 实验一c语言输入输出和简单程序应用编程
    1#include<stdio.h>2intmain()3{456printf("o\n");7printf("<H>\n");8printf("II\n");9printf("o\n");10printf("<H>\n");11......
  • C++并发编程:线程池学习
    文章目录一、线程池的概念二、线程池的设计三、线程池的实现1、ThreadPool声明2、线程创建3、添加任务4、ThreadPool析构四、相关知识点1、emplace_back和push_back2、typenamestd::result_of<F(Args...)>::type3、std::packaged_task<return_type()>4、函数模板和......