c++ 网络编程LINUX-epoll/windows-IOCP下socket opoll函数用法 优于select方法的epoll 以及windows下IOCP 解决多进程服务端创建进程资源浪费问题,感兴趣的小伙伴一起来学习吧
一、IOCP和Epoll之间的异同
1、异
1).IOCP是WINDOWS系统下使用。Epoll是Linux系统下使用。
2).IOCP是IO操作完毕之后,通过Get函数获得一个完成的事件通知。
Epoll是当你希望进行一个IO操作时,向Epoll查询是否可读或可写,若处于可读或可写状态,Epoll会通过epoll_wait进行通知。
3).IOCP封装了异步的消息事件的通知机制,同时封装了部分IO操作。但Epoll仅仅封装了一个异步事件的通知机制,并不负责IO读写操作。Epoll保持了事件通知和IO操作间的独立性,更加简单灵活。
4).基于上面的描述,我们可以知道Epoll不负责IO操作,所以它只告诉你当前可读可写了,并且将协议读写缓冲填充,由用户去读写控制,此时我们可以做出额外的许多操作。IOCP则直接将IO通道里的读写操作都做完了才通知用户,当IO通道里发生了堵塞等状况我们是无法控制的。
2、同
1).它们都是异步的事件驱动的网络模型。
2).它们都可以向底层进行指针数据传递,当返回事件时,除可通知事件类型外,还可以通知事件相关数据。
二:Epoll理解与应用。
1、epoll是什么?
epoll是当前在Linux下开发大规模并发网络程序的热门人选,epoll 在Linux2.6内核中正式引入,和select相似,都是I/O多路复用(IO multiplexing)技术。
Linux下设计并发网络程序,常用的模型有:
Apache模型(Process Per Connection,简称PPC)
TPC(Thread PerConnection)模型
select模型和poll模型。
epoll模型
2、epoll与select对比优化
基于select的I/O复用技术速度慢的原因:
1),调用select函数后常见的针对所有文件描述符的循环语句。它每次事件发生需要遍历所有文件描述符,找出发生变化的文件描述符。(以前写的示例没加循环)
2),每次调用select函数时都需要向该函数传递监视对象信息。即每次调用select函数时向操作系统传递监视对象信息,至于为什么要传?是因为我们监视的套接字变化的函数,而套接字是操作系统管理的。(这个才是最耗效率的)
注释:基于这样的原因并不是说select就没用了,在这样的情况下就适合选用select:1,服务端接入者少 2,程序应具有兼容性。
3、epoll是怎么优化select问题的
1),每次发生事件它不需要循环遍历所有文件描述符,它把发生变化的文件描述符单独集中到了一起。
2),仅向操作系统传递1次监视对象信息,监视范围或内容发生变化时只通知发生变化的事项。
实现epoll时必要的函数和结构体:
函数:epoll_create:创建保存epoll文件描述符的空间,该函数也会返回文件描述符,所以终止时,也要调用close函数。(创建内存空间)
epoll_ctl:向空间注册,添加或修改文件描述符。(注册监听事件)
epoll_wait:与select函数类似,等待文件描述符发生变化。(监听事件回调)
结构体:
struct epoll_event { __uint32_t events; epoll_data_t data; } typedef union epoll_data { void *ptr; int fd; __uinit32_t u32; __uint64_t u64; } epoll_data_t;
三、epoll的几个函数的介绍:
1、epoll_create函数
/** * @brief 该函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。 * * @param size size就是你在这个epoll fd上能关注的最大socket fd数 * * @return 生成的文件描述符 */ int epoll_create(int size);
2、epoll_ctl函数
/** * @brief 该函数用于控制某个epoll文件描述符上的事件,可以注册事件,修改事件,删除事件。 * * @param epfd 由 epoll_create 生成的epoll专用的文件描述符 * @param op 要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD 注册、EPOLL_CTL_MOD 修 改、EPOLL_CTL_DEL 删除 * @param fd 关联的文件描述符 * @param event 指向epoll_event的指针 * * @return 0 succ * -1 fail */ int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
其中用到的数据结构结构如下
op值:
- EPOLL_CTL_ADD:注册新的fd到epfd中;
- EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
- EPOLL_CTL_DEL:从epfd中删除一个fd;
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 */ };
常用的事件类型:
- EPOLLIN :表示对应的文件描述符可以读;
- EPOLLOUT:表示对应的文件描述符可以写;
- EPOLLPRI:表示对应的文件描述符有紧急的数据可读
- EPOLLERR:表示对应的文件描述符发生错误;
- EPOLLHUP:表示对应的文件描述符被挂断;
- EPOLLET: 表示对应的文件描述符有事件发生;
例:
struct epoll_event ev; //设置与要处理的事件相关的文件描述符 ev.data.fd=listenfd; //设置要处理的事件类型 ev.events=EPOLLIN|EPOLLET; //注册epoll事件 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);