使用libevent库实现惊人的高并发C++服务器!

一、引言

在当今互联网时代,高并发服务器的需求越来越迫切。无论是面对日益增长的用户量、海量的请求还是高速的数据传输,构建一个能够优雅应对这些挑战的C++服务器变得至关重要。而幸运的是,有一个强大的利器——libevent库,它可以帮助实现惊人的高并发能力!

libevent库不仅仅是一个网络编程库,它是一个真正的加速器,为C++服务器注入无限的潜力。通过充分发挥libevent库的特性和功能,可以轻松地构建出一个能够处理百万级并发连接的服务器,实现超乎想象的性能和响应速度。

准备好了吗?手把手教你从0开始编写TCP服务器程序,体验开局一块砖,大厦全靠垒。

二、libevent简介

libevent是一个事件通知库,封装了reactor。

libevent API 提供了一种机制,用于在文件描述符上发生特定事件或达到超时后执行回调函数。此外,libevent还支持由于信号或常规超时而导致的回调。

libevent 旨在替换在事件驱动的网络服务器中找到的事件循环。应用程序只需要调用event_dispatch(),然后动态添加或删除事件,而无需更改事件循环。

目前,该控件支持/dev/poll, kqueue(), event ports, POSIX select(), Windows select(), poll()epoll()。内部事件机制完全独立于公开的事件 API,并且 libevent 的简单更新可以提供新功能,而无需重新设计应用程序。因此,Libevent 允许可移植应用程序开发,并提供操作系统上可用的最具可扩展性的事件通知机制。libevent 还可用于多线程应用程序,方法是隔离每个event_base,以便只有单个线程访问它,或者锁定对单个共享event_base的访问。

libevent 还为缓冲网络 IO 提供了一个复杂的框架,支持套接字、筛选器、速率限制、SSL、零副本文件传输和 IOCP。自由度包括对几种有用协议的支持,包括 DNS、HTTP 和最小的 RPC 框架。

libevent编译安装,官网下载安装包并解压。进入解压目录执行:

wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
tar -zxvf libevent-2.1.12-stable.tar.gz 
cd libevent-2.1.12-stable
./configure 
make 
sudo make install

三、libevent库的封装层级

3.1、reactor对象封装struct event_base

reactor对象封装为struct event_base;通过:

(1)event_base_new()构造对象。

(2)event_base_free()销毁对象。

3.2、事件对象struct event

事件对象通过struct event的结构体封装使用。

struct event {
	struct event_callback ev_evcallback;

	/* for managing timeouts */
	union {
		TAILQ_ENTRY(event) ev_next_with_common_timeout;
		int min_heap_idx;
	} ev_timeout_pos;
	evutil_socket_t ev_fd;

	struct event_base *ev_base;

	union {
		/* used for io events */
		struct {
			LIST_ENTRY (event) ev_io_next;
			struct timeval ev_timeout;
		} ev_io;

		/* used by signal events */
		struct {
			LIST_ENTRY (event) ev_signal_next;
			short ev_ncalls;
			/* Allows deletes in callback */
			short *ev_pncalls;
		} ev_signal;
	} ev_;

	short ev_events;
	short ev_res;		/* result passed to event callback */
	struct timeval ev_timeout;
};
变量含义
ev_evcallback回调函数。事件是异步处理的,需要回调函数。
min_heap_idx时间事件的最小堆的索引。
ev_fd定时事件的fd。
ev_base事件对象所属的reactor的对象。
ev_io网络事件关注的事情。
ev_signal信号事件关注的事情。
ev_timeout超时。
ev_timeout_pos和ev_fd定时任务处理的事情。
ev_events具体注册的事件。
ev_具体的信号。

通常,event对象可以自己处理IO。

(1)event_new():构建事件对象、绑定、事件回调。

(2)event_free():销毁事件对象。

bufferevent和evconnlistener对象只需要关注业务逻辑的处理,由libevent内部处理IO操作。

