libevent网络库基础知识总结和代码实例(1)

道虽迩,不行不至;事虽小,不为不成。

简介和基本环境安装

libevent是一款事件驱动的网络开发包,由于采用c语言开发体积小巧,跨平台,速度极快。大量开源项目使用了libevent比如谷歌浏览器和分布式的高速缓存系统Mem-cached。libevent支持kqueue、select、poll、epoll、iocp。内部事件机制完全独立于公开的事件APl,Libevent支持跨平台可以在Linux、BSD、Mac OS x、Solaris、Windows等平台上编译。下载地址libevent.net

安装环境(环境有先后顺序)

  • perl(编译openssl使用)
  • nasm
  • zlib 1.2.11
  • openssl 1.1.1
  • libevent 2.1.8
sudo apt-get install perl g++ make automake libtool unzip
接口分析
event_base结构源码解析

后续补上

服务端接收连接

服务器接收连接代码

#include <event2/event.h>
#include <event2/listener.h>

#include <iostream>

#include <cstring>
#include <csignal>

#define SPORT 5001

// 回调函数
void listen_cb(struct evconnlistener *e, evutil_socket_t s, 
		struct sockaddr *a, int socklen, void* arg) {
	std::cout << "listen_cb" << std::endl;
}

int main() {
	
#ifdef ____Win32
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2) &wsa);
#else
    /** 忽略管道信息
    *   发送数据给已经关闭的socket时程序会宕掉,因此我们在此忽略他
    */
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        return 1;
    }
#endif
	// 创建libevent的上下文
	event_base *base = event_base_new();
	if (base) {
		std::cout << "event_base_new success! \n";
	}
	
	struct sockaddr_in sin;
	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SPORT);

    /** 分配一个新的evconnlistener对象来监听给定文件描述符上传入的TCP连接
	* 此处包含了socket(), bind(), listen(), accept四个functions
	* 此处为什么可以有回调函数?就是因为此处还调用了一个accept阻塞!
    */
	evconnlistener *ev = evconnlistener_new_bind(
		base, // libevent的上下文
		listen_cb, 	// 接收到连接的回调函数
		base, // 回调函数的参数之一arg
		/**  LEV_OPT_REUSEABLE: 地址重用
		* LEV_OPT_CLOSE_ON_FREE: 关闭同时清理socket
		*/
		LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 
		10,	// 连接队列大小
		(struct sockaddr*)&sin,	// 服务器绑定的IP和port
		sizeof(sin) //sin的大小
		);
	
	// 事件分发处理
	if (base) {
		event_base_dispatch(base);
		std::cout << "count: " << event_base_get_num_events(base, EVENT_BASE_COUNT_ADDED) << std::endl;
	}
	if (ev) {
		evconnlistener_free(ev);
	}
	if (base) {
		// 销毁上下文
		event_base_free(base);
	}
	return 0;
}

此处提一下event_config_new,这是用来配置event_base上下文的

struct event_config {
	TAILQ_HEAD(event_configq, event_config_entry) entries;

	int n_cpus_hint;
	struct timeval max_dispatch_interval;
	int max_dispatch_callbacks;
	int limit_callbacks_after_prio;
	/* 主要配置以下两项 */
	enum event_method_feature require_features;
	enum event_base_config_flag flags;
};
enum event_method_feature {
	/* 边缘触发点后端 */
    EV_FEATURE_ET = 0x01,
    /* 要求添加、删除单个事件,或者确定哪个事件激活的操作是0 (1)复杂度的后端 */
    EV_FEATURE_O1 = 0x02,
    /* 要求支持任意文件描述符,而不仅仅是套接字的后端 */
    EV_FEATURE_FDS = 0x04,
    /* 检测连接关闭事件。 */
    EV_FEATURE_EARLY_CLOSE = 0x08
};

