官网:libevent
# linux中下载
apt-get install libevent-dev
官网的简介:
The libevent API provides a mechanism to execute a callback function when a specific event occurs on a file descriptor or after a timeout has been reached. Furthermore, libevent also support callbacks due to signals or regular timeouts.
libevent API提供了一种机制,可以在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时而产生的回调。
测试环境:
# libevent 2.1.8-stable # gcc gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04) # linux > lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.6 LTS Release: 18.04 Codename: bionic
编译指令
# 指定lib gcc main.c -levent ./a.out
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**
* @brief
* bufferevent 的读事件回调
* @param bev
* @param ctx
*/
void read_cb(struct bufferevent* bev, void* ctx) {
const int fd = (int)ctx;
printf("[%s] [%d]\n", __func__, fd);
char buf[128] = {};
size_t ret = bufferevent_read(bev, buf, sizeof(buf));
if (ret < 0) {
printf("bufferevent_read error\n");
} else {
printf("read from %d %s\n", fd, buf);
}
}
/**
* @brief
* bufferevent 其他事件回调(非读写)
* @param bev
* @param what
* @param ctx
*/
void event_cb(struct bufferevent* bev, short what, void* ctx) {
const int fd = (int)ctx;
printf("[%s] [%d] error happen [%#x]\n", __func__, fd, what);
if (what & BEV_EVENT_EOF) {
printf("client [%d] down\n", fd);
// 释放buffer对象
bufferevent_free(bev);
} else {
printf("event_cb unknow error\n");
}
}
/**
* @brief
*
* @param listener
* @param fd tcp连接的fd
* @param addr
* @param socklen
* @param arg
*/
void listen_cb(struct evconnlistener* listener, evutil_socket_t fd,
struct sockaddr* addr, int socklen, void* arg) {
printf("accept a client [%d]\n", fd);
struct event_base* base = (struct event_base*)arg;
// 针对已经存在的socket创建bufferevent对象
// - 事件集合
// - fd
// - 参数
struct bufferevent* bev =
bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
if (NULL == bev) {
printf("bufferevent_socket_new error");
exit(1);
}
// 给bufferevent设置回调函数
// - bufferevent对象
// - 读事件回调
// - 写事件回调
// - 其他事件回调
// - 参数
bufferevent_setcb(bev, read_cb, NULL, event_cb, (void*)fd);
// 使能 bufferevent 对象
bufferevent_enable(bev, EV_READ);
}
int main(int argc, char** argv) {
// 创建一个事件集合
struct event_base* base = event_base_new();
if (NULL == base) {
perror("event_base_new error");
exit(1);
}
// !!!注意测试的时候,端口不要被占用!!!
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8888);
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
// 创建socket bind listen accept
// 枚举 释放监听时关闭socket 端口重复使用
// 接受函数也是在这里个回调里面
struct evconnlistener* listener = evconnlistener_new_bind(
base, listen_cb, /*void *ptr*/ base,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 10,
(struct sockaddr*)&server_addr, sizeof(server_addr));
if (NULL == listener) {
perror("evconnlistener_new_bind error");
exit(-1);
}
printf("start to event loop\n");
// 监听事件循环
event_base_dispatch(base);
// 释放两个对象
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
梳理一下流程
定义一个struct event_base*
对象
借助evconnlistener_new_bind()
完成创建socket bind listen accept一系列步骤。同时注册到base中。
struct bufferevent*
。bufferevent_setcb()
,并使能bufferevent_enable()
。
bufferevent_data_cb readcb
读事件bufferevent_data_cb writecb
写事件bufferevent_event_cb eventcb
其他事件开启事件监听event_base_dispatch(base)
,此时会阻塞程序,直到全部事件结束,或者不监听。
// 对于 bufferevent_event_cb 可能出现的一些事件
/** @name Bufferevent event codes
These flags are passed as arguments to a bufferevent's event callback.
*/
#define BEV_EVENT_READING 0x01 /**< error encountered while reading */
#define BEV_EVENT_WRITING 0x02 /**< error encountered while writing */
#define BEV_EVENT_EOF 0x10 /**< eof file reached */
#define BEV_EVENT_ERROR 0x20 /**< unrecoverable error encountered */
#define BEV_EVENT_TIMEOUT 0x40 /**< user-specified timeout reached */
#define BEV_EVENT_CONNECTED 0x80 /**< connect operation finished. */
监听三次ctrl+c
第四次退出。
// 常用内置 signals
/* ISO C99 signals. */
#define SIGINT 2 /* Interactive attention signal. */
#define SIGILL 4 /* Illegal instruction. */
#define SIGABRT 6 /* Abnormal termination. */
#define SIGFPE 8 /* Erroneous arithmetic operation. */
#define SIGSEGV 11 /* Invalid access to storage. */
#define SIGTERM 15 /* Termination request. */
#include
#include
#include
int signal_count = 0;
/**
* @brief
* C语言不允许函数定义的时候参数匿名
* @param fd
* @param events
* @param arg
*/
void callback_signal_handle(evutil_socket_t fd, short events, void *arg) {
printf("receive signal fd[%d]\n", fd);
struct event *ev = (struct event *)arg;
signal_count += 1;
if (signal_count >= 3) {
// 把事件从集合中删除
// 从集合中删除这个事件
event_del(ev);
}
}
int main() {
// 不用全局的事件集合
// 从堆上创建事件集合
struct event_base *base = event_base_new();
// 创建事件
struct event ev;
// 把事件和信号绑定
//
// 1. 事件对象的地址
// 2. 指定的信号集合
// 3. fd
// 4. 监听的事件类型 EV_PERSIST 多次循环监听,否则默认监听一次
// 5. 回调函数
// 6. 回调函数参数
event_assign(&ev, base, SIGINT, EV_SIGNAL | EV_PERSIST,
callback_signal_handle, &ev);
// 添加到集合中
event_add(&ev, NULL);
// 监听集合,直到集合中没东西
int dispatch = event_base_dispatch(base);
printf("dispatch = %d\n", dispatch);
// 释放集合
event_base_free(base);
return 0;
}
read
在libevent中存在一个全局的struct event_base *
。可以直接使用。
#include
#include
#include
#include
#include
#include
/**
* @brief
*
* @param fd
* @param events
* @param arg
* 当事件满足条件的时候会触发该回调函数
*/
void callback_fifo_read(evutil_socket_t fd, short events, void* arg) {
char buf[1024] = {};
int ret = read(fd, buf, sizeof(buf));
if (-1 == ret) {
perror("callback read error");
exit(1);
}
printf("callback from fifo = [%s]\n", buf);
}
/**
* @brief Create a fifo object
*
* @param path
* @return int
* 创建有名管道
* 注意,每次启动前注意删除一下
*/
int create_fifo(const char* const path) {
int ret = mkfifo(path, 0666);
if (-1 == ret) {
perror("mkfifo = -1");
exit(-1);
}
int fd = open(path, O_RDONLY);
if (-1 == fd) {
perror("open = -1");
exit(-1);
}
return fd;
}
int main(void) {
int fd = create_fifo("fifo.tmp");
// 初始化事件集合
// 不用参数,内部有个全局的变量
event_init();
// 创建一个事件
struct event ev;
// 初始化事件(把fd和事件ev绑定)
// 默认加入到全局变量的事件集合中
//
// 1. 事件对象的地址
// 2. fd
// 3. 监听的事件类型 EV_PERSIST 多次循环监听,否则默认监听一次
// 4. 回调函数
// 5. 回调函数的arg参数
event_set(&ev, fd, EV_READ | EV_PERSIST, callback_fifo_read, NULL);
// 事件地址,超时时间
event_add(&ev, NULL);
// 开始监听循环,阻塞程序
// 如果集合中没有事件,则返回
int dispatch = event_dispatch();
printf("dispatch = %d\n", dispatch);
return 0;
}
write
此处的写操作就不要创建了。
#include
#include
#include
#include
#include
#include
int main(void) {
int fd = open("fifo.tmp", O_WRONLY);
if (-1 == fd) {
perror("open");
exit(-1);
}
char buf[1024] = {};
while (1) {
printf("> Please Input : ");
scanf("%s", buf);
int ret = write(fd, buf, sizeof(buf));
if (-1 == ret) {
perror("write error");
exit(-1);
}
// 终止,主动退出
if (!strcmp(buf, "bye")) {
break;
}
memset(buf, 0, sizeof(buf));
}
close(fd);
return 0;
}
/**
* This is a simple socket client code in Linux;
* Use TCP mode; By gcc;
*/
#include
#include
#include
#include
#include
#include
#define Socket_Port (8888)
#define Socket_Ip ("127.0.0.1")
#define Open_Receive (1) // receive data from server
#define Open_Loop (1) // is loop to read & write
void start_client(void) {
printf("> [Line%03d]{%s} start\n", __LINE__, __PRETTY_FUNCTION__);
int ret = 0;
// ipv4 stream -> tcp
const int fd = socket(PF_INET, SOCK_STREAM, 0);
if (-1 == fd) {
perror("socket fd = -1\n");
return;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(Socket_Ip);
servaddr.sin_port = htons(Socket_Port);
ret = connect(fd, (struct sockaddr*)&servaddr, sizeof(servaddr));
if (-1 == ret) {
perror("connect ret = -1\n");
close(fd);
return;
}
char buffer[1024] = "";
do {
printf("$Please Input and q or Q out>");
scanf("%s", buffer);
if (strcmp(buffer, "Q") == 0 || strcmp(buffer, "q") == 0) {
break;
}
ret = write(fd, buffer, sizeof(buffer));
if (-1 == ret) {
perror("write error\n");
break;
}
#if Open_Receive
ret = read(fd, buffer, sizeof(buffer));
if (-1 == ret) {
perror("read error\n");
break;
} else {
printf("client read => [%s]\n", buffer);
}
#endif
} while (Open_Loop);
close(fd);
printf("> [Line%03d]{%s} end\n", __LINE__, __PRETTY_FUNCTION__);
}
int main(const int argc, const char** argv) {
printf("> [Line%03d]{%s} start\n", __LINE__, __PRETTY_FUNCTION__);
printf("> argc = {%d}\n", argc);
for (int i = 0; i < argc; i += 1) {
printf("> argv[%d] = {%s}\n", i, argv[i]);
}
start_client();
printf("> [Line%03d]{%s} end\n", __LINE__, __PRETTY_FUNCTION__);
}
参考:https://www.bilibili.com/video/BV1xp4y1q75p/