龙湖地产 网站建设,个人做流量大的网站,wordpress 获得主题,网站开发人员必备技能Libevent 是一个开源的事件驱动库#xff0c;用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能#xff0c;具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。
一、事件基础结构#xff08;event_base#x… Libevent 是一个开源的事件驱动库用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。
一、事件基础结构event_base
事件基础结构event_base是 Libevent 的核心组件用于管理和调度事件。它可以看作是事件循环的主要部分负责监听和分发事件。
1.1、event_base_new
用于创建事件基础结构的函数。
原型
struct event_base *event_base_new(void);实现
#define INT_MAX 2147483647//event_base_new 创建新的event_base
struct event_base *
event_base_new(void)
{struct event_base *base NULL;struct event_config *cfg event_config_new();if (cfg) {base event_base_new_with_config(cfg);event_config_free(cfg);}return base;
}//event_config_new
struct event_config *
event_config_new(void)
{struct event_config *cfg mm_calloc(1, sizeof(*cfg));if (cfg NULL)return (NULL);TAILQ_INIT(cfg-entries);cfg-max_dispatch_interval.tv_sec -1;cfg-max_dispatch_callbacks INT_MAX;//最大可分发的回调数cfg-limit_callbacks_after_prio 1;return (cfg);
}struct event_base *
event_base_new_with_config(const struct event_config *cfg)
{int i;struct event_base *base;int should_check_environment;#ifndef EVENT__DISABLE_DEBUG_MODEevent_debug_mode_too_late 1;
#endifif ((base mm_calloc(1, sizeof(struct event_base))) NULL) {event_warn(%s: calloc, __func__);return NULL;}if (cfg)base-flags cfg-flags;should_check_environment !(cfg (cfg-flags EVENT_BASE_FLAG_IGNORE_ENV));{struct timeval tmp;int precise_time cfg (cfg-flags EVENT_BASE_FLAG_PRECISE_TIMER);int flags;if (should_check_environment !precise_time) {precise_time evutil_getenv_(EVENT_PRECISE_TIMER) ! NULL;if (precise_time) {base-flags | EVENT_BASE_FLAG_PRECISE_TIMER;}}flags precise_time ? EV_MONOT_PRECISE : 0;evutil_configure_monotonic_time_(base-monotonic_timer, flags);gettime(base, tmp);}min_heap_ctor_(base-timeheap);base-sig.ev_signal_pair[0] -1;base-sig.ev_signal_pair[1] -1;base-th_notify_fd[0] -1;base-th_notify_fd[1] -1;TAILQ_INIT(base-active_later_queue);evmap_io_initmap_(base-io);evmap_signal_initmap_(base-sigmap);event_changelist_init_(base-changelist);base-evbase NULL;if (cfg) {memcpy(base-max_dispatch_time,cfg-max_dispatch_interval, sizeof(struct timeval));base-limit_callbacks_after_prio cfg-limit_callbacks_after_prio;} else {base-max_dispatch_time.tv_sec -1;base-limit_callbacks_after_prio 1;}if (cfg cfg-max_dispatch_callbacks 0) {base-max_dispatch_callbacks cfg-max_dispatch_callbacks;} else {base-max_dispatch_callbacks INT_MAX;}if (base-max_dispatch_callbacks INT_MAX base-max_dispatch_time.tv_sec -1)base-limit_callbacks_after_prio INT_MAX;for (i 0; eventops[i] !base-evbase; i) {//①if (cfg ! NULL) {/* determine if this backend should be avoided */if (event_config_is_avoided_method(cfg,eventops[i]-name))continue;/**这里不符合我们cfg-require_features指定的I/O都不会往下走*只有满足条件的才写到event_base里面去*/if ((eventops[i]-features cfg-require_features)! cfg-require_features)continue;}/* also obey the environment variables */if (should_check_environment event_is_method_disabled(eventops[i]-name))continue;base-evsel eventops[i];base-evbase base-evsel-init(base);}if (base-evbase NULL) {event_warnx(%s: no event mechanism available,__func__);base-evsel NULL;event_base_free(base);return NULL;}if (evutil_getenv_(EVENT_SHOW_METHOD))event_msgx(libevent using: %s, base-evsel-name);/* allocate a single active event queue */if (event_base_priority_init(base, 1) 0) {event_base_free(base);return NULL;}/* prepare for threading */#if !defined(EVENT__DISABLE_THREAD_SUPPORT) !defined(EVENT__DISABLE_DEBUG_MODE)event_debug_created_threadable_ctx_ 1;
#endif#ifndef EVENT__DISABLE_THREAD_SUPPORTif (EVTHREAD_LOCKING_ENABLED() (!cfg || !(cfg-flags EVENT_BASE_FLAG_NOLOCK))) {int r;EVTHREAD_ALLOC_LOCK(base-th_base_lock, 0);EVTHREAD_ALLOC_COND(base-current_event_cond);r evthread_make_base_notifiable(base);if (r0) {event_warnx(%s: Unable to make base notifiable., __func__);event_base_free(base);return NULL;}}
#endif#ifdef _WIN32if (cfg (cfg-flags EVENT_BASE_FLAG_STARTUP_IOCP))event_base_start_iocp_(base, cfg-n_cpus_hint);
#endifreturn (base);
}① 设置支持的事件机制 libevent 中系统会自动选择适用的事件机制。libevent 会根据当前系统的支持情况以及用户在编译时的配置和运行时的环境变量来确定使用哪种事件机制。 libevent 支持多种事件机制如 epoll、kqueue、select、poll 等每种事件机制都有其特定的优势和适用场景。选择事件机制的过程如下 首先libevent 会根据编译时的配置和系统支持情况确定可用的事件机制。这些配置选项通常是在编译 libevent 时使用 ./configure 命令指定的。 常见的配置选项包括 –disable-epoll、–disable-kqueue、–disable-select 等用于禁用特定的事件机制。如果用户没有指定特定的事件机制libevent 将根据系统支持情况进行自动选择。 在运行时libevent 会检测系统支持的事件机制并进行初始化。它会按照优先级顺序尝试初始化可用的事件机制直到成功初始化一个事件机制或尝试完所有可用的事件机制。如果成功初始化了一个事件机制libevent 将使用该事件机制来驱动事件循环。如果所有可用的事件机制都无法初始化libevent 将抛出错误或警告并无法正常工作。
1.1.1、选择并配置特定的事件机制
Libevent 中可以通过以下步骤选择和配置特定的事件机制
获取系统支持的事件机制列表使用 event_get_supported_methods 函数获取系统支持的事件机制名称列表。 int i;const char **methods event_get_supported_methods();printf(Starting Libevent %s. Available methods are:\n,event_get_version());for (i0; methods[i] ! NULL; i) {printf(Support: %s\n, methods[i]);} 创建事件基础结构体使用 event_base_new 函数创建事件基础结构体。该结构体将用于管理和驱动事件循环设置事件机制通过调用 event_base_set 函数将事件基础结构体与特定的事件机制关联起来。
//将 event_method_name 替换为要使用的事件机制的名称如 epoll、kqueue、poll 或 select。
//如果事件机制设置成功event_base_set 函数将返回 0。否则返回 -1。
int result event_base_set(base, event_method_name);检查事件机制设置结果可以通过调用 event_base_get_method 函数来获取当前事件基础结构体使用的事件机制名称确认是否成功设置了特定的事件机制。
符合条件的是epoll,poll,select,根据for循环条件!base-evbase 可知第一个epoll赋给base-evbase后循环就会结束所以默认就是epoll。
1.2、event_base_dispatch
用于启动事件循环并处理注册的事件。它会一直运行直到没有更多的活动事件或显式地停止事件循环
原型
int event_base_dispatch(struct event_base *);实现
int
event_base_dispatch(struct event_base *event_base)
{return (event_base_loop(event_base, 0));
}int
event_base_loop(struct event_base *base, int flags)
{const struct eventop *evsel base-evsel;struct timeval tv;struct timeval *tv_p;int res, done, retval 0;/* Grab the lock. We will release it inside evsel.dispatch, and again* as we invoke user callbacks. */EVBASE_ACQUIRE_LOCK(base, th_base_lock);if (base-running_loop) {event_warnx(%s: reentrant invocation. Only one event_base_loop can run on each event_base at once., __func__);EVBASE_RELEASE_LOCK(base, th_base_lock);return -1;}base-running_loop 1;/**clear_time_cache 函数用于清除事件基础结构体 event_base 中的时间缓存。*时间缓存是用于存储上一次事件循环中的时间信息*以便在下一次循环中进行快速访问而不需要每次都重新获取时间*/clear_time_cache(base);if (base-sig.ev_signal_added base-sig.ev_n_signals_added)evsig_set_base_(base);done 0;#ifndef EVENT__DISABLE_THREAD_SUPPORTbase-th_owner_id EVTHREAD_GET_ID();
#endifbase-event_gotterm base-event_break 0;while (!done) {base-event_continue 0;base-n_deferreds_queued 0;/* Terminate the loop if we have been asked to */if (base-event_gotterm) {break;}if (base-event_break) {break;}tv_p tv;if (!N_ACTIVE_CALLBACKS(base) !(flags EVLOOP_NONBLOCK)) {timeout_next(base, tv_p);} else {/** if we have active events, we just poll new events* without waiting.*/evutil_timerclear(tv);}/* If we have no events, we just exit */if (0(flagsEVLOOP_NO_EXIT_ON_EMPTY) !event_haveevents(base) !N_ACTIVE_CALLBACKS(base)) {event_debug((%s: no events registered., __func__));retval 1;goto done;}/**用于将执行的事件变为活动状态。这些事件是在事件循环中通过使用 event_add *函数添加到执行队列中的。*/event_queue_make_later_events_active(base);clear_time_cache(base);res evsel-dispatch(base, tv_p);//①if (res -1) {event_debug((%s: dispatch returned unsuccessfully.,__func__));retval -1;goto done;}update_time_cache(base);timeout_process(base);if (N_ACTIVE_CALLBACKS(base)) {int n event_process_active(base);if ((flags EVLOOP_ONCE) N_ACTIVE_CALLBACKS(base) 0 n ! 0)done 1;} else if (flags EVLOOP_NONBLOCK)done 1;}event_debug((%s: asked to terminate loop., __func__));done:clear_time_cache(base);base-running_loop 0;EVBASE_RELEASE_LOCK(base, th_base_lock);return (retval);
}① 启动事件循环派发。 事件循环是 Libevent 的核心机制用于监听和处理事件。通过调用 event_base_dispatch 函数启动事件循环示例代码如下
int ret event_base_dispatch(base);
if (ret -1) {// 事件循环启动失败进行错误处理return -1;
}事件循环会一直运行直到没有事件需要处理或者调用 event_base_loopbreak 或 event_base_loopexit 函数终止事件循环。
在事件循环中Libevent 会不断地监听事件并分发给相应的回调函数进行处理。当事件满足触发条件时注册的回调函数将被调用。
1.3、event_base_free
原型
void event_base_free(struct event_base *);实现
static void
event_base_free_(struct event_base *base, int run_finalizers)
{int i, n_deleted0;struct event *ev;/* XXXX grab the lock? If there is contention when one thread frees* the base, then the contending thread will be very sad soon. *//* event_base_free(NULL) is how to free the current_base if we* made it with event_init and forgot to hold a reference to it. */if (base NULL current_base)base current_base;/* Dont actually free NULL. */if (base NULL) {event_warnx(%s: no base to free, __func__);return;}/* XXX(niels) - check for internal events first */#ifdef _WIN32event_base_stop_iocp_(base);//①
#endif/* threading fds if we have them */if (base-th_notify_fd[0] ! -1) {event_del(base-th_notify);EVUTIL_CLOSESOCKET(base-th_notify_fd[0]);if (base-th_notify_fd[1] ! -1)EVUTIL_CLOSESOCKET(base-th_notify_fd[1]);base-th_notify_fd[0] -1;base-th_notify_fd[1] -1;event_debug_unassign(base-th_notify);}/* Delete all non-internal events. */evmap_delete_all_(base);//②while ((ev min_heap_top_(base-timeheap)) ! NULL) {event_del(ev);n_deleted;}for (i 0; i base-n_common_timeouts; i) {struct common_timeout_list *ctl base-common_timeout_queues[i];event_del(ctl-timeout_event); /* Internal; doesnt count */event_debug_unassign(ctl-timeout_event);for (ev TAILQ_FIRST(ctl-events); ev; ) {struct event *next TAILQ_NEXT(ev,ev_timeout_pos.ev_next_with_common_timeout);if (!(ev-ev_flags EVLIST_INTERNAL)) {event_del(ev);n_deleted;}ev next;}mm_free(ctl);}if (base-common_timeout_queues)mm_free(base-common_timeout_queues);for (;;) {/* For finalizers we can register yet another finalizer out from* finalizer, and iff finalizer will be in active_later_queue we can* add finalizer to activequeues, and we will have events in* activequeues after this function returns, which is not what we want* (we even have an assertion for this).** A simple case is bufferevent with underlying (i.e. filters).*/int i event_base_free_queues_(base, run_finalizers);event_debug((%s: %d events freed, __func__, i));if (!i) {break;}n_deleted i;}if (n_deleted)event_debug((%s: %d events were still set in base,__func__, n_deleted));while (LIST_FIRST(base-once_events)) {struct event_once *eonce LIST_FIRST(base-once_events);LIST_REMOVE(eonce, next_once);mm_free(eonce);}if (base-evsel ! NULL base-evsel-dealloc ! NULL)base-evsel-dealloc(base);for (i 0; i base-nactivequeues; i)EVUTIL_ASSERT(TAILQ_EMPTY(base-activequeues[i]));EVUTIL_ASSERT(min_heap_empty_(base-timeheap));min_heap_dtor_(base-timeheap);mm_free(base-activequeues);evmap_io_clear_(base-io);//③evmap_signal_clear_(base-sigmap);//④event_changelist_freemem_(base-changelist);//⑤EVTHREAD_FREE_LOCK(base-th_base_lock, 0);EVTHREAD_FREE_COND(base-current_event_cond);/* If were freeing current_base, there wont be a current_base. */if (base current_base)current_base NULL;mm_free(base);
}① 停止 IOCPInput/Output Completion Port事件循环的内部函数。IOCP 是 Windows 平台上的一种事件通知机制用于异步 I/O 操作的处理② 删除事件映射event map中的所有事件。事件映射是用于管理事件的数据结构它将文件描述符或套接字映射到相应的事件处理器。③ 清除 IO 事件映射中的特定事件处理器。IO 事件映射是用于管理文件描述符或套接字相关事件处理器的数据结构。④ 清除信号事件映射中的特定事件处理器。信号事件映射是用于管理信号相关事件处理器的数据结构。⑤ 释放事件改变列表event changelist相关的内存资源。
二、 事件对象event
2.1、event
事件对象event用于表示一个特定的事件可以是文件描述符事件、定时器事件或者信号事件。每个事件对象与一个特定的事件类型和回调函数相关联。
创建事件对象的示例代码如下
#include event2/event.hvoid event_callback(evutil_socket_t fd, short events, void *arg) {// 事件发生时的回调函数
}struct event *ev event_new(base, sockfd, EV_READ | EV_PERSIST, event_callback, arg);
if (!ev) {// 创建失败进行错误处理return -1;
}// 使用事件对象进行事件处理...// 释放事件对象资源
event_free(ev);在创建事件对象后可以将其添加到事件循环中当事件满足触发条件时注册的回调函数将被调用。可以使用 event_add 函数将事件对象添加到事件循环中使用 event_del 函数将其从事件循环中移除。
2.2、bufferevent
bufferevent 是 libevent 中的一个高级抽象用于管理基于缓冲区的 I/O 操作。它提供了对网络套接字和文件描述符的封装并提供了方便的读写接口和事件回调机制。
bufferevent 的主要功能包括 缓冲区管理bufferevent 内部维护了输入缓冲区和输出缓冲区可以方便地进行读取和写入操作并支持自动调整缓冲区大小。 I/O 事件处理bufferevent 可以注册读事件和写事件的回调函数当有数据可读或可写时会触发相应的事件回调。 数据流处理bufferevent 提供了高级数据流处理功能可以自动处理粘包和拆包的问题使得应用程序可以更方便地处理数据流。 超时处理bufferevent 支持设置读取超时和写入超时可以在超时事件发生时触发相应的回调函数。 错误处理bufferevent 可以检测底层 I/O 操作的错误并通过回调函数通知应用程序进行错误处理。
通过使用 bufferevent开发者可以更加方便地进行基于缓冲区的 I/O 操作处理数据流管理超时和错误处理等。
libevent 提供了多种类型的 bufferevent 实现包括基于套接字的 bufferevent_socket 和基于文件描述符的 bufferevent_fd 等
三、 事件类型
libevent-2.1.12-stable\include\event2\event.h
/*** name event flags** Flags to pass to event_new(), event_assign(), event_pending(), and* anything else with an argument of the form short events*/
/**{*/
/** Indicates that a timeout has occurred. Its not necessary to pass* this flag to event_for new()/event_assign() to get a timeout. */
//超时事件
#define EV_TIMEOUT 0x01
/** Wait for a socket or FD to become readable */
//读事件
#define EV_READ 0x02
/** Wait for a socket or FD to become writeable */
//写事件
#define EV_WRITE 0x04
/** Wait for a POSIX signal to be raised*/
//信号事件
#define EV_SIGNAL 0x08
/*** Persistent event: wont get removed automatically when activated.** When a persistent event with a timeout becomes activated, its timeout* is reset to 0.*/
//指定事件是否持久性触发
#define EV_PERSIST 0x10
/** Select edge-triggered behavior, if supported by the backend. */
//指定事件为边缘触发Edge-Triggered模式
#define EV_ET 0x20
/*** If this option is provided, then event_del() will not block in one thread* while waiting for the event callback to complete in another thread.** To use this option safely, you may need to use event_finalize() or* event_free_finalize() in order to safely tear down an event in a* multithreaded application. See those functions for more information.**/
//
#define EV_FINALIZE 0x40
/*** Detects connection close events. You can use this to detect when a* connection has been closed, without having to read all the pending data* from a connection.** Not all backends support EV_CLOSED. To detect or require it, use the* feature flag EV_FEATURE_EARLY_CLOSE.**/
//关闭事件
#define EV_CLOSED 0x80EV_READ指定事件是读事件用于在套接字可读时触发事件。EV_WRITE指定事件是写事件用于在套接字可写时触发事件。EV_SIGNAL指定事件是信号事件用于在收到指定信号时触发事件。EV_TIMEOUT指定事件是超时事件用于在指定时间间隔后触发事件。EV_PERSIST指定事件是持久性事件用于重复触发事件直到显式地禁用或删除。EV_FINALIZE指定事件的最终化通常与event_base_loopexit()一起使用EV_CLOSE指定事件是连接关闭事件
可以使用按位或运算符 | 将不同的事件类型进行组合示例代码如下
Copy
// 创建一个文件描述符读事件和定时器事件的组合
struct event *ev event_new(base, sockfd, EV_READ | EV_TIMEOUT, event_callback, arg);四、 事件回调函数
事件回调函数是在事件触发时被调用的函数用于处理特定的事件。回调函数的原型通常为 void callback(evutil_socket_t fd, short events, void *arg)其中各个参数的含义如下
fd触发事件的文件描述符。events触发的事件类型可以是 EV_READ、EV_WRITE、EV_TIMEOUT 等。arg传递给回调函数的参数。
在回调函数中可以编写相应的逻辑来处理事件例如读取数据、写入数据、处理定时器等。
五、基于libevent源码编译
5.1、下载源码
wget -c https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz5.2、解压源码
tar -zxvf libevent-2.1.12-stable.tar.gz5.3、进入解压文件夹编译安装libevent
5.3.1、进入源码文件夹
cd libevent-2.1.12-stable5.3.2、查看configure的帮助文档
./configure --help5.3.3、执行configure脚本
./configure --prefix/usr/local/libevent5.3.4、编译并安装
make sudo make install可以看到默认动态库(shared)和静态库(static)都会编译生成可以进入usr/local/libevent/lib查看
5.4、环境变量配置
5.4.1、编写bashrc文件
vim ~/.bashrc5.4.2、配置环境变量
在bashrc文件最后添加如下内容
unset PKG_CONFIG_LIB
export PKG_CONFIG_PATH/usr/local/libevent/lib/pkgconfig:$PKG_CONFIG_PATH
export LD_LIBRARY_PATH/usr/local/libevent/lib:$LD_LIBRARY_PATH5.4.3、环境变量生效
source ~/.bashrc5.5、g编译
g编译的时候需要使用-levent指定对libevent的依赖
g -o server server.cpp -levent六、基于libevent动态库创建项目
6.1、拷贝头文件和库文件
拷贝5.3.4生成的头文件和库文件
6.2、配置CMakeLists.txt cmake_minimum_required(VERSION 3.0)
project(LIBEVENT)set(CMAKE_CXX_STANDARD 11)#设置头文件路径
include_directories(${CMAKE_SOURCE_DIR}/include)find_library(LIBEVENTNAMES eventPATHS ${CMAKE_SOURCE_DIR}/lib
)find_library(LIBEVENT_CORENAMES event_corePATHS ${CMAKE_SOURCE_DIR}/lib
)find_library(LIBEVENT_EXTRANAMES event_extraPATHS ${CMAKE_SOURCE_DIR}/lib
)find_library(LIBEVENT_OPENSSLNAMES event_opensslPATHS ${CMAKE_SOURCE_DIR}/lib
)find_library(LIBEVENT_PTHREADSNAMES event_pthreadsPATHS ${CMAKE_SOURCE_DIR}/lib
)add_executable(server_io server_ev_with_io.cpp)add_executable(server_nio server_ev_without_io.cpp)target_link_libraries(server_io ${LIBEVENT} ${LIBEVENT_CORE} ${LIBEVENT_EXTRA} ${LIBEVENT_OPENSSL} ${LIBEVENT_PTHREADS})target_link_libraries(server_nio ${LIBEVENT} ${LIBEVENT_CORE} ${LIBEVENT_EXTRA} ${LIBEVENT_OPENSSL} ${LIBEVENT_PTHREADS})七、基于libevent,原生处理IO
7.1、event_new
用于创建一个新的事件对象并将其与指定的文件描述符和事件类型关联起来
struct event *event_new(struct event_base *base,evutil_socket_t fd,short events,event_callback_fn callback,void *arg
);base指向 struct event_base 的指针表示事件所属的事件基础结构。fd要关联的文件描述符套接字、管道等。events表示要监听的事件类型的位掩码可以是以下常量之一或它们的组合 EV_READ读事件。EV_WRITE写事件。EV_SIGNAL信号事件。EV_TIMEOUT超时事件。 callback指向事件回调函数的指针。当事件发生时将调用该回调函数进行处理。arg一个指针用于传递给回调函数的用户自定义数据。
7.2、event_add
用于将事件添加到事件基础结构中以进行监听
int event_add(struct event *ev, const struct timeval *timeout);ev指向 struct event 的指针表示要添加的事件对象。timeout指向 struct timeval 的指针表示事件的超时时间。如果传递 NULL表示无超时限制。
7.3、event_assign
用于将事件对象与指定的事件回调函数和自定义数据关联起来
void event_assign(struct event *ev,struct event_base *base,evutil_socket_t fd,short events,event_callback_fn callback,void *arg
);ev指向 struct event 的指针表示要关联的事件对象。base指向 struct event_base 的指针表示事件所属的事件基础结构。fd要关联的文件描述符套接字、管道等。events表示要监听的事件类型的位掩码可以是以下常量之一或它们的组合 EV_READ读事件。EV_WRITE写事件。EV_SIGNAL信号事件。EV_TIMEOUT超时事件。 callback指向事件回调函数的指针。当事件发生时将调用该回调函数进行处理。arg一个指针用于传递给回调函数的用户自定义数据。
7.4、完整示例
#include iostream
#include event2/event.h
#include string.h
#include sys/types.h
#include sys/socket.h
#include unistd.h#define PORT 8596
#define MESSAGE_LEN 1024;//初始化socket
int tcp_server_init(int port,int listen_num);
//客户端连接事件的回调
void ev_accept_cb(int fd,short events,void * arg);
//客户端写事件的回调
void ev_client_read_cb(int fd,short events,void *arg);int main(){int sock_fdtcp_server_init(PORT,10);if(sock_fd-1){perror(socket init error);return -1;}//创建event_basestruct event_base *baseevent_base_new();//创建accept状态的读事件struct event * ev_acceptevent_new(base,sock_fd,EV_READ | EV_PERSIST,ev_accept_cb,base);event_add(ev_accept,NULL);event_base_dispatch(base);event_del(ev_accept);event_base_free(base);return 0;
}int tcp_server_init(int port,int listen_num){int error_save;evutil_socket_t socket_fd socket(AF_INET,SOCK_STREAM,0);if (socket_fd -1){return -1;}//允许多次绑定同一个地址。要用在socket和bind之间evutil_make_listen_socket_reuseable(socket_fd);struct sockaddr_in sin;sin.sin_familyAF_INET;sin.sin_addr.s_addrINADDR_ANY;sin.sin_porthtons(port);if ((bind(socket_fd,(struct sockaddr *)sin,sizeof(sin)))0){perror(bind socket error);return -1;}if((listen(socket_fd,listen_num))0){perror(listen port error);return-1;}//将套接字设置为非阻塞状态evutil_make_socket_nonblocking(socket_fd);return socket_fd;
}void ev_accept_cb(int fd,short events,void * arg){struct sockaddr_in accept_addr;socklen_t lensizeof(accept_addr);//创建连接的客户端fdevutil_socket_t client_fdaccept(fd,(struct sockaddr*)accept_addr,len);evutil_make_socket_nonblocking(client_fd);struct event_base *base(struct event_base*)arg;//创建客户端读事件struct event* ev_readevent_new(NULL,-1,0,NULL,NULL);//添加客户端读事件的回调event_assign(ev_read,base,client_fd,EV_READ | EV_PERSIST,ev_client_read_cb,(void *)ev_read);//添加客户端的读事件event_add(ev_read,NULL);
}void ev_client_read_cb(int fd,short events,void *arg){char msg[4096];struct event* ev(struct event*)arg;int lenread(fd,msg,sizeof(msg)-1);if(len 0){event_free(ev);close(fd);return;}msg[len]\0;printf(receive client msg:%s,msg);char replymsg[4096]receive msg:;strcat(replymsgstrlen(replymsg),msg);write(fd,replymsg,strlen(replymsg));
}八、基于libevent封装IO
8.1、evconnlistener_new_bind
用于创建并绑定监听器的函数
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,evconnlistener_cb cb,void *ptr,unsigned flags,int backlog,const struct sockaddr *sa,int socklen
);base指向 struct event_base 的指针表示监听器所属的事件基础结构。cb指向监听器回调函数的指针当有新连接到达时会调用该回调函数。ptr一个指针用于传递给回调函数的用户自定义数据。flags监听器的标志位用于指定监听器的行为。例如可以使用 LEV_OPT_REUSEABLE 标志启用地址重用使用 LEV_OPT_CLOSE_ON_FREE 标志在释放监听器时关闭套接字。backlog连接请求的等待队列长度。sa指向 struct sockaddr 的指针表示要绑定的地址信息。socklen地址结构体的长度
8.2、bufferevent_socket_new
用于创建基于套接字的缓冲事件的函数
struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options
);该函数用于创建一个新的缓冲事件并与给定的套接字关联起来。下面是参数的说明
base指向 struct event_base 的指针表示缓冲事件所属的事件基础结构。fd要关联的套接字描述符。options缓冲事件的选项可以是以下常量之一 BEV_OPT_CLOSE_ON_FREE在释放缓冲事件时关闭关联的套接字。BEV_OPT_THREADSAFE启用缓冲事件的线程安全模式。
8.3、bufferevent_setcb
用于设置缓冲事件的回调函数以便在不同的事件发生时进行处理
void bufferevent_setcb(struct bufferevent *bev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg
);bev指向 struct bufferevent 的指针表示要设置回调函数的缓冲事件对象。readcb指向读事件回调函数的指针。当有数据可读时将调用该回调函数。writecb指向写事件回调函数的指针。当可写入数据时将调用该回调函数。eventcb指向事件回调函数的指针。当发生异常事件如连接关闭、错误等时将调用该回调函数。cbarg一个指针用于传递给回调函数的用户自定义数据。
8.4、bufferevent_enable
用于启用或禁用缓冲事件的指定事件类型
void bufferevent_enable(struct bufferevent *bev, short event);bev指向 struct bufferevent 的指针表示要启用或禁用的缓冲事件对象。event一个表示事件类型的位掩码可以是以下常量之一或它们的组合 EV_READ启用读事件。EV_WRITE启用写事件。
8.5、完整示例
#include iostream
#include event2/event.h
#include event2/listener.h
#include event2/bufferevent.h
#include event2/buffer.h
#include string.h
#include sys/types.h
#include sys/socket.h
#include unistd.h//客户端连接回调
void listener_cb(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr* addr,int socklen,void *arg);
//客户端读事件回调
void socket_read_cb(struct bufferevent * bev,void *arg);
//客户端事件回调
void socket_event_cb(struct bufferevent *bev, short event, void *arg);
int main(){//创建event_basestruct event_base * baseevent_base_new();//声明地址结构体设置服务端地址参数struct sockaddr_in sin;memset(sin,0,sizeof(struct sockaddr_in));sin.sin_familyAF_INET;sin.sin_porthtons(8596);struct evconnlistener* evlistener evconnlistener_new_bind(base,listener_cb,base,LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,10,(struct sockaddr*)sin,sizeof(struct sockaddr_in));event_base_dispatch(base);evconnlistener_free(evlistener);event_base_free(base);return 0;
}void listener_cb(struct evconnlistener *listener,evutil_socket_t fd,struct sockaddr* addr,int socklen,void *arg){char ip[32] {0};evutil_inet_ntop(AF_INET,addr,ip,sizeof(ip)-1);printf(accept client ip:%s\n,ip);struct event_base *base evconnlistener_get_base(listener);struct bufferevent *bebufferevent_socket_new(base,fd,BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(be,socket_read_cb,NULL,socket_event_cb,arg);bufferevent_enable(be,EV_READ | EV_PERSIST);// 添加以下代码来设置读回调函数//bufferevent_setwatermark(be, EV_READ, 0, 4096); // 设置读取水位标记//bufferevent_enable(be, EV_READ); // 启用读事件
}void socket_read_cb(struct bufferevent * bev,void *arg){struct evbuffer *evbufbufferevent_get_input(bev);char *msg evbuffer_readln(evbuf,NULL,EVBUFFER_EOL_LF);if(!msg) return;printf(server read data:%s\n,msg);char reply[4096]{0};sprintf(reply,receive msg:%s\n,msg);free(msg);bufferevent_write(bev,reply,strlen(reply));
}void socket_event_cb(struct bufferevent *bev, short event, void *arg){if (event BEV_EVENT_EOF)printf(connection closed\n);else if (event BEV_EVENT_ERROR)printf(some other error\n);//这将自动close套接字和free读写缓冲区bufferevent_free(bev);
}