Ubuntu20.04 安装 libevent

Ubuntu20.04 安装 libevent


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

运行结果

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值