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. File的功能--> 获取功能-->所有的根目录 | 创建文件功能,但是如果文件已经存在-->不再创建(新手)

    //导入的包.import java.io.File;import java.io.FileFilter;import java.io.IOException; // 获取功能-->所有的根目录 ...

  2. Java中static和final的解析

    static关键字和final关键字是Java中一个难点&重点.本文通过static的用途.常见问题.final的用途.final常见问题,以及static和final的对比来解释这两个关键字 ...

  3. python基础知识6——函数

    函数:自定义函数:函数的参数:不带参数,普通参数,默认参数,动态参数:返回值return:函数作用域:内置函数高阶函数:map,reduce,filter,sorted:lambda表达式:文件操作: ...

  4. 使用Eclipse开发python

    第一步:下载python插件http://sourceforge.net/projects/pydev/files/pydev/PyDev%204.1.0/第二步:在Eclipse上安装插件a.假设E ...

  5. 洛谷 P1438 无聊的数列 题解

    原题链接 首先,我们考虑用差分解决问题. 用 \(x_i\) 表示原数列,\(a_i = x_i - x_{i-1}\) 那么,先普及一下差分: 如果我们只需要维护区间加值,单点求值的话,你会发现两个 ...

  6. java 实现全排列

    public List<List<Integer>> permute(int[] nums) { List<List<Integer>> res = n ...

  7. 03.第一个Go程序

    第一个Go程序 Hello World 现在我们来创建第一个Go项目--hello.在我们的GOPATH下的src目录中创建hello目录. 在该目录中创建一个main.go文件: package m ...

  8. 【翻译】.NET 5 Preview2发布

    在4月2日,发布了.NET 5.0 Preview2,这次发布对一些功能和性能做了相关的改进,同时后面也会实施5.0版本更多的功能,其中一些功能目前也dotnet/designs在.NET 5 Pre ...

  9. vue+cordova实现退出app效果

    //vue钩子函数created方法中添加监听等待设备API库加载好 created(){ var that = this; document.addEventListener("devic ...

  10. 关于 IDEA 启动 springboot 项目异常 - Disconnected from the target VM, address: '127.0.0.1:59770', transport: 'socket'

    关于 IDEA 启动 springboot 项目异常 - Disconnected from the target VM, address: '127.0.0.1:59770', transport: ...