当前位置: 首页 > news >正文

服务器搭建(TCP套接字)-libevent版(服务端)

     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 (r<0) {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 (i=0; 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==(flags&EVLOOP_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_deleted=0;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;/* Don't 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; doesn't 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 we're freeing current_base, there won't be a current_base. */if (base == current_base)current_base = NULL;mm_free(base);
}

  • 停止 IOCP(Input/Output Completion Port)事件循环的内部函数。IOCP 是 Windows 平台上的一种事件通知机制,用于异步 I/O 操作的处理

  • 删除事件映射(event map)中的所有事件。事件映射是用于管理事件的数据结构,它将文件描述符(或套接字)映射到相应的事件处理器。

  • 清除 IO 事件映射中的特定事件处理器。IO 事件映射是用于管理文件描述符(或套接字)相关事件处理器的数据结构。

  • 清除信号事件映射中的特定事件处理器。信号事件映射是用于管理信号相关事件处理器的数据结构。

  • 释放事件改变列表(event changelist)相关的内存资源。

二、 事件对象(event)

2.1、event

事件对象(event)用于表示一个特定的事件,可以是文件描述符事件、定时器事件或者信号事件。每个事件对象与一个特定的事件类型和回调函数相关联。

创建事件对象的示例代码如下:

#include <event2/event.h>void 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.  It's 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: won't 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	0x80
  • EV_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.gz

5.2、解压源码

tar -zxvf libevent-2.1.12-stable.tar.gz

5.3、进入解压文件夹,编译安装libevent

5.3.1、进入源码文件夹

cd libevent-2.1.12-stable

5.3.2、查看configure的帮助文档

./configure --help

在这里插入图片描述
在这里插入图片描述

5.3.3、执行configure脚本

./configure --prefix=/usr/local/libevent

5.3.4、编译并安装

make && sudo make install

可以看到默认动态库(shared)和静态库(static)都会编译生成,可以进入usr/local/libevent/lib查看
在这里插入图片描述

5.4、环境变量配置

5.4.1、编写bashrc文件

vim ~/.bashrc

5.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_PATH

5.4.3、环境变量生效

source ~/.bashrc

5.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_fd=tcp_server_init(PORT,10);if(sock_fd==-1){perror("socket init error");return -1;}//创建event_basestruct event_base *base=event_base_new();//创建accept状态的读事件struct event * ev_accept=event_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_family=AF_INET;sin.sin_addr.s_addr=INADDR_ANY;sin.sin_port=htons(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 len=sizeof(accept_addr);//创建连接的客户端fdevutil_socket_t client_fd=accept(fd,(struct sockaddr*)&accept_addr,&len);evutil_make_socket_nonblocking(client_fd);struct event_base *base=(struct event_base*)arg;//创建客户端读事件struct event* ev_read=event_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 len=read(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(replymsg+strlen(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 * base=event_base_new();//声明地址结构体,设置服务端地址参数struct sockaddr_in sin;memset(&sin,0,sizeof(struct sockaddr_in));sin.sin_family=AF_INET;sin.sin_port=htons(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 *be=bufferevent_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 *evbuf=bufferevent_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);
}

相关文章:

服务器搭建(TCP套接字)-libevent版(服务端)

Libevent 是一个开源的事件驱动库&#xff0c;用于开发高性能、并发的网络应用程序。它提供了跨平台的事件处理和网络编程功能&#xff0c;具有高性能、可扩展性和可移植性。下面详细讲解 Libevent 的主要组成部分和使用方法。 一、事件基础结构&#xff08;event_base&#x…...

斐波那契模型系列【动态规划】

动态规划步骤 1、状态表示 是什么&#xff1a;dp表&#xff08;可能是一维或二维数组&#xff09;里的值所表示的含义。 怎么来&#xff1a; 1、题目要求 2、经验题目要求 3、发现重复子问题 2、状态转移方程 dp[i]... 3、初始化 保证填表不越界 4、填表顺序 5、返回值 写代码时…...

【Java】微服务——Nacos注册中心

目录 1.Nacos快速入门1.1.服务注册到nacos1&#xff09;引入依赖2&#xff09;配置nacos地址3&#xff09;重启 2.服务分级存储模型2.1.给user-service配置集群2.2.同集群优先的负载均衡 3.权重配置4.环境隔离4.1.创建namespace4.2.给微服务配置namespace 5.Nacos与Eureka的区别…...

Redis Cluster Gossip Protocol: PING, PONG, MEET

返回目录 PING / PONG / MEET 的发送 过程 计算freshNodes。freshNodes表示在消息中能携带的&#xff0c;在cluster节点字典中的节点总数&#xff0c;但需要减去myself和对端节点&#xff0c;因为myself的信息会存储在消息头中。实际上&#xff0c;并非所有在cluster节点字典…...

httpserver 下载服务器demo 以及libevent版本的 httpserver

实现效果如下&#xff1a; 图片可以直接显示 cpp h 这些可以直接显示 其他的 则是提示是否要下载 单线程 还有bug 代码如下 先放上来 #include "httpserver.h" #include "stdio.h" #include <stdlib.h> #include <arpa/inet.h> #include…...

构建强大的RESTful API:@RestController与@Controller的对比与应用

构建强大的RESTful API&#xff1a;RestController与Controller的对比与应用 前言什么是RESTful APIRestController&#xff0c;Controller&#xff0c;ResponseBody1. Controller注解&#xff1a;2. RestController注解&#xff1a;3. ResponseBody注解&#xff1a; 示例非thy…...

【Java-LangChain:使用 ChatGPT API 搭建系统-10】评估(下)-当不存在一个简单的正确答案时

第十章&#xff0c;评估&#xff08;下&#xff09;-当不存在一个简单的正确答案时 在上一章中&#xff0c;了解了如何评估 LLM 模型在 有明确正确答案 的情况下的输出&#xff0c;我们可以编写一个函数来判断 LLM 输出是否正确地分类并列出产品。 然而&#xff0c;如果 LLM …...

【微服务的集成测试】python实现-附ChatGPT解析

1.题目 微服务的集成测试 知识点:深搜 时间限制: 1s 空间限制: 256MB 限定语言:不限 题目描述: 现在有n个容器服务,服务的启动可能有一定的依赖性 (有些服务启动没有依赖)其次服务自身启动加载会消耗一些时间。 给你一个 nxn 的二维矩阵 useTime,其中 useTime[i][i]=10 表示…...

Mesa新版来袭

Mesa 17.1.6 发布了&#xff0c;Mesa 是一个三维&#xff08;3D&#xff09;图形库的开源集合&#xff0c;其主要目标是在 Linux / UNIX 操作系统下实现各种 API&#xff08;应用程序编程接口&#xff09;和 OpenGL 规范。 它面向 3D 计算机图形&#xff0c;硬件加速 3D 渲染和…...

基于 SpringBoot 2.7.x 使用最新的 Elasticsearch Java API Client 之 ElasticsearchClient

1. 从 RestHighLevelClient 到 ElasticsearchClient 从 Java Rest Client 7.15.0 版本开始&#xff0c;Elasticsearch 官方决定将 RestHighLevelClient 标记为废弃的&#xff0c;并推荐使用新的 Java API Client&#xff0c;即 ElasticsearchClient. 为什么要将 RestHighLevelC…...

辅助驾驶功能开发-功能对标篇(15)-NOA领航辅助系统-吉利

1.横向对标参数 厂商吉利车型FX11/EX11/DCY11/G636上市时间2022Q4方案6V5R+1DMS摄像头前视摄像头1*(8M)侧视摄像头/后视摄像头1环视摄像头4DMS摄像头1雷达毫米波雷达54D毫米波雷达/超声波雷达12激光雷达/域控供应商福瑞泰克辅助驾驶软件供应商福瑞泰克高精度地图百度芯片TDA4 T…...

javascript: Sorting Algorithms

// Sorting Algorithms int JavaScript https://www.geeksforgeeks.org/sorting-algorithms/ /** * file Sort.js * 1. Bubble Sort冒泡排序法 * param arry * param nszie */ function BubbleSort(arry, nszie) {var i, j, temp;var swapped;for (i 0; i < nszie - 1; i)…...

嵌入式Linux应用开发-驱动大全-同步与互斥④

嵌入式Linux应用开发-驱动大全-同步与互斥④ 第一章 同步与互斥④1.5 自旋锁spinlock的实现1.5.1 自旋锁的内核结构体1.5.2 spinlock在UP系统中的实现1.5.3 spinlock在SMP系统中的实现 1.6 信号量semaphore的实现1.6.1 semaphore的内核结构体1.6.2 down函数的实现1.6.3 up函数的…...

2023年【高压电工】证考试及高压电工复审模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 高压电工证考试根据新高压电工考试大纲要求&#xff0c;安全生产模拟考试一点通将高压电工模拟考试试题进行汇编&#xff0c;组成一套高压电工全真模拟考试试题&#xff0c;学员可通过高压电工复审模拟考试全真模拟&a…...

C/C++学习 -- 分组密算法(3DES算法)

1. 3DES算法概述 3DES&#xff08;Triple Data Encryption Standard&#xff09;&#xff0c;又称为TDEA&#xff08;Triple Data Encryption Algorithm&#xff09;&#xff0c;是一种对称加密算法&#xff0c;是DES&#xff08;Data Encryption Standard&#xff09;的加强版…...

C/C++面试题总结

1.new与malloc的区别 new操作符从自由存储区上为对象动态分配内存空间&#xff0c;而malloc函数从堆上动态分配内存。 使用new操作符申请内存分配时无须指定内存块的大小&#xff0c;而malloc则需要显式地指出所需内存的尺寸。 int *p new int; delete p;//一定要配对使用n…...

Java下正面解除警告Unchecked cast: ‘java.lang.Object‘ to ‘java.util.ArrayList‘

就是我在反序列化时&#xff0c;遇到这样一个警告&#xff1a; Unchecked cast: java.lang.Object to java.util.ArrayList<com.work1.Student>然后我去网上查&#xff0c;有些人说用SuppressWarnings(“unchecked”)去忽略警告&#xff0c;但是我觉得作为一名合格的程序…...

图像处理与计算机视觉--第四章-图像滤波与增强-第二部分

目录 1.图像噪声化处理与卷积平滑 2.图像傅里叶快速变换处理 3.图像腐蚀和膨胀处理 4 图像灰度调整处理 5.图像抖动处理算法 学习计算机视觉方向的几条经验: 1.学习计算机视觉一定不能操之过急&#xff0c;不然往往事倍功半&#xff01; 2.静下心来&#xff0c;理解每一个…...

[前端基础]typescript安装以及类型拓展

&#xff08;0&#xff09;写在前面&#xff1a; 作者之前都是在写js&#xff0c;所以这里介绍ts肯定是不能从头开始介绍了&#xff0c;主要在js的基础上介绍ts关于类型的几个特性&#xff0c;以及ts的安装还有配置问题 &#xff08;1&#xff09;ts和js是什么关系 通俗点来…...

网络参考资料汇总(1)

将这段时间参考的各路大佬的资料加以汇总分类&#xff1a; &#xff08;1&#xff09;FFmpeg: 基于FFmpeg进行rtsp推流及拉流&#xff08;详细教程&#xff09; Linux 编译安装 FFmpeg 步骤&#xff08;带ffplay&#xff09; Jetson 环境安装(三):jetson nano配置ffmpeg和ngin…...

Remove和RemoveLast用法

LeetCode 46 全排列 先贴代码 class Solution {List<List<Integer>> result new ArrayList<>();List<Integer> temp new ArrayList<>();public List<List<Integer>> permute(int[] nums) {dfs(nums, 0);return result;}public v…...

(一) 使用 Hugo 搭建个人博客保姆级教程(上篇)

手把手教你如何从0开始构建一个静态网站&#xff0c;这不需要有太多的编程和开发经验和时间投入&#xff0c;也基本不需要多少成本&#xff08;除了个性化域名&#xff09;&#xff0c;使用GitHub和Hugo模板即可快速构建和上线一个网站。 目标读者 本文档适用于以下用户&…...

数据结构之栈

栈的模拟实现 1.栈的概念2.栈的方法3.栈的模拟实现(代码)3.1 接口My_Stack3.2 StackList3.3 异常类StackException3.4 测试类Test 1.栈的概念 2.栈的方法 3.栈的模拟实现(代码) 3.1 接口My_Stack 3.2 StackList 3.3 异常类StackException 3.4 测试类Test...

wireshark of tshark tools v3.4.0版本 支持json

tshark(1) Install tshark (Wireshark) Ver.3.4.0 on CentOS7 --It must be "ps", "text", "pdml", "psml" or "fields". TCP 协议中的三次握手和四次挥手是 TCP 连接建立和关闭的过程。 三次握手 客户端向服务器发送 SYN…...

Python开源项目月排行 2023年9月

#2023年9月2023年9月9日1fishdraw这个项目是用来随机生成一条鱼的&#xff0c;这条鱼特别的稀奇古怪&#xff0c;这个项目不依赖任何库&#xff0c;支持 svg, json, csv 等格式。2vizro一个用于创建模块化数据可视化应用程序的工具包。在几分钟内快速自助组装定制仪表板 - 无需…...

uniapp项目实践总结(二十五)苹果 ios 平台 APP 打包教程

导语:当你的应用程序开发完成后,在上架 ios 应用商店之前,需要进行打包操作,下面就简单介绍一下打包方法。 目录 准备工作注册账号生成证书打包配置准备工作 在打包之前,请保证你的 uniapp 应用程序编译到 ios 模拟器或者是真机调试基座环境下是可以正常运行的,苹果打包…...

MySQL查询(基础到高级)

一、单表查询&#xff1a; 1.基本查询&#xff1a; 1.1 查询多个字段&#xff1a; 1.查询所有字段&#xff1a; select * from 表名;2.查询指定字段&#xff1a; select 字段1,字段2 from 表名; 1.2 去除重复记录 select distinct "字段" FROM "表名"; …...

电脑通过串口助手和51单片机串口通讯

今天有时间把电脑和51单片机之间的串口通讯搞定了&#xff0c;电脑发送的串口数据&#xff0c;单片机能够正常接收并显示到oled屏幕上&#xff0c;特此记录一下&#xff0c;防止后面自己忘记了怎么搞得了。 先来两个图片看看结果吧&#xff01; 下面是串口3.c的文件全部内容&a…...

【Linux】线程详解完结篇——信号量 + 线程池 + 单例模式 + 读写锁

线程详解第四篇 前言正式开始信号量引例信号量的本质信号量相关的四个核心接口生产消费者模型用环形队列实现生产者消费者模型基于环形队列的生产消费模型的原理代码演示单生产者单消费者多生产者多消费者 计数器的意义 线程池基本概念代码 单例模式STL,智能指针和线程安全STL中…...

弧度、圆弧上的点、圆的半径(r)、弧长(s)之间的关系

要计算弧度和圆弧上的点&#xff0c;需要知道以下几个要素&#xff1a; 圆的半径&#xff08;r&#xff09;&#xff1a;即圆的中心到圆周上任意一点的距离。 弧长&#xff08;s&#xff09;&#xff1a;从圆周上的一个点到另一个点所经过的弧长。 弧度&#xff08;θ&#x…...

珠海市网站开发公司/营销网站建设制作

开发原因&#xff1a; 页面需要做页面统计&#xff0c;需要访问域。 访问域方法&#xff1a; using System.DirectoryServices.AccountManagement;命名空间负责管理。 MSDN&#xff1a;https://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement%2…...

问答类网站怎么做啊/自制网页

除了最初发表的以"熔岩音乐"和"流利阅读"为范本&#xff0c;以及初学者入门点这里&#xff0c;内容太过于巨细靡遗&#xff0c;主要是为了给入门朋友详解相关的基本流程&#xff0c;但之后所有文章唯有对于实例的重点讲解&#xff0c;主要在于培养大家对于…...

如何加强省市级门户网站的建设/成人大学报名官网入口

出发点&#xff1a; 微服务架构上通过业务来划分服务的&#xff0c;通过REST调用&#xff0c;对外暴露的一个接口&#xff0c;可能需要很多个服务协同才能完成这个接口功能&#xff0c;如果链路上任何一个服务出现问题或者网络超时&#xff0c;都会形成导致接口调用失败。随着业…...

泊头网站建设/宁波seo网站排名优化公司

为什么80%的码农都做不了架构师&#xff1f;>>> ./config --prefix/usr/local/OpenSSL -fPIC shared 转载于:https://my.oschina.net/eduosi/blog/782783...

南京做电商网站的公司/软文素材

原文: Comparing Virtual Machines vs Docker Containers 译者: Fundebug 为了保证可读性&#xff0c;本文采用意译而非直译。另外&#xff0c;本文版权归原作者所有&#xff0c;翻译仅用于学习。 首先&#xff0c;大家需要明确一点&#xff0c;Docker容器不是虚拟机。 2014年&…...

怎么宣传自己的网站推广/百度关键词排名技术

Cobar执行流程:...