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

Linux C++ 网络编程学习系列(6)——多路IO之epoll高级用法的更多相关文章

  1. Linux C++ 网络编程学习系列(1)——端口复用实现

    Linux C++ 网络编程学习系列(1)--端口复用实现 源码地址:https://github.com/whuwzp/linuxc/tree/master/portreuse 源码说明: serv ...

  2. Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发

    多路IO之epoll边沿触发+非阻塞 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example 源码说 ...

  3. Linux C++ 网络编程学习系列(4)——多路IO之epoll基础

    epoll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll 源码说明: server.cpp: 监听127.1:6666,功 ...

  4. Linux C++ 网络编程学习系列(3)——多路IO之poll实现

    poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/poll 源码说明: server.cpp: 监听127.1:6666,功能是 ...

  5. Linux C++ 网络编程学习系列(2)——多路IO之select实现

    select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...

  6. Linux C++ 网络编程学习系列(7)——mbedtls编译使用

    mbedtls编译使用 环境: Ubuntu18.04 编译器:gcc或clang 编译选项: 静态编译使用 1. mbedtls源码 下载地址: https://github.com/ARMmbed ...

  7. Linux C网络编程学习笔记

    Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...

  8. linux下网络编程学习——入门实例ZZ

    http://www.cppblog.com/cuijixin/archive/2008/03/14/44480.html 是不是还对用c怎么实现网络编程感到神秘莫测阿,我们这里就要撕开它神秘的面纱, ...

  9. Linux下网络编程学习杂记

    1.TCP/IP协议的体系结构包含四层:应用层(负责应用程序的网络服务,通过端口号识别各个不同的进程)->传输层(传输控制层协议TCP.用户数据报协议UDP.互联网控制消息协议ICMP)-> ...

随机推荐

  1. 软件工程作业0——The Road Not Taken

    目录 第一部分:结缘计算机 缘起 四顾 思考 第二部分:在计算机系里学习 挑战 落差 第三部分:未来规划 向前 未来四个月的软工课 项目 内容 这个作业属于 2020春季计算机学院软件工程(罗杰 任健 ...

  2. JMeter脚本拷贝自动化

    方法一:DOC命令拷贝脚本(适合Windows系统) 1.写一段DOC命令(保存为批处理文件copyscript.bat),将本地JMeter脚本拷贝到远程机器上. net use \\<远程机 ...

  3. 图解I/O模型

      本文带你鸟瞰I/O模型全貌,希望可以让你对I/O模型有一个直观的认识 什么是I/O?I/O的过程?同步阻塞 I/O同步非阻塞 I/OI/O多路复用异步I/O 什么是I/O?   I/O就是计算机内 ...

  4. Deep Protein Methylation Profiling by Combined Chemical and Immunoaffinity Approaches Reveals Novel PRMT1 Targets (结合层析法和免疫沉淀法的蛋白甲基化的深度检测技术发现了PRMT1新的靶标蛋白)

    题目:Deep Protein Methylation Profiling by Combined Chemical and Immunoaffinity Approaches Reveals Nov ...

  5. 为什么 select count(*) from t,在 InnoDB 引擎中比 MyISAM 慢?

    统计一张表的总数量,是我们开发中常有的业务需求,通常情况下,我们都是使用 select count(*) from t SQL 语句来完成.随着业务数据的增加,你会发现这条语句执行的速度越来越慢,为什 ...

  6. Longest subarray of target sum

    2018-07-08 13:24:31 一.525. Contiguous Array 问题描述: 问题求解: 我们都知道对于subarray的问题,暴力求解的时间复杂度为O(n ^ 2),问题规模已 ...

  7. PyTorch 实战-张量

    Numpy 是一个非常好的框架,但是不能用 GPU 来进行数据运算. Numpy is a great framework, but it cannot utilize GPUs to acceler ...

  8. 从DeepNet到HRNet,这有一份深度学习“人体姿势估计”全指南

    从DeepNet到HRNet,这有一份深度学习"人体姿势估计"全指南 几十年来,人体姿态估计(Human Pose estimation)在计算机视觉界备受关注.它是理解图像和视频 ...

  9. kaggle入门——泰坦尼克之灾

    目录 引言 数据认识 总结 特征处理 建模预测 logistic分类模型 随机森林 SVM xgboost 模型验证 交叉验证 学习曲线 高偏差: 高方差 模型融合 总结 后记 引言 一直久闻kagg ...

  10. 【强烈推荐】适合Flutter初学者的完整项目

    简介 Flutter Fly是什么?Flutter Fly是一款开源的Flutter 项目,非常适合初学者进行学习.App内集成了160+Flutter基础控件的详细介绍及用法,内容来源于:http: ...