首页 > 系统相关 >Ubuntu20.04 安装 libevent

Ubuntu20.04 安装 libevent

时间:2024-08-22 09:26:54浏览次数:14  
标签:Ubuntu20.04 struct cb void bufferevent base libevent 安装 event

https://blog.csdn.net/qq_62381297/article/details/136151148

Ubuntu20.04 安装 libevent

文章目录
libevent 源码下载
libevent 安装
libevent 使用
echo 服务器:
echo 客户端:
运行结果
libevent 源码下载
https://libevent.org


libevent 安装
$ tar -zxvf libevent-2.1.12-stable.tar.gz
$ cd libevent-2.1.12-stable
$ ./configure --prefix=/opt/libevent

在配置时 ./configure 出现错误:

 

configure: error: openssl is a must but can not be found. You should add the directory containing ‘openssl.pc’ to the ‘PKG_CONFIG_PATH’ environment variable, or set ‘CFLAGS’ and ‘LDFLAGS’ directly for openssl, or use ‘–disable-openssl’ to disable support for openssl encryption

Ubuntu 里安装 openssl 开发工具解决上述问题

$ sudo apt install libssl-dev
$ openssl version # 查看 openssl 版本

继续安装 libevent

$ ./configure --prefix=/opt/libevent
$ make
$ sudo make install

成功安装至指定目录

 

libevent 使用
首先环境配置,引入 libevent 的头文件和库文件
我使用的是 cmake,CMakeLists.txt 如下:

cmake_minimum_required(VERSION 3.26)
project(libevent_server)

set(CMAKE_CXX_STANDARD 11)

add_executable(libevent_server main.cpp)

set(LIBEVENT_HOME "/opt/libevent")
target_include_directories(libevent_server PRIVATE ${LIBEVENT_HOME}/include)
target_link_directories(libevent_server PRIVATE ${LIBEVENT_HOME}/lib)
target_link_libraries(libevent_server -levent)

实现一个简单的 echo 服务器,示例代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>

#include <event.h>

#include <sys/socket.h>
#include <arpa/inet.h>
#include <csignal>

void client_cb(int fd,short events,void *arg);
void accept_cb(int fd,short events,void *arg);
void exit_cb(int,short,void *arg);

int main() {
int lfd = -1;
struct sockaddr_in addr{0};

// 创建监听套接字
lfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (lfd == -1) {
perror("socket()");
}

addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(10000);

if (bind(lfd, (struct sockaddr*)(&addr), sizeof(addr)) == -1) {
perror("bind()");
}
if (listen(lfd, 10) == -1) {
perror("listen()");
}

struct event_base *base = event_base_new();

// 监听套接字
struct event *ev_listen = event_new(base, lfd, EV_READ | EV_PERSIST, accept_cb, base);
event_add(ev_listen, nullptr);
// 信号监听
struct event *ev_signal = evsignal_new(base, SIGINT, exit_cb, base);
event_add(ev_signal, nullptr);

// 开启循环
std::cout << "listen..." << std::endl;
event_base_dispatch(base);

// 退出循环,释放资源
event_free(ev_listen);
event_free(ev_signal);
event_base_free(base);

std::cout << "\ndone..." << std::endl;
return 0;
}

void client_cb(int fd,short events,void *arg) {
auto *ev = static_cast<struct event*>(arg);

char buf[1024]{0};
ssize_t byte_read{0};

// 回声
byte_read = recv(fd, buf, sizeof(buf), 0);
if (byte_read == 0) {
std::cout << "client offline" << std::endl;
event_del(ev);
event_free(ev);
} else if (byte_read == -1) {
if (errno != EINTR) {
perror("recv()");
event_del(ev);
event_free(ev);
}
} else {
std::cout << "client " << fd << " : " << buf;
send(fd, buf, byte_read, 0);
std::memset(buf, 0, byte_read);
}
}

void accept_cb(int fd,short events,void *arg) {
auto *base = static_cast<struct event_base*>(arg);
int conn = -1;

// 接收客户端连接请求
conn = accept(fd, nullptr, nullptr);
if (conn == -1) {
perror("accept()");
}
std::cout << "new client" << std::endl;

// 添加客户端监听
struct event *ev_client = event_new(base, conn, EV_READ | EV_PERSIST, client_cb, nullptr);
event_assign(ev_client, base, conn, EV_READ | EV_PERSIST, client_cb, ev_client);
event_add(ev_client, nullptr);
}

void exit_cb(int,short,void *arg) {
auto *base = static_cast<struct event_base*>(arg);

// 退出 event_base_dispatch()
event_base_loopbreak(base);
}
使用 nc 命令向服务器发送字符串,运行结果如下:

 

学过 IO 多路复用的话上述代码阅读起来应该不困难

下面的 echo 服务器和客户端使用了更多的 libevent API 进行实现

echo 服务器:
#include <iostream>
#include <cstdio>
#include <cstring>

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

#include <sys/socket.h>
#include <arpa/inet.h>
#include <csignal>

static void exit_cb(int,short,void *arg);

static void conn_read_cb(struct bufferevent *bev, void *user_data);
static void conn_write_cb(struct bufferevent *bev, void *user_data);
static void conn_event_cb(struct bufferevent *bev, short events, void *user_data);
static void listener_cb(struct evconnlistener *listener, int cfd, struct sockaddr *sa, int socklen, void *user_data);
static void listener_error_cb(struct evconnlistener *listener, void *user_data);

int main() {
struct event_base *base;
struct evconnlistener *listener;
struct event *ev_signal;

struct sockaddr_in addr{0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(10000);

base = event_base_new();
if (!base) {
std::puts("Couldn't open event base");
return 1;
}

// 连接监听
listener = evconnlistener_new_bind(
base,
listener_cb, nullptr,
LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
-1,
(struct sockaddr*)&addr,sizeof(addr));
if (!listener) {
perror("Couldn't create listener");
return 1;
}
evconnlistener_set_error_cb(listener, listener_error_cb);

// 信号监听 ctrl c
ev_signal = evsignal_new(base, SIGINT, exit_cb, base);
event_add(ev_signal, nullptr);

// 开启循环
std::cout << "listen..." << std::endl;
//event_base_dispatch(base);
event_base_loop(base, 0x0);

// 退出循环,释放资源
evconnlistener_free(listener);
event_free(ev_signal);
event_base_free(base);

std::cout << "\ndone..." << std::endl;
return 0;
}

void exit_cb(int,short,void *arg) {
auto *base = static_cast<struct event_base*>(arg);

// 退出 event_base_dispatch()
event_base_loopbreak(base);
}

void listener_cb(struct evconnlistener *listener, int cfd, struct sockaddr *sa, int socklen, void *user_data) {
struct event_base *base = evconnlistener_get_base(listener);

std::cout << "new client" << std::endl;
struct bufferevent *bev = bufferevent_socket_new(base,
cfd, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
std::fprintf(stderr, "Error constructing bufferevent!");
event_base_loopbreak(base);
return;
}
bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, nullptr);
// 默认情况下,新创建的 bufferevent 的写入是启用的,但是读取没有启用
// 启用读
bufferevent_enable(bev, EV_READ);
// 关闭写
//bufferevent_disable(bev, EV_WRITE);
//bufferevent_enable(bev, EV_WRITE);// 添加这行后面会直接触发一次 conn_write_cb
}

void listener_error_cb(struct evconnlistener *listener, void *user_data)
{
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, nullptr);
}

void conn_read_cb(struct bufferevent *bev, void *user_data) {
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);

char buf[1024]{0};
int bytes_read;

// 读
bytes_read = evbuffer_remove(input, buf, sizeof(buf));
std::cout << "-------------------" << std::endl;
std::cout << "conn_read_cb " << bufferevent_getfd(bev) << std::endl;
std::cout << "client : " << buf;
// 写
evbuffer_add(output, buf, bytes_read);
}

void conn_write_cb(struct bufferevent *bev, void *user_data) {

std::cout << "conn_write_cb " << bufferevent_getfd(bev) << std::endl;
std::cout << "-------------------" << std::endl;
}

void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
if (events & BEV_EVENT_EOF) {
std::printf("Client fd %d connection closed.\n", bufferevent_getfd(bev));
} else if (events & BEV_EVENT_ERROR) {
std::printf("Got an error on the connection fd %d: %s\n",
bufferevent_getfd(bev), strerror(errno));
}
/* None of the other events can happen here, since we haven't enabled timeouts */
bufferevent_free(bev);
}


echo 客户端:

#include <iostream>
#include <cstdio>
#include <cstring>

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

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>


static void stdin_cb(int fd,short events,void *arg);
static void conn_read_cb(struct bufferevent *bev, void *user_data);
static void conn_write_cb(struct bufferevent *bev, void *user_data);
static void conn_event_cb(struct bufferevent *bev, short events, void *user_data);

struct client_info {
struct event_base *base;
struct bufferevent *bev;
};

int main() {

struct event_base *base = event_base_new();

struct sockaddr_in addr{0};
addr.sin_family = AF_INET;
addr.sin_port = htons(10000);
inet_aton("127.0.0.1", &addr.sin_addr);

struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
// 连接服务器
bufferevent_socket_connect(bev, (struct sockaddr*)(&addr), sizeof(addr));
bufferevent_setcb(bev, conn_read_cb, conn_write_cb, conn_event_cb, nullptr);
bufferevent_enable(bev, EV_READ);

struct client_info *info = new client_info;
info->base = base;
info->bev = bev;
struct event *ev_stdin = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, stdin_cb, info);
event_add(ev_stdin, nullptr);

std::cout << "client loop..." << std::endl;
event_base_dispatch(base);

event_free(ev_stdin);
bufferevent_free(bev);
event_base_free(base);

std::cout << "client over..." << std::endl;
return 0;
}

void stdin_cb(int fd, short events, void *arg) {
char buf[1024]{0};
struct client_info *info = static_cast<client_info *>(arg);
struct evbuffer *output = bufferevent_get_output(info->bev);

fgets(buf, sizeof(buf), stdin);
if (std::strcmp(buf, "exit\n") == 0) {
event_base_loopbreak(info->base);
return;
}

bufferevent_write(info->bev, buf, strlen(buf));
std::cout << "-------------------" << std::endl;
std::cout << "stdin_cb output buffer len " << evbuffer_get_length(output) << std::endl;
}

void conn_read_cb(struct bufferevent *bev, void *user_data) {
char buf[1024]{0};
size_t bytes_read;

bytes_read = bufferevent_read(bev, buf, sizeof(buf));

std::cout << "conn_read_cb " << std::endl;
std::cout << "receive from server : " << buf;
std::cout << "-------------------" << std::endl;
}

void conn_write_cb(struct bufferevent *bev, void *user_data) {
struct evbuffer *output = bufferevent_get_output(bev);

std::cout << "conn_write_cb output buffer len " << evbuffer_get_length(output) << std::endl;
// evbuffer_get_length(output) = 0,回调前已经发送给对端了
}

void conn_event_cb(struct bufferevent *bev, short events, void *user_data) {
struct event_base *base = bufferevent_get_base(bev);

// 成功连接
if (events & BEV_EVENT_CONNECTED) {
std::cout << "Connect to server successfully." << std::endl;
}
// 对端关闭
else if (events & BEV_EVENT_EOF) {
std::cout << "Server closed." << std::endl;
event_base_loopexit(base, nullptr);
}
// 错误
else if (events & BEV_EVENT_ERROR) {
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the bufferevent. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, nullptr);

————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/qq_62381297/article/details/136151148

标签:Ubuntu20.04,struct,cb,void,bufferevent,base,libevent,安装,event
From: https://www.cnblogs.com/shuimuqingyang/p/18373047

相关文章

  • Windows-安装MySQL数据库
    mysql安装:11.将zip包解压到指定目录,例如:D:\tools\mysql-8.0.18-winx64注:不放到C硬盘,win10系统权限不够会报错,麻烦2.复制改变my.ini文件放在目录:D:\tools\mysql-8.0.18-winx64\bin,并添加相关配置(给的文件中配置内容已经都有了)注1:my.ini配置文件里面非注释语句不要有“”双引......
  • Mac安装Typora
    文章目录介绍软件功能下载安装1.下载完成后打开downloads双击进行安装2.将软件拖到应用程序中3.在程序坞中搜索打开4.提示安全问题就打开通用,安全隐私与设置5.打开成功小结介绍Typoraformac是一款简洁的轻量级的markdown编辑器、写作软件。它去除了预览窗口,模......
  • 银河麒麟系统V10(arm版)安装Mysql-5.7.29说明
    #银河麒麟系统适配#随着2024年微软全球蓝屏丑闻的出现,系统安全越来越重要。目前很多企业开始尝试国产化操作系统上,本文介绍如何在国产化银河麒麟系统V10(arm)版上安装mysql。本资源使用的是arm版本的Mysql-5.7.29离线安装包,能够在arm版国产化银河麒麟系统上进行安装,并配置防火......
  • 第二章 redis环境安装与配置
    redis环境安装redis的官方只提供了linux版本的redis,window系统的redis是微软团队根据官方的linux版本高仿的。官方原版:https://redis.io/中文官网:http://www.redis.cn1、下载和安装下载地址:https://github.com/tporadowski/redis/releases使用以下命令启动redis......
  • 本地快速安装运行史上最强开源LLaMa3大模型
    https://liaoxuefeng.com/blogs/all/2024-05-06-llama3/ 史上最强开源AI大模型——Meta的LLaMa3一经发布,各项指标全面逼近GPT-4。它提供了8B和70B两个版本,8B版本最低仅需4G显存即可运行,可以说是迄今为止能在本地运行的最强LLM。虽然LLaMa3对中文支持不算好,但HuggingFace上很快......
  • qt静态编译 全自动编译qt静态库 qt5 windows安装qt (2024.2.23)
    全自动编译qt5静态库(2024.2.23)本教程是从无到有配置qt.io和vcpkg实现全自动编译qt5的静态库,使得您可以静态编译qt项目0.安装VisualStudio2022这个我就不多解释了,直接去官网下载社区版本,勾选使用C++的桌面开发安装好就行1.安装qt.io的开发环境1.1下载在线安装包并且配......
  • Office 2010 详细安装教程
    Office2010引入了新的文件格式,改善了用户界面,并提供了64位版本,以提高性能和支持更大的数据集。此外,Office2010还包括了在线协作功能,允许用户从不同地点和设备上共同工作,以及新的SmartArt图形和背景移除工具等功能,以提高办公效率和文档的专业外观。 安装包:百度网盘请输入......
  • 【pip镜像设置】pip使用清华镜像源安装
    文章目录问题:问题描述原因分析:PyPI(PythonPackageIndex)PypI镜像列表解决方案:问题:大家经常会使用pip进行python的第三方库安装,但是,有时会出现ERROR:CouldnotfindaversionthatsatisfiestherequirementPyQt6(fromversions:none)ERROR:Nomatching......
  • Broadcom BCM43142驱动安装
    broadcom-sta通用驱动下载地址: http://mirrors.ustc.edu.cn/kali/pool/non-free/b/broadcom-sta/安装内核头文件及编译工具和dkmssudoapt-getinstalllinux-headers-genericbuild-essentialdkmssudoapt-getupdate安装内核源码sudoapt-getinstalllinux-sourcesudo......
  • MySQL8.0安装教程
    一、下载二、解压、初始化数据库以windows管理员打开cmd进入MySQL解压后的bin目录执行mysqld--initialize--user=mysql--consoleMySQL初始化完成三、安装mysqld服务mysqld--install四、启动mysql服务netstartmysql五、修改root用户密码mysql-uroot-p输入......