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