poll实现多路IO

  1. 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_libevent
  2. 源码说明:

1. 概要

1.1 libevent的优势

  1. 采用了回调函数,这里就是epoll_event.data.ptr的使用,使用ptr而不是fd, 指向自定义的结构体,结构体中含有回调函数和需要的参数
  2. 把read和write分割开,提高效率(因为对方如果接收端缓冲区满,我们是不能发送的,设定等待EPOLLOUT信号有利于提升效率),read完就删除节点,添加write节点;write完就删除,添加read

因为EPOLLIN_ET信号处理完之后,我们可以不会再read了,所以修改为EPOLLOUT,等待write

EPOLLOUT的用处

2. 核心代码

//myepoll.h
#ifndef MyEPOLL_H_
#define MyEPOLL_H_ #include <string.h>
#include <stdio.h>
#include <sys/epoll.h>
#define EPOLLIN_ET (EPOLLIN | EPOLLET)
//#define EPOLLIN_ET EPOLLIN
struct myevent_s{
int fd;
uint32_t events;
int status;
char buf[1024];
int len;
void * arg;
void (*callback)(void*);
}; void event_set(struct myevent_s* myevt, int fd, uint32_t events, void(*callback)(void*)); void event_add(int epfd, struct myevent_s * myevt);
void event_del(int epfd, struct myevent_s * myevt);
void event_mod(int epfd, struct myevent_s * myevt);
#endif

核心: myevent_s这个结构体中有回调函数,还有arg,这个将会指向myevents_s自己,这样callback就可以调用结构体中的数据了(相当于全都封装在一起了)

//myepoll.cpp
#include "myepoll.h" void event_set(struct myevent_s *myevt, int fd, uint32_t events,
void (*callback)(void *arg)) {
myevt->fd = fd;
myevt->events = events;
myevt->status = 1;
myevt->arg = myevt; //核心, 设置指向自己
memset(myevt->buf, 0, sizeof(myevt->buf));
myevt->len = 0;
myevt->callback = callback;
} void event_add(int epfd, struct myevent_s *myevt) {//封装add
struct epoll_event evt;
evt.events = myevt->events;
evt.data.ptr = myevt;
epoll_ctl(epfd, EPOLL_CTL_ADD, myevt->fd, &evt);
}
void event_del(int epfd, struct myevent_s *myevt) {//封装del
myevt->status = 0;
epoll_ctl(epfd, EPOLL_CTL_DEL, myevt->fd, nullptr);
}
void event_mod(int epfd, struct myevent_s *myevt) {//删除自己,并添加另一个, write和read
epoll_ctl(epfd, EPOLL_CTL_DEL, myevt->fd, nullptr); struct epoll_event evt;
if (myevt->events & EPOLLIN_ET) {
myevt->events = EPOLLOUT;
} else {
myevt->events = EPOLLIN_ET;
}
evt.events = myevt->events;
evt.data.ptr = myevt; epoll_ctl(epfd, EPOLL_CTL_ADD, myevt->fd, &evt);
} //server.cpp
#include "include/myepoll.h"
#include "include/wrap.h"
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <unistd.h>
#define MAX_CLIENT 1024
#define PORT 6666 static int g_epfd;
static struct epoll_event g_epoll_evts[MAX_CLIENT];
static struct myevent_s g_my_evts[MAX_CLIENT]; void callback_write(void *arg);
void callback_read(void *arg);
void handler(char *in, char *out) {
for (int i = 0; i < (int)strlen(out) + 1; ++i) {
out[i] = toupper(in[i]);
}
} void callback_write(void *arg) {
printf("callback_write...\n");
struct myevent_s *myevt = (struct myevent_s *)arg;
Write(myevt->fd, myevt->buf, (size_t)myevt->len);
printf("send: %s\n", myevt->buf);
memset(myevt->buf, 0, sizeof(myevt->buf)); event_mod(g_epfd, myevt); //删除write, 添加read
myevt->callback = callback_read; //修改回调函数为read
printf("change to read\n");
} void callback_read(void *arg) {
printf("callback_read...\n");
struct myevent_s *myevt = (struct myevent_s *)arg;
int ret = 0;
myevt->len = 0; while (true) {
if (myevt->len >= (int)sizeof(myevt->buf)) break; ret = (int)Read(myevt->fd, myevt->buf + myevt->len, 2);
// ret = (int)Read(myevt->fd, myevt->buf + ret,
// sizeof(myevt->buf) - (unsigned long)myevt->len);
printf("ret: %d\n", ret);
if (ret > 0) { //继续读
myevt->len += ret;
} else if (ret == 0) { //异常
Close(myevt->fd);
event_del(g_epfd, myevt);
return;
} else if ((ret == -1) && (myevt->len == 0)) {//说明一个都没读
return;
} else if ((ret == -1) && (myevt->len > 0)) {//非阻塞时返回-1为正常
break;
}
} printf("recv: %s\n", myevt->buf);
handler(myevt->buf, myevt->buf); //转大写处理 event_mod(g_epfd, myevt); //删除read,添加write
myevt->callback = callback_write;//改为write
printf("change to write\n");
} void callback_accept(void *arg) {
printf("callback_accept...\n");
struct myevent_s * myevt = (struct myevent_s *)arg;
struct sockaddr_in addr;
int cfd = -1;
int i = 0;
socklen_t len = sizeof(addr); cfd = Accept(myevt->fd, (struct sockaddr *)&addr, &len);
printf("=========new client: socket %d %s:%d=============\n", cfd,
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); fcntl(cfd, F_SETFL, O_NONBLOCK); //设置为非阻塞
for (i = 0; i < MAX_CLIENT; ++i) {
if (g_my_evts[i].status == 0) { break; }
}
event_set(&g_my_evts[i], cfd, EPOLLIN_ET, callback_read); //设置myevent_s
event_add(g_epfd, &g_my_evts[i]);//添加cfd的监听,最初为read
} int initsocket(int &lfd, int port) {
int opt = 0;
g_epfd = epoll_create(MAX_CLIENT);//create lfd = Socket(AF_INET, SOCK_STREAM, 0);
fcntl(lfd, F_SETFL, O_NONBLOCK);//非阻塞
opt = 1;
Setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, (socklen_t)sizeof(opt)); struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons((uint16_t)port);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
Bind(lfd, (struct sockaddr *)&addr, sizeof(addr)); event_set(&g_my_evts[MAX_CLIENT - 1], lfd, EPOLLIN_ET, callback_accept);//添加
event_add(g_epfd, &g_my_evts[MAX_CLIENT - 1]); Listen(lfd, 20);
return 0;
} int main() {
int lfd = 0;
int i = 0;
int nselect = 0;
struct myevent_s *myevt = nullptr; for (i = 0; i < MAX_CLIENT; ++i) {
g_my_evts[i].status = 0;
} initsocket(lfd, PORT);
while (true) {
printf("waiting...\n");
nselect = epoll_wait(g_epfd, g_epoll_evts, MAX_CLIENT + 1, 1000);
for (i = 0; i < nselect; ++i) {
myevt = (struct myevent_s *)(g_epoll_evts[i].data.ptr); if (myevt->fd == lfd) {
myevt->callback(myevt->arg);//回调
continue;
} if ((g_epoll_evts[i].events & EPOLLIN_ET) &&
(myevt->events & EPOLLIN_ET)) {
myevt->callback(myevt->arg);//回调
}
if ((g_epoll_evts[i].events & EPOLLOUT) &&
(myevt->events & EPOLLOUT)) {
myevt->callback(myevt->arg);//回调
}
}
sleep(1);
} }