bufferevent是在event对象上面封装的缓冲区。

3.3、struct bufferevent对象

struct bufferevent中的重要成员变量:

变量含义
ev_base事件对象所属的reactor的对象。
be_opsbufferevent的具体操作。控制某个事件的打开、关闭、移除等,其中input是用户态读缓冲区,output是用户态写缓冲区
readcb读事件的回调函数
writecb注意不是写事件回调,而是低水平触发的回调函数。这是涉及到写失败时的处理,内部会处理写事件发送出去。通常不需要设置写回调函数。
errorcb所有错误事件的回调函数。被动关闭连接或其他异常的回调函数。
wm_read读水平线,里面分有高水平和低水平。低水平是指buffer中有多少数据就要触发回调,默认为0,即每次读事件都会触发回调;高水平是指缓冲区中达到多大的数据就要关闭读事件,即buffer数据比较多的时候不再处理读事件。
wm_write写水平线,写只有低水平没有高水平。低水平默认值是0,即用户态缓冲区为空时回调写回调函数。

struct bufferevent_ops中的重要成员变量:

变量含义
input用户态读缓冲区。
output用户态写缓冲区。

(1)bufferevent_socket_new():构建bufferevent对象。
(2)bufferevent_free():销毁bufferevent对象。

3.4、evconnlistener对象

evconnlistener是专门处理listenfd的对象,使我们不需要关注bind、listen、accept的具体操作。

struct evconnlistener_ops {
	int (*enable)(struct evconnlistener *);
	int (*disable)(struct evconnlistener *);
	void (*destroy)(struct evconnlistener *);
	void (*shutdown)(struct evconnlistener *);
	evutil_socket_t (*getfd)(struct evconnlistener *);
	struct event_base *(*getbase)(struct evconnlistener *);
};

struct evconnlistener {
	const struct evconnlistener_ops *ops;
	void *lock;
	evconnlistener_cb cb;
	evconnlistener_errorcb errorcb;
	void *user_data;
	unsigned flags;
	short refcnt;
	int accept4_flags;
	unsigned enabled : 1;
};

(1)evconnlistener_new():构建evconnlistener对象、绑定、事件回调。

(2)evconnlistener_free():销毁evconnlistener对象。

(3)evconnlistener_bind_new():创建listenfd、bind、listen、注册读事件。

3.5、事件循环

(1)事件循环:event_base_dispatch(),event_base_loop()。

(2)事件循环退出:event_base_loopexit(),event_base_break()。

3.6、事件处理

设置事件相对应的回调。

(1)如果是使用event对象,在event_new()会设置相对应的回调。

(2)如果IO由libevent处理,那么使用bufferevent_setcb()来设置回调。

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

四、完整示例代码

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

#include <sys/socket.h>

#include <functional>
#include <cstring>
#include <stdlib.h>


#define SOCKET_LISTEN_PORT  9703
#define SOCKET_BACKLOG_NUM  128

class asyn_event
{
private:
    
    /* data */
    struct event_base *base;
    struct evconnlistener *listener;
public:
    asyn_event(/* args */);
    ~asyn_event();
    static void accept_cb(struct evconnlistener *listen,evutil_socket_t fd,struct sockaddr *sock,int socklen,void *arg);
    static void socket_event_callback(struct bufferevent *bev, short events, void *arg);
    static void socket_read_callback(struct bufferevent *bev, void *arg);
    void loop_run();
};


asyn_event::asyn_event(/* args */)
{
    base=event_base_new();
    struct sockaddr_in server={0};
    server.sin_family=AF_INET;
    server.sin_addr.s_addr=htonl(INADDR_ANY);
    server.sin_port=htons(SOCKET_LISTEN_PORT);

    listener=evconnlistener_new_bind(
        base,
        &asyn_event::accept_cb,
        base,
        LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE,
        SOCKET_BACKLOG_NUM,
        (struct sockaddr*)&server,
        sizeof(server)
    );
}

