libevent学习和整理(一)

这篇博客介绍了作者在项目中使用libevent进行网络通信的背景,以及如何使用libevent进行socket封装和多线程通信。作者从最简单的定时器事件开始学习libevent,并详细解释了event_assign函数的用法,以及如何设置定时回调函数和处理不同类型的事件。文章还讨论了event_base_dispatch和event_base_loop的区别,并提供了结束循环和释放资源的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


       由于最近遇到一个关于局域网通信的项目,主要是4个client端传送数据至1个server端,每个client端的PC均由网线连接一个网络转串口服务器,每个服务器分配1个ip,每个ip最多虚拟8个串口端,这些串口端获取的数据将作为数据源被处理,最终通过网络传输至server端。于是乎,我对socket进行封装处理,所有关于socket的初始化,组包和收发包均通过这里进行,thread也是单独封装了,在server端一个进程共由5个线程组成,主线程作ui相关处理,其他4个线程分别对不同的client端进行通信处理。虽然,整个小小的架构做了两三次优化调整,但是总觉得哪里有所欠缺。于是,好奇心害死人,libevent登场了。
       我对libevent也只是刚刚接触,很多东西正在慢慢研究整理中,有些自我认知可能是有问题的,如有发现,还望指正。这几天从libevent里sample中最简单的定时器事件开始接触。

      

#include <sys/types.h>

#include <event2/event-config.h>

#include <sys/stat.h>
#ifndef _WIN32
#include <sys/queue.h>
#include <unistd.h>
#endif
#include <time.h>
#ifdef EVENT__HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/util.h>

#ifdef _WIN32
#include <winsock2.h>
#endif

struct timeval tm; 

int main()
{
    struct event_base *base = NULL;
    struct event timeout;  

#ifdef _WIN32
    WORD wVersion = MAKEWORD(2, 2);
    WSADATA wsaData;  
    WSAStartup(wVersion, &wsaData);
#endif

    

    return 0;
}

       libevent关于网络,总归要WSAStartup进行初始化一下。每个libevent下的base似乎如同一个事件集或者事件根,新建出来的事件都得添加、绑定至base内,这里我先从定时器事件开始测试的。接下来:

// libevent version
const char *version = event_get_version();

// creat base
base = event_base_new();
if(!base)
{
    printf("event_base_new occurs an error...\n");
    return -1;
}

short flags = EV_TIMEOUT | EV_PERSISIT;
int result = event_assign(&timeout, base, -1, flags, timeout_cb, (void*)timeout);
if(result < 0)
{
    // error msg and how to deal
    return -1;
}

       从event_assign(struct event* ,struct event_base*, evutil_socket_t, short, event_callback_fn, void *)函数声明,基本可以看出在base下分配event(当前是time event),通过event_callback_fn回调函数处理所要做的事情,回调函数是要进行传参的。这里的flags确定事件的类型,基本由如下几种组成:

#define EV_TIMEOUT    0x01
#define EV_READ       0x02
#define EV_WRITE      0x04
#define EV_SIGNAL     0x08
#define EV_PERSIST    0x10
#define EV_ET         0x20
#define EV_FINALIZE   0x40

       再看这里的回调函数

void timeout_cb(evutil_socket_t fd, short ev_type, void *arg)
{
    struct timeval new_tm, diff_tm;
    
    evutil_gettimeofday(&new_tm, NULL);

    // get delt time(second)
    evutil_timersub(&new_tm, &tm, &diff_tm);
    double t = diff_tm.tv_sec + diff_tm.tv_usec/1.0e6;
    printf("timeout_cb occurs : hello, libevent...\n", t);

    tm = new_tm;
}

       这样,时间回调函数写好了,经过测试似乎event_assign(struct event* ,struct event_base*, evutil_socket_t, short, event_callback_fn, void *)函数中的evutil_socket_t和void*都传递给回调函数中对应的evutil_socket_t和void*,既然是定时事件,

那么ev_type自然是EV_TIMEOUT类型。定时回调函数既然完成,为定时器添加定时时间,接下来:

tv.tv_sec = 5;
tv.tv_usec = 0;
event_add(&timeout, &tv);

evutil_gettimeofday(&tm, NULL);

event_base_dispatch(base);
//event_base_loop(base, ...);

开始监听定时事件,这里的event_base_dispatch(struct event_base *base)是阻塞模式。event_base_loop(struct event_base *base, int flags)相对更为灵活,flags有如下几种类型:

#define EVLOOP_ONCE              0x01
#define EVLOOP_NONBLOCK          0x02
#define EVLOOP_NO_EXIT_ON_EMPTY  0x04

EVLOOP_ONCE: 被监听的一个或多个定时事件,任意一个事件处于激活状态被执行之后,立即跳出对base下的所有event循环监听状态;

EVLOOP_NONBLOCK:仅仅监听事件是否激活,非阻塞状态,所以若当前没有事件被激活,直接结束。这点可以通过修改tv.tv_sec = 0使定时器事件立即被激活、触发即可验证;

EVLOOP_NO_EXIT_ON_EMPTY:等同于event_base_dispatch(event_base*),循环监听事件。

       如果,想结束循环状态,可以通过如下两个函数:

// exit by base
event_base_loopbreak(struct event_base*);

// exit by base and time
event_base_loopexit(struct event_base*, timeval*);

最后,通过event_base_free(struct event_base*)释放base所占用的资源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值