3. 参考网址

  1. https://www.bilibili.com/video/av53016117?p=69

最新文章

  1. python 学习笔记6(数据库 sqlite)
  2. php100 的下拉分页效果
  3. PHP使用mail()函数发送邮件流程以及注意事项
  4. github and SourceTree初步使用
  5. Eclipse中调试技巧
  6. ASP.NET伪静态-无法读取配置文件,因为它超过了最大文件大小的解决办法
  7. git 分支操作
  8. struts2框架概述
  9. PS 图像调整算法——饱和度调整
  10. 20175208《Java程序设计》第五周学习总结
  11. postgresql 9源码安装
  12. 利用springloaded进行java jar&amp;class的动态替换
  13. 继承标签extend
  14. C#DateTime.ToString 格式化时间字符串和数值类型转换为字符串
  15. mysql基本语句集合
  16. jdbc连接遭遇RAC设备
  17. ajax回调函数中使用$(this)取不到对象的解决方法
  18. JS复制制定内容到剪贴板怎么做?
  19. Windows server 2008 Tips
  20. Linux入门——shell脚本常用信息小结

热门文章

  1. C语言-转义字符
  2. Python入门到放弃
  3. angular vue通过node启动项目局域网内关闭防火墙无法访问的解决办法
  4. adt-bundle环境搭建(Win7+Win10)
  5. 【短道速滑一】OpenCV中cvResize函数使用双线性插值缩小图像到长宽大小一半时速度飞快(比最近邻还快)之异象解析和自我实现。
  6. 使用SlimYOLOv3框架实现实时目标检测
  7. GitHub也会断供:美国制裁地区帐号都受限,毫无预警,个人页面直接404
  8. Input标签中属性的注意点
  9. iOS 编译过程原理(1)
  10. [bzoj4977]跳伞求生&lt;贪心&gt;