asyn_event::~asyn_event()
{
    // 销毁evconnlistener对象
	evconnlistener_free(listener);

	// 销毁事件对象
	event_base_free(base);
}

void asyn_event::accept_cb(struct evconnlistener *listen,evutil_socket_t fd,struct sockaddr *sock,int socklen,void *arg)
{
	struct event_base *base = (struct event_base *)arg;

	// 连接的建立---接收连接
	char ip[32] = { 0 };
	evutil_inet_ntop(AF_INET, sock, ip, sizeof(ip) - 1);
	printf("accept a client fd:%d, ip:%s\n", fd, ip);

	struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	
	// 注册读事件
	bufferevent_setcb(bev, socket_read_callback, NULL, socket_event_callback, NULL);//写事件回调一般为NULL
	bufferevent_enable(bev, EV_READ | EV_PERSIST);


}
void asyn_event::loop_run()
{
    // 事件循环
	event_base_dispatch(base);
}

// 处理连接断开
void asyn_event::socket_event_callback(struct bufferevent *bev, short events, void *arg)
{
	if (events &BEV_EVENT_EOF)//read=0
	{
		printf("connection closed\n");
	}
	else if (events & BEV_EVENT_ERROR)//strerro(errno)
	{
		printf("some other error\n");
	}
	else if (events &BEV_EVENT_TIMEOUT)
		printf("time out\n");

	bufferevent_free(bev);// close(fd)
}

// 读回调
void asyn_event::socket_read_callback(struct bufferevent *bev, void *arg)
{
    struct evbuffer *input = bufferevent_get_input(bev);
    struct evbuffer *output = bufferevent_get_output(bev);

    // 从输入缓冲区读取数据
    char buf[1024];
    size_t len;
    while ((len = evbuffer_remove(input, buf, sizeof(buf))) > 0) {
        //printf("Received: %.*s", (int)len, buf);
        evbuffer_add(output, buf, len);
    }
    
	//bufferevent_write(bev, reply, strlen(reply));
}

int main()
{
    asyn_event ev;
    ev.loop_run();
    return 0;
}

编译时要指定事件库,添加 -levent 参数。

gcc -o ev ev.c  -levent

运行时出现libevent-2.1.so.7。

error while loading shared libraries: libevent-2.1.so.7: cannot open shared object file: No such file or directory

产生原因:libevent动态库在默认安装时,存放的路径在/usr/local/lib下,不在系统的默认查找路径内。

解决办法有两个:

(1)将该路径放在系统查找路径内。这种方法仅永久有效。

sudo echo "/usr/local/lib" >> /etc/ld.so.conf
sudo ldconfig

(2)添加环境变量的方法,添加 export LD_LIBRARY_PATH=XXX。这种方法仅当前有效。

export LD_LIBRARY_PATH=/usr/local/lib/

五、总结

(1)有了libevent可以不使用IO函数。因为如果使用IO函数,既需要知道这些IO函数里面的系统调用返回值的含义。

(2)有了libevent就可以不清楚数据拷贝原理。

(3)有了libevent就可以不清楚网络原理以及网络编程流程。

(4)有了libevent只需要知道事件处理,IO操作完全交由libevent处理。

至此,我们实现了使用libevent库开发高并发的服务器程序,但是,这个服务器程序有些局限性,我们还要继续改善、优化。在改进之前,需要开发一个后台日志模块,这是服务器程序必须的,所有,下一个章节将开发一个高效的后台日志模块。

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: libevent是一个开源的C语言网络编程,主要用于处理高并发网络连接。它提供了对事件驱动的支持,使得开发者可以方便地编写高效的并发网络应用程序。 libevent的核心是事件循环机制。在传统的网络编程中,通常需要使用多线程或多进程来处理并发连接,而使用libevent可以通过一个事件循环来处理多个连接。在事件循环中,可以注册多个事件,并定义回调函数来处理事件的触发。当有事件发生时,libevent会调用相应的回调函数来处理事件的处理逻辑。这样可以大大简化并发编程的复杂性,并提高程序的性能。 libevent的事件模型基于操作系统提供的I/O多路复用机制,如select、poll和epoll等。它可以在不同的操作系统平台上运行,并提供一致的接口和高效的事件处理机制。借助这些机制,libevent可以同时处理大量的并发连接,并保持低延迟和高吞吐量。 除了处理网络连接,libevent还提供了其他常用的功能,如定时器和信号处理等。它允许开发者在事件循环中注册定时器事件,可以用于定时任务的调度。同时,libevent还可以处理来自操作系统的信号,并提供了对信号的处理接口,以便开发者能够处理各种系统事件。 总之,libevent是一个功能强大、简单易用的高并发网络编程,适用于开发各种类型的网络应用。无论是开发服务器、代理、聊天程序还是实时应用,libevent都能帮助开发者快速编写高性能的并发网络程序。 ### 回答2: libevent是一个开源的C/C++网络,用于高性能的事件驱动编程。它提供了一个轻量级、可移植的框架,用于开发高并发的网络应用程序。 它的设计目标是提供一个高效的事件处理器,可以处理成千上万个并发连接,并且支持多线程并发处理。libevent基于事件驱动模型,通过异步I/O和回调函数来实现高并发处理网络请求。 libevent提供了一系列的函数来注册和监听各种网络事件,包括读、写、超时和信号等等。当一个事件发生时,libevent会调用相应的回调函数来处理事件。通过这种方式,我们可以非常方便地处理并发连接,并实现高性能的网络编程。 libevent的优点主要包括: 1. 高性能:libevent使用异步I/O和事件驱动模型,能够处理成千上万个并发连接,具有很高的处理能力。 2. 可移植性:libevent提供了统一的接口,可以在多种操作系统上运行,包括Linux、Windows、Mac等。 3. 易用性:libevent简单易用,只需注册感兴趣的事件和相应的回调函数,就可以实现高效的网络编程。 4. 多线程支持:libevent支持多线程并发处理,可以充分利用多核CPU的性能优势。 总之,libevent是一款非常适合高并发网络编程的开源,它可以帮助我们实现高性能的服务器程序,提升系统的并发处理能力。无论是开发网络服务器还是网络应用程序,libevent都是一个不错的选择。 ### 回答3: libevent 是一个用于高并发网络编程的 C/C++ 。它提供了一个跨平台的异步事件驱动的网络编程框架,能够实现高效地处理大量并发连接的需求。 libevent 的主要特点包括: 1. 异步事件驱动:libevent 使用事件驱动模型,主要利用非阻塞 I/O 和事件回调机制,能够高效地处理大量并发事件。 2. 跨平台支持:libevent 提供了跨不同操作系统的支持,包括 Windows、Linux、Unix 等,并且提供了统一的 API 接口,方便开发者进行跨平台开发。 3. 支持多种网络协议libevent 支持 TCP、UDP、HTTP 等多种网络协议,为开发者提供了丰富的网络编程能力。 4. 高性能:libevent 的设计目标之一是高性能,它通过使用多路复用技术,将系统资源高效地利用起来,能够同时处理大量并发连接,并且保持低延迟。 5. 灵活易用:libevent 提供了简洁的 API,使用起来非常方便,可以快速实现高并发网络编程的需求。 总之,libevent 是一个强大而灵活的 C/C++ ,适用于各种需要处理高并发连接的网络应用程序。无论是开发高性能服务器、代理、负载均衡器还是其他类似应用,libevent 都是一个值得推荐的选择。它的高效性能、跨平台支持和简洁易用的 API 接口使得开发者能够快速构建稳定可靠的高并发网络应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Lion Long

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

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

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

打赏作者

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

抵扣说明:

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

余额充值