Linux C++ 网络编程学习系列(4)——多路IO之epoll基础
epoll实现多路IO
- 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll
- 源码说明:
- server.cpp: 监听127.1:6666,功能是将收到的小写转大写
- include/wrap.cpp: 封装的一些socket基本操作,加了基本的错误处理
- client.cpp: 客户端, 地址在: https://github.com/whuwzp/linuxc/tree/master/simple_cs/client/
1. 概要
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
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 */ //监听的事件,如EPOLLIN
epoll_data_t data; /* User data variable */ // 其他数据部分,这个是我们可以设定的,因为epoll_wait返回时会把这个结构体返回到events中,我们就可获取有信号的属性了
};
1.1 epoll_create
epfd = epoll_create(FD_SETSIZE); //返回文件描述符epfd,是红黑树的根节点
1.2 epoll_ctl
//增加节点
evt.data.fd = fd_client;//这里的联合体选择了int fd
evt.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd_client, &evt);
//删除节点
epoll_ctl(epfd, EPOLL_CTL_DEL, evts[i].data.fd, nullptr);
增加节点: 是把fd_client文件描述符添加到epfd的红黑树中,同时附带自己的属性,即epoll_event类型的结构体(这个结构体在epoll_wait后会复制到events数组中,这样返回时就可以获取有信号的fd的属性了)
删除节点: 从树中移除
1.3 epoll_wait
nselect = epoll_wait(epfd, evts, FD_SETSIZE, -1);
epfd: 输入,待监听的红黑树
evts: 输出,数组中的元素类型是epoll_event,当fd==n的文件描述符有信号时,会把fd的属性(详见epoll_ctl, 也是epoll_event类型)拷贝到evts中,这样后续我们就可以访问evts[i].data.xxx和evts[i].events来获取我们之前设定的数据
1.4 原理
- 树中每个节点是fd,每个fd映射着一个epoll_event类型的结构体,像属性一样(epoll_ctl添加节点时自定义的)
- epoll_wait监听各个节点,有信号的话,就把它的属性(epoll_event)拷贝到evts中,返回后,就可以通过
evts[i].data.xxx得到映射的数据 - 如果联合体选择fd, 即evts[i].data.fd,我们就相当于直接获取了fd,可以直接read,write.
- 后面的epoll_libevent我们会用ptr指向自定义的结构体
1.5 优势劣势
一定先epoll实现,epoll是poll的改进版,有以下不同:
- 结果返回(判断是否有信号)
- poll:需要轮询数组中的每个元素,看看是不是有信号,用
fds[0].revents & POLLIN - epoll:一个数组evts专门存放有信号的节点的属性
- poll:需要轮询数组中的每个元素,看看是不是有信号,用
2. 核心代码
#include "include/wrap.h"
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdnoreturn.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <unistd.h>
#include <wait.h>
#define LOCALIP "127.0.0.1"
#define PORT 6666
void handler(char *in, char *out) {
for (int i = 0; i < (int)strlen(out) + 1; ++i) {
out[i] = toupper(in[i]);
}
}
int workthread(const int &fd_client) {
char recvbuf[2048] = {0};
char sendbuf[2048] = {0};
int ret = 0;
ret = (int)Read(fd_client, recvbuf, 2048);
if (ret <= 0) {
printf("ret==0\n");
return ret;
}
handler(recvbuf, sendbuf);
ret = (int)Write(fd_client, sendbuf, strlen(sendbuf) + 1);
return ret;
}
void startsock(int &fd, struct sockaddr_in &addr, const char *ip,
const int port) {
fd = Socket(AF_INET, SOCK_STREAM, 0);
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ip);
addr.sin_port = htons(port);
}
int main() {
int fd_server = 0;
int fd_client = 0;
int ret = 0;
struct sockaddr_in sock_client;
struct sockaddr_in sock_server;
socklen_t client_len = (socklen_t)sizeof(sock_client);
int opt = 0;
int epfd = 0;
int nselect = 0;
int i = 0;
struct epoll_event evts[FD_SETSIZE];
struct epoll_event evt;
startsock(fd_server, sock_server, LOCALIP, PORT);
opt = 1;
Setsockopt(fd_server, SOL_SOCKET, SO_REUSEADDR, &opt,
(socklen_t)sizeof(opt));
Bind(fd_server, (struct sockaddr *)&sock_server, sizeof(sock_server));
Listen(fd_server, 5);
epfd = epoll_create(FD_SETSIZE);
if (epfd == -1) { perror_exit("epoll create failed"); }
evt.events = EPOLLIN;
evt.data.fd = fd_server;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd_server, &evt);
while (true) {
printf("epolling...\n");
nselect = epoll_wait(epfd, evts, FD_SETSIZE, -1);
printf("get %d select\n", nselect);
for (i = 0; i < nselect; ++i) {
if (!(evts[i].events && EPOLLIN)) continue;
if (evts[i].data.fd == fd_server) {//直接拿到fd_server
fd_client = Accept(fd_server, (struct sockaddr *)&sock_client,
&client_len);
printf("accept: %s: %d\n", inet_ntoa(sock_client.sin_addr),
ntohs(sock_client.sin_port));
evt.data.fd = fd_client;
evt.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, fd_client, &evt);
} else {
ret = workthread(evts[i].data.fd);//通过data.fd获取fd
if (ret <= 0) {
Close(evts[i].data.fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, evts[i].data.fd, nullptr);
}
}
}
}
Close(fd_server);
}
3. 参考网址
- https://www.bilibili.com/video/av53016117
- EPOLLOUT的用处:https://www.zhihu.com/question/22840801 https://github.com/yedf/handy
write: 不停地写数据,因为EPOLLOUT是只要缓冲区未满,就会有信号
Linux C++ 网络编程学习系列(4)——多路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++ 网络编程学习系列(6)——多路IO之epoll高级用法
poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/epoll_libevent 源码说明: server.cpp: 监听127. ...
- 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)-> ...
随机推荐
- Natas24 Writeup(strcmp绕过漏洞)
Natas24: 一个登录页面,查看源码,发现关键代码: if(array_key_exists("passwd",$_REQUEST)){ if(!strcmp($_REQUES ...
- Fiddler2 下断点修改HTTP报文
一 Fiddler中设置断点修改HTTP请求 方法1:全局断点.Rules-->Automatic BreakPoint-->Before Requests(或快捷键F11),这种方法会拦 ...
- 【简说Python WEB】数据库
目录 [简说Python WEB]数据库 数据库表 docker安装MySQL Flask-SQLAlchemy操纵MySQL数据库 初始化 定义模型 定义关系 数据库的CRUD操作 创建表 inse ...
- Jmeter之JSON提取器应用
在接口测试中有一个这样的场景:登录之后,需要进行昵称修改,怎么实现? 首先我们分别看下登录.昵称修改的接口说明: 以上业务中补充一点,昵称修改,还需要添加请求头Authorization传登录获取的t ...
- 设计模式—建造者模式(Builder)
title: 设计模式-建造者模式 建造者模式(Builder)是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节.建造者模式属于对 ...
- 插了带蠕虫的U盘后,文件不见了怎么快速恢复
朋友插了别人U盘,电脑中毒,找到我,在网上找了一些教程,好多都是用杀软的,懒的下,亲测本教程简单有效 首先,我们创建了一个reg文件(注册表文件),如下, 利用REG文件我们可以直接对注册表进行任何修 ...
- 强化学习之三点五:上下文赌博机(Contextual Bandits)
本文是对Arthur Juliani在Medium平台发布的强化学习系列教程的个人中文翻译,该翻译是基于个人分享知识的目的进行的,欢迎交流!(This article is my personal t ...
- CNN更新换代!性能提升算力减半,还即插即用
传统的卷积运算,要成为过去时了. Facebook和新加坡国立大学联手提出了新一代替代品:OctConv(Octave Convolution),效果惊艳,用起来还非常方便. OctConv就如同卷积 ...
- 多伦多大学&NVIDIA最新成果:图像标注速度提升10倍!
图像标注速度提升10倍! 这是多伦多大学与英伟达联合公布的一项最新研究:Curve-GCN的应用结果. Curve-GCN是一种高效交互式图像标注方法,其性能优于Polygon-RNN++.在自动模式 ...
- 对于一个由0..n的所有数按升序组成的序列,我们要进行一些筛选,每次我们取当前所有数字中从小到大的第奇数位个的数,并将其丢弃。重复这一过程直到最后剩下一个数。请求出最后剩下的数字。
输入描述: 每组数据一行一个数字,为题目中的n(n小于等于1000). 输出描述: 一行输出最后剩下的数字.我的思路是用两个链表,一个用于存储原数据,一个用于存储要丢掉的数据,再循环从元数据中剔除掉即 ...