enum event_base_config_flag {
	/* 不要为event_ base分配锁。设置这个选项可以为event_ base 节省一点用于锁定和解锁的时间, */
	EVENT_BASE_FLAG_NOLOCK = 0x01,
	/* 忽略环境变量影响 */
	EVENT_BASE_FLAG_IGNORE_ENV = 0x02,
	/* 仅用于Windows,启用任何必需的I0CP分发逻辑 */
	EVENT_BASE_FLAG_STARTUP_IOCP = 0x04,
	/* 不是在事件循环每次准备执行超时回调时检测当前时间,而是在每次超时回调后进行检测 */
	EVENT_BASE_FLAG_NO_CACHE_TIME = 0x08,
	/* epoll下有效,防止同一个fd多次激发事件,fd如果做复制会有bug */
	EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST = 0x10,
	/* 默认使用系统最快的记时机制,如果系统有较慢且更精确的则采用 */
	EVENT_BASE_FLAG_PRECISE_TIMER = 0x20
};

边缘触发(ET):当状态变化时触发
水平触发(LT):满足条件触发

设置config配置

struct event_base * event_base_new_with_config(const struct event_config * 	cfg)

/* 设置require_feature */
int event_config_require_features(
	struct event_config * 	cfg,
	int 	feature 
)
/* 设置flag */
int event_config_set_flag(
	struct event_config * 	cfg,
	int 	flag 
)

显示支持的网络模式

/** 获取Libevent支持的所有事件通知机制。
* 这个函数按照Libevent首选的顺序返回事件机制。注意,这个列表将包括Libevent已编译支持的所有后端,并且不一定检查您的操作系统是否拥有所需的资源。
*/
const char ** event_get_supported_method(void);

获取当前的网络模型

/* Get the kernel event notification mechanism used by Libevent. */
const char * event_base_get_method(const struct event_base *base)

禁用某一个网络模型

/* Enters an event method that should be avoided into the configuration.

This can be used to avoid event mechanisms that do not support certain file descriptor types, or for debugging to avoid certain event mechanisms. An application can make use of multiple event bases to accommodate incompatible file descriptor types. */
int event_config_avoid_method(
	struct event_config * 	cfg,
	const char * 	method
	)

其他相关可见官网文档(我整理成了一个易读性高的文档,欢迎大家前来访问,具体网站稍后奉上)

Event事件处理
事件状态分析
// 已初始化initialized
event_new()// 创建一个事件,将socket和event绑定

// 待觉pending
event_add() // 将事件添加到上下文中

// 激活active
// 事件发生会变成active状态,当事件调用完又回到non-pending状态

// 持久的persistent

event_new():初始化一个事件并绑定socket

struct event * event_new(
	struct event_base * 	base,// 
	evutil_socket_t 	fd,// 要监视的文件描述符或信号,或-1。
	short 	events,// 需要监控的事件:EV_READ、EV_WRITE、EV_SIGNAL、EV_PERSIST、EV_ET的位域
	event_callback_fn 	cb,// 事件发生时调用的回调函数
	void * 	arg // 传递给回调函数的参数
)
// 源码
struct event * event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
{
	struct event *ev;
	ev = mm_malloc(sizeof(struct event));
	if (ev == NULL)
		return (NULL);
	if (event_assign(ev, base, fd, events, cb, arg) < 0) {
		mm_free(ev);
		return (NULL);
	}

	return (ev);
}

/* 此处解释一下events参数 */
/*
	EV_TIMEOUT:超时,默认忽略
	EV_READ:读事件
	EV_WRITE:写事件
	EV_SIGNAL:信号事件
	EV_PERSIST:持久化事件,active状态后回到pending,默认是non-penging
	EV_ET:边缘触发,影响read
*/

event_add():将事件添加到上下文中

int event_add(
	struct event * 	ev,// 通过event_assign()或event_new()初始化的事件结构
	const struct timeval * 	timeout // 超时事件,否则永远等待
)

// 源码
int event_add(struct event *ev, const struct timeval *tv)
{
	int res;

	if (EVUTIL_FAILURE_CHECK(!ev->ev_base)) {
		event_warnx("%s: event has no event_base set.", __func__);
		return -1;
	}
	// 此时是多线程的情况下的锁机制
	EVBASE_ACQUIRE_LOCK(ev->ev_base, th_base_lock);
	res = event_add_nolock_(ev, tv, 0);
	EVBASE_RELEASE_LOCK(ev->ev_base, th_base_lock);

	return (res);
}

event_del():将事件从上下文移除

/* 从监控的事件集中删除一个事件。 */
int event_del(struct event *ev)

// 源码
int event_del(struct event *ev)
{
	return event_del_(ev, EVENT_DEL_AUTOBLOCK);
}
static int event_del_(struct event *ev, int blocking)
{
	int res;
	struct event_base *base = ev->ev_base;

	if (EVUTIL_FAILURE_CHECK(!base)) {
		event_warnx("%s: event has no event_base set.", __func__);
		return -1;
	}

	EVBASE_ACQUIRE_LOCK(base, th_base_lock);
	res = event_del_nolock_(ev, blocking);
	EVBASE_RELEASE_LOCK(base, th_base_lock);

	return (res);
}
定时器接口

是为event_add()的timeout参数服务的。

对于超时事件,超时后存储的数据结构是二叉堆(binary heap),添加和删除都是时间复杂度是O(logn),定时器接口的超时数据结构是双向队列,时间复杂度是O(1),因此使用定时器可以进行优化。

双向队列的适用于大量具体相同超时值的时候

// 初始化一个事件no pending
evtimer_new()	
// 转化成pending
evtimer_add()
// active
// 发生事件
struct timeval {
	long tv_sec;	/* second */
	long tv_usec; /* and microseconds */
};
event_base *base = event_base_new();
event *e = event_new(...);

// 传统
static timeval t1 = {3, 0};
event_add(e, t1)//O(logn)

//超时优化性能优化
static timeval t1 = {3, 0};
const timeval *t;
t = event_base_init_common_timeout(base, &t1);// 绑定超时事件

event_add(e, t)// O(1)
信号事件

下面是头文件 signal.h 中定义的宏,这些宏将在下列两个函数中使用。SIG_ 宏与 signal 函数一起使用来定义信号的功能

序号宏 & 描述
1SIG_DFL,默认的信号处理程序。
2SIG_ERR,表示一个信号错误。
3SIG_IGN,忽视信号。

SIG 宏用于表示以下各种条件的信号码:

信号
SIGABRT(Signal Abort) 程序异常终止。
SIGFPE(Signal Floating-Point Exception) 算术运算出错,如除数为 0 或溢出(不一定是浮点运算)。
SIGILL(Signal Illegal Instruction) 非法函数映象,如非法指令,通常是由于代码中的某个变体或者尝试执行数据导致的。
SIGINT(Signal Interrupt) 中断信号,如 ctrl-C,通常由用户生成。
SIGSEGV(Signal Segmentation Violation) 非法访问存储器,如访问不存在的内存单元。
SIGTERM(Signal Terminate) 发送给本程序的终止请求信号

回调函数中表示事件本身的操作:event_self_cbarg()

文件读取事件

直接写实例代码就vans

#include <event2/event.h>// libevent头文件

#include <iostream>// C++标准库
#include <thread>

#ifndef ____Win32
#include <csignal>// C标准函数库
#else
#endif

#include <sys/types.h>// linux系统调用库
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>

void read_file(evutil_socket_t, short, void *);

int main() {
	
#ifdef ____Win32
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2) &wsa);
#else
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        return 1;
    }
#endif
	event_config *conf = event_config_new();
	event_config_require_features(conf, EV_FEATURE_FDS);
	
	event_base *base = event_base_new_with_config(conf);
	event_config_free(conf);

	int sock = open("/var/og/auth/log", O_RDONLY | O_NOBLOCK, 0);
	
	// 文件指针移到结尾处
	lseek(sock, 0, SEEK_END);
	event *fev = event_new(
					base, sock, EV_READ | EV_PERSIST, read_file, 0);
	event_add(fev, NULL);
	
	event_base_dispatch(base);
	event_base_free(base);
	return 0;
}

void read_file(evutil_socket_t fd, short event, void *arg) {
	char buf[1024] = 0;
	int len = read(fd, buf, sizeof(buf) - 1);
	if (len > 0) {
		std::cout << buf << std::endl;
	} 
	else {
		std::cout << "." << std::flush;	
		this_thread::sleep_for(500ms);
	}
}
网络服务器事件

老活

#include <event2/event.h>// libevent头文件

#include <iostream>// C++标准库
#include <thread>

#ifndef ____Win32
#include <csignal>// C标准函数库
#include <cstring>
#else
#endif

#include <sys/types.h>// linux系统调用库
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SPORT 5001

void listen_cb(evutil_socket_t, short, void *);
void client_cb(evutil_socket_t, short, void *);

int main() {
#ifdef ____Win32
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2) &wsa);
#else
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        return 1;
    }
#endif
    event_base *base = event_base_new();

    evutil_socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
    
    // 设置地址复用和非阻塞
    evutil_make_socket_nonblocking(sock);
    evutil_make_listen_socket_reuseable(sock);

    sockaddr_in sin;
    memset(&sin, 0, sizoef(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SPORT);

    ::bind(sock, (sockaddr*) &sin, sizeof(sin));

    listen(sock, 10);

    // 开始接收连接事件,默认水平触发
    /* 先把监听套接字加入到base中产生事件创建客户的socket */
    event *ev = event_new(base, sock, EV_READ | EV_PERSIST, listen_cb, base);
    event_add(ev, 0);

    event_base_dispatch(base);
    evutil_closesocket(sock);
    event_base_free(base);
    return 0;
}
void listen_cb(evutil_socket_t s, short w, void *arg) {
    sockaddr_in sin;
    socklen_t size = sizoef(sin);

    evutil_socket_t client = accept(s, (sockaddr*) &sin, &size);

    char ip[16] = { 0 };
    evutil_inet_ntop(AF_INET, &sin.in_addr, ip, sizoef(ip) - 1);
    std::cout << "ip is : " << ip << std::endl;

    // 客户端数据读取
    event_base *base = (event_base *)arg;
    /* EV_ET:边缘触发 */
    event *ev = event_new(base, client, EV_READ | EV_PERSIST | EV_ET, client_cb, event_self_cbarg());
    timeval t = {10, 0};
    event_add(ev, &t);
}
void client_cb(evutil_socket_t s, short w, void *arg) {
    event* ev = (event)arg;

    if (w & EV_TIMEOUT) {
        std::cout << "timeout" << std::endl;
        event_free(ev); // 清理这个事件
        evutil_closesocket(s);
        return;
    }
    char buf[1024] = {0};
    int len = recv(s, buf, sizoef(buf) - 1, 0);
    if (len > 0) {
        std::cout << buf << std::endl;
        send(s, "ok", 2, 0);
    }
    else { 
        std::cout << "." << std::flush;
        event_free(ev); // 清理这个事件
        evutil_closesocket(s);
    }  
}
循环事件

循环是对event_base结构起作用的

event_base_dispatch(struct event_base *base);

event_base_loop(struct event_base *base, int flags);

/* flags,注意,此处的返回不是停止循环,而是状态返回,即active返回为no pending */
#define EVLOOP_ONCE 0x01	// 等待一个事件运行,直到没有再返回(他要先等待一个事件的产生)
#define EVLOOP_NONBLOACK 0x02 // 非阻塞,没有事件立刻返回,默认阻塞
#define EVLOOP_NO_EXIT_ON_EMPTY 0x04// 空的时候不退出,默认退出

来看源码

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(base);

	if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)
		evsig_set_base_(base);

	done = 0;

#ifndef EVENT__DISABLE_THREAD_SUPPORT
	base->th_owner_id = EVTHREAD_GET_ID();
#endif

	base->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_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);
}

停止循环

/** 运行完所有激活事件的回调之才退出
* 事件循环没有运行时,下一-轮回调完成
* 后立即停止
*/
int event_base_loopexit(struct event_base *event_base, const struct timeval *tv)
{
	return (event_base_once(event_base, -1, EV_TIMEOUT, event_loopexit_cb,
		    event_base, tv));
}
/** 执行完当前正在处理的事件后立即退
* 出,如无操作则立即退出
*/
int event_base_loopbreak(struct event_base *event_base)
{
	int r = 0;
	if (event_base == NULL)
		return (-1);

	EVBASE_ACQUIRE_LOCK(event_base, th_base_lock);
	event_base->event_break = 1;

	if (EVBASE_NEED_NOTIFY(event_base)) {
		r = evthread_notify_base(event_base);
	} else {
		r = (0);
	}
	EVBASE_RELEASE_LOCK(event_base, th_base_lock);
	return r;
}
bufferevent缓冲区
基本概念和API

缓冲区,数据输入或者输出都会经过缓冲区evbuffer。

建立缓冲区

struct bufferevent * bufferevent_socket_new(
	struct event_base * 	base,
	evutil_socket_t 	fd,
	int 	options 
)
/* options */
enum bufferevent_options {
	BEV_OPT_CLOSE_ON_FREE = (1<<0),	/* 释放时关闭socket */
	BEV_OPT_THREADSAFE = (1<<1), /* 线程安全,自动分配锁,可以在多线程中使用 */
	
	/* 延迟回调不会立即调用,而是event_ loop ()调用中被排队,通常的事件回调之后执行 */
	BEV_OPT_DEFER_CALLBACKS = (1<<2),/* 延迟所有回调?防有栈溢出 */
	BEV_OPT_UNLOCK_CALLBACKS = (1<<3)/* 开启线程安全后,默认调用你传递回调函数会加锁,设置这个就不锁 */
};

设置属性(读/写)

int bufferevent_enable(struct bufferevent *bufev, short event)
{
	struct bufferevent_private *bufev_private = BEV_UPCAST(bufev);
	short impl_events = event;
	int r = 0;

	bufferevent_incref_and_lock_(bufev);
	if (bufev_private->read_suspended)
		impl_events &= ~EV_READ;
	if (bufev_private->write_suspended)
		impl_events &= ~EV_WRITE;

	bufev->enabled |= event;

	if (impl_events && bufev->be_ops->enable(bufev, impl_events) < 0)
		r = -1;
	if (r)
		event_debug(("%s: cannot enable 0x%hx on %p", __func__, event, bufev));

	bufferevent_decref_and_unlock_(bufev);
	return r;
}

回调函数

void bufferevent_setcb(struct bufferevent *bufev,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg)
{
	BEV_LOCK(bufev);

	bufev->readcb = readcb;
	bufev->writecb = writecb;
	bufev->errorcb = eventcb;

	bufev->cbarg = cbarg;
	BEV_UNLOCK(bufev);
}

size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size)
{
	return (evbuffer_remove(bufev->input, data, size));
}

int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size)
{
	if (evbuffer_add(bufev->output, data, size) == -1)
		return (-1);

	return 0;
}

超时

int bufferevent_set_timeouts(struct bufferevent *bufev,
			 const struct timeval *tv_read,
			 const struct timeval *tv_write)
{
	int r = 0;
	BEV_LOCK(bufev);
	if (tv_read) {
		bufev->timeout_read = *tv_read;
	} else {
		evutil_timerclear(&bufev->timeout_read);
	}
	if (tv_write) {
		bufev->timeout_write = *tv_write;
	} else {
		evutil_timerclear(&bufev->timeout_write);
	}

	if (bufev->be_ops->adj_timeouts)
		r = bufev->be_ops->adj_timeouts(bufev);
	BEV_UNLOCK(bufev);

	return r;
}

event事件回调宏定义

#define BEV_EVENT_READING 0x01
#define BEV_EVENT_WARNING 0x02
#define BEV_EVENT_EOF 0x10
#define BEV_EVENT_ERROR 0x20
#define BEV_EVENT_TIMEOUT 0x40
#define BEV_EVENT_CONNECTED 0x80

触发事件

/**
   Triggers bufferevent data callbacks.

   The function will honor watermarks unless options contain
   BEV_TRIG_IGNORE_WATERMARKS. If the options contain BEV_OPT_DEFER_CALLBACKS,
   the callbacks are deferred.

   @param bufev the bufferevent object
   @param iotype either EV_READ or EV_WRITE or both.
   @param options
 */
void bufferevent_trigger(struct bufferevent *bufev, short iotype, int options)
{
	bufferevent_incref_and_lock_(bufev);
	bufferevent_trigger_nolock_(bufev, iotype, options&BEV_TRIG_ALL_OPTS);
	bufferevent_decref_and_unlock_(bufev);
}
代码实例server
// server.c
#include <event2/event.h>// libevent头文件
#include <event2/listener.h>

#include <iostream>// C++标准库
#include <thread>

#ifndef ____Win32
#include <csignal>// C标准函数库
#include <cstring>
#else
#endif

#include <sys/types.h>// linux系统调用库
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SPORT 5001

void listen_cb(evconnlistener *, evutil_socket_t, sockaddr *, int, void *);
// 回调函数
void read_cb(bufferevent *, void *);
void write_cb(bufferevent *, void *);
void event_cb(bufferevent *, short, void *); /* short用于确定事件的类型 */

int main() {
#ifdef ____Win32
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2) &wsa);
#else
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        return 1;
    }
#endif
    event_base *base = event_base_new();

    sockaddr_in sin;
    memset(&sin, 0, sizoef(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SPORT);

    evconnlistener_new_bind(
        base,
        listen_cb,
        base,// params
        LEV_REUSERABLE | EV_OPT_CLOSE_ON_FREE,
        10,
        (sockaddr*) &sin,
        sizeof(sin)
    );


    event_base_dispatch(base);
    evconnlistener_free(ev);
    event_base_free(base);  
    return 0;
}
void listen_cb(evconnlistener *ev, evutil_socket_t s, sockaddr *sin, int slen, void *arg) {
    // 创建buffer上下文
    event_base *base = (event_base *)arg;
    bufferevent *bev = bufferevent_socket_new(base, s, BEV_OPT_CLOSE_ON_FREE);

    // 设置属性
    bufferevent_enable(bev, EV_READ | EV_WRITE);

    // 设置水位
    bufferevent_setwatermark(
        bev,
        EV_READ,
        5,// 低水位
        10// 高水位
    );
    bufferevent_setwatermark(
        bev,
        EV_WRITE,
        5,// 低水位
        10// 高水位
    );
    // 超时时间的设置
    timeval t1 = {3, 0};
    bufferevent_set_timeouts(bev, &t1, 0);
    // 回调函数
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, base);
}
void read_cb(bufferevent *be, void *arg) {
    char data[1024] = {0};
    // 读取输入缓冲区
    bufferevent_read(be, data, sizeof(data) - 1);/* 留一位放置\0 */

    if (len <= 0) {
        return;
    }
    if (strstr(data, "quit") != NULL) {
        std::cout << "quit \n";
    }
    // 发送数据, 写入到输出缓冲区
    bufferevent_write(be, "ok", 3);
}
void write_cb(bufferevent *be, void *arg) {
    bufferevent_write();
}
void event_cb(bufferevent *be, short events, void *arg) {
    // 读取超时时间发生后,数据停止读取
    if (events & BEV_EVENT_TIMEOUT && events & BEV_EVENT_READING) {
        std::cout << "BEV_EVENT_TIMEOUT" << std::endl;
        bufferevent_free(be);
    }
    else if (events & BEV_EVENT_ERROR ) {
         bufferevent_free(be);
    }
    else {
        std::cout << "OTHERS" << std::endl;
    }
}
代码实例client

连接

/** bufferevent_socket_connect:与server建立连接
* @param bev:缓冲区上下文
* @param sa:地址和端口
* @param socklen:长度
*/
int bufferevent_socket_connect(struct bufferevent *bev, const struct sockaddr *sa, int socklen)
// client.c
#include <event2/event.h>// libevent头文件
#include <event2/listener.h>

#include <iostream>// C++标准库
#include <thread>

#ifndef ____Win32
#include <csignal>// C标准函数库
#include <cstring>
#else
#endif

#include <sys/types.h>// linux系统调用库
#include <sys.stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SPORT 5001

// 回调函数
void read_cb(bufferevent *, void *);
void write_cb(bufferevent *, void *);
void event_cb(bufferevent *, short, void *); /* short用于确定事件的类型 */

int main() {
#ifdef ____Win32
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 2) &wsa);
#else
    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
        return 1;
    }
#endif
    event_base *base = event_base_new();

    sockaddr_in sin;
    memset(&sin, 0, sizoef(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SPORT);

    evconnlistener_new_bind(
        base,
        listen_cb,
        base,// params
        LEV_REUSERABLE | EV_OPT_CLOSE_ON_FREE,
        10,
        (sockaddr*) &sin,
        sizeof(sin)
    );

{
    // 调用客户端代码
    // -1内部创建的socket
    bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);

    sockaddr_in sin;
    memset(&sin, 0, sizoef(sin));
    sin.sin_family = AF_INET;
    sin.sin_port = htons(SPORT);
    evutil_inet_pton(AF_INET, "127.0.0.1", %sin.sin_addr.s_addr);   // 绑定IP地址

    // 设置属性
    bufferevent_enable(bev, EV_READ | EV_WRITE);
    bufferevent_setcb(bev, read_cb, write_cb, event_cb, base);
    int re = bufferevent_socket_connect(bev, (sockaddr*) &sin, sizeof(sin));// 建立连接

    if (re == 0) {
        // do something
    }
}

    event_base_dispatch(base);
    evconnlistener_free(ev);
    event_base_free(base);  
    return 0;
}
void read_cb(bufferevent *be, void *arg) {
    char data[1024] = {0};
    // 读取输入缓冲区
    bufferevent_read(be, data, sizeof(data) - 1);/* 留一位放置\0 */

    if (len <= 0) {
        return;
    }
    if (strstr(data, "quit") != NULL) {
        std::cout << "quit \n";
    }
    // 发送数据, 写入到输出缓冲区
    bufferevent_write(be, "ok", 3);
}
void write_cb(bufferevent *be, void *arg) {
    bufferevent_write();
}
void event_cb(bufferevent *be, short events, void *arg) {
    // 读取超时时间发生后,数据停止读取
    if (events & BEV_EVENT_TIMEOUT && events & BEV_EVENT_READING) {
        std::cout << "BEV_EVENT_TIMEOUT" << std::endl;
        bufferevent_free(be);
    }
    else if (events & BEV_EVENT_ERROR ) {
         bufferevent_free(be);
    }
    else {
        std::cout << "OTHERS" << std::endl;
    }
}

下一篇:

最后想说一句,知识无穷无尽的,但是我们是有穷尽的,我们应该在有穷尽的时候探索那未知的令人神往的地方,触及未曾触及的领域,会发现未曾见过的精彩。

晚安各位~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖怪喜欢风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值