Linux C++ 网络编程学习系列(6)——多路IO之epoll高级用法
poll实现多路IO
- 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_libevent
- 源码说明:
- server.cpp: 监听127.1:6666,功能是将收到的数据打印到屏幕
- include/wrap.cpp: 封装的一些socket基本操作,加了基本的错误处理
- include/myepoll.cpp: 封装了些epoll的处理
- client.cpp: 客户端, 地址在: https://github.com/whuwzp/linuxc/tree/master/simple_cs/client/
1. 概要
1.1 libevent的优势
- 采用了回调函数,这里就是epoll_event.data.ptr的使用,使用ptr而不是fd, 指向自定义的结构体,结构体中含有回调函数和需要的参数
- 把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. 参考网址
Linux C++ 网络编程学习系列(6)——多路IO之epoll高级用法的更多相关文章
- Linux C++ 网络编程学习系列(1)——端口复用实现
Linux C++ 网络编程学习系列(1)--端口复用实现 源码地址:https://github.com/whuwzp/linuxc/tree/master/portreuse 源码说明: serv ...
- Linux C++ 网络编程学习系列(5)——多路IO之epoll边沿触发
多路IO之epoll边沿触发+非阻塞 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_ET_LT_NOBLOCK_example 源码说 ...
- Linux C++ 网络编程学习系列(4)——多路IO之epoll基础
epoll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll 源码说明: server.cpp: 监听127.1:6666,功 ...
- Linux C++ 网络编程学习系列(3)——多路IO之poll实现
poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/poll 源码说明: server.cpp: 监听127.1:6666,功能是 ...
- Linux C++ 网络编程学习系列(2)——多路IO之select实现
select实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/select 源码说明: server.cpp: 监听127.1:6666 ...
- Linux C++ 网络编程学习系列(7)——mbedtls编译使用
mbedtls编译使用 环境: Ubuntu18.04 编译器:gcc或clang 编译选项: 静态编译使用 1. mbedtls源码 下载地址: https://github.com/ARMmbed ...
- Linux C网络编程学习笔记
Linux C网络编程总结报告 一.Linux C 网络编程知识介绍: 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户端:(client) 在网络程序中, ...
- linux下网络编程学习——入门实例ZZ
http://www.cppblog.com/cuijixin/archive/2008/03/14/44480.html 是不是还对用c怎么实现网络编程感到神秘莫测阿,我们这里就要撕开它神秘的面纱, ...
- Linux下网络编程学习杂记
1.TCP/IP协议的体系结构包含四层:应用层(负责应用程序的网络服务,通过端口号识别各个不同的进程)->传输层(传输控制层协议TCP.用户数据报协议UDP.互联网控制消息协议ICMP)-> ...
随机推荐
- Python程序设计试验报告一: 熟悉IDLE和在线编程平台
安徽工程大学 Python程序设计 实验报告 班级 物流192 ...
- iOS开发如何面对疫情过后的面试高峰期 !
2020年本应该是一个 "爱你.爱你"的年份!却因为 黑天鹅 给我们带来非常大的影响! 一.2020年iOS招聘数据分析 这里是 2020年3月份BOSS直聘 北京iOS招聘前几页 ...
- Spring01——你应该了解的,有关 IOC 容器的一切
从本文开始,将开始介绍关于 Spring 的一些常见知识点.关注我的公众号「Java面典」,每天 10:24 和你一起了解更多 Java 相关知识点. 在如今的 Java Web 开发中,Spring ...
- Spring优雅整合Redis缓存
“小明,多系统的session共享,怎么处理?”“Redis缓存啊!” “小明,我想实现一个简单的消息队列?”“Redis缓存啊!” “小明,分布式锁这玩意有什么方案?”“Redis缓存啊!” “小明 ...
- 贪心-Bag of Tokens
2020-01-20 22:32:28 问题描述: 问题求解: 双指针 + 贪心. public int bagOfTokensScore(int[] tokens, int P) { Arrays. ...
- 滑动窗口-Substring Search Problem
2018-07-18 11:19:19 一.Minimum Window Substring 问题描述: 问题求解: public String minWindow(String s, String ...
- Oracle数据库开机自启动的配置
如果服务器断电重启或计划内重启,在服务器的操作系统启动后,需要手工启动数据库实例和监听,本文介绍如何把Oracle数据库的启动和关闭配置成系统服务,在操作系统启动/关闭时,自动启动/关闭Oracle实 ...
- 逻辑回归和sigmoid函数分类
逻辑回归和sigmoid函数分类:容易欠拟合,分类精度不高,计算代价小,易于理解和实现 sigmoid函数与阶跃函数的区别在于:阶跃函数从0到1的跳跃在sigmoid函数中是一个逐渐的变化,而不是突变 ...
- arcgis10.4.X的oracle数据库要求
受支持的数据库版本:(标准版/标准独立版/企业版) Oracle 11g R2(64 位)11.2.0.4 Oracle 12c R1(64 位)12.1.0.2 受支持的操作系统: 数据库 支持的操 ...
- JavaScript中去除字符串空格的方法
语法 string.trim() 参数值 无 返回值 类型:string 描述:返回移除头尾空格的字符串 技术细节 JavaScript版本: ECMAScript 5 去除字符串左右两端的空格,在v ...