Loading

【Linux系统编程】libevent库bufferevent与evconnlistener

libevent库bufferevent与evconnlistener

介绍

bufferevent是libevent提供的一个自带输入输出缓冲区的事件模型,bufferevent监听socket文件描述符的读写等事件,并自动将内核缓冲区中的数据读写到用户区的缓冲区,并自动触发相应的回调函数。

evconnlistener封装了socket、bind、listen、accept几个函数,当有客户端连接的时候,自动accept并调用回调函数。

相关函数

bufferevent_socket_new()

函数说明
基于一个已有的socket创建一个socket bufferevent。

函数原型

#include <event2/bufferevent.h>
struct bufferevent* bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options);

函数参数

  • struct event_base *base:添加事件的event_base结构体。
  • evutil_socket_t fd:需要监听的socket文件描述符。
  • int options:配置标志
    • BEV_OPT_THREADSAFE:使bufferevent能够在多线程下是安全的
    • BEV_OPT_CLOSE_ON_FREE:释放buffer_event时,自动关闭底层接口(关闭文件描述符)

函数返回值

  • 成功返回bufferevent的结构体指针
  • 失败返回nullptr

bufferevent_socket_connect()

函数说明
连接到对端socket,自动绑定配置bufferevent。

函数原型

int bufferevent_socket_connect(struct bufferevent *bufev, const struct sockaddr *addr, int socklen);

函数参数

  • struct bufferevent *bufev:需要绑定的bufferevent结构体指针。
  • const struct sockaddr *addr:要连接的对端sockaddr信息。
  • int socklen:第二个参数的大小。

函数返回值

  • 成功返回0,失败返回-1

void bufferevent_free(struct bufferevent *bufev)

函数说明
释放bufferevent

函数参数

  • struct bufferevent *bufev:要释放的bufferevent结构体指针

bufferevent_setcb()

函数说明:设置bufferevent的回调函数。

函数原型

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

函数参数

  • struct bufferevent *bufev:要设置回调函数的bufferevent
  • bufferevent_data_cb readcb:读事件回调函数
  • bufferevent_data_cb writecb:写事件回调函数
    • typedef void(* bufferevent_data_cb) (struct bufferevent *bev, void *ctx):bev是调用该函数的bufferevent结构体指针,ctx是用户定义上下文的指针,用来传递参数
  • bufferevent_event_cb eventcb:特殊事件回调函数
    • typedef void(* bufferevent_event_cb) (struct bufferevent *bev, short what, void *ctx):what用来标识发生了什么事件
      • BEV_EVENT_EOF:遇到文件结束指示
      • BEV_EVENT_ERROR:发生错误
      • BEV_EVENT_TIMEOUT:发生超时
      • BEV_EVENT_CONNECTED:请求的过程中连接已经完成
  • void *cbarg:回调函数的参数

size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size)

函数说明:从bufferevent的缓冲区读取数据到data,data大小为size(最大读入size大小),返回实际读入的数据长度。

int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size)

函数说明:将data中的数据写到bufferevent的缓冲区,大小为size,返回实际写入的数据长度。

int bufferevent_enable(struct bufferevent *bufev, short event)

函数说明:使能bufferevent的回调函数。

函数参数

  • short event:要使能的事件,可为EV_READ | EV_WRITE的组合。

int bufferevent_disable(struct bufferevent *bufev, short event)

函数说明:失能相关事件。

evconnlistener_new_bind()

函数说明:创建套接字并连接,同时初始化evconnlistener。

函数原型

struct evconnlistener* evconnlistener_new_bind(
	struct event_base *base,
	evconnlistener_cb cb,
	void *ptr,
	unsigned flags,
	int backlog,
	const struct sockaddr *sa,
	int socklen
)

函数参数

  • evconnlistener_cb cb:监听事件的回调函数
    -typedef void(* evconnlistener_cb) (struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *):参数分别为evconnlistener的结构体指针、客户端连接的文件描述符、客户端地址端口信息、sockaddr长度、自定义的参数指针。
  • void *ptr:回调函数参数
  • unsigned flags:配置标志
    • LEV_OPT_LEAVE_SOCKETS_BLOCKING:文件描述符为阻塞的
    • LEV_OPT_CLOSE_ON_FREE关闭时自动释放
    • LEV_OPT_REUSEABLE:端口复用
    • LEV_OPT_THREADSAFE:分配锁, 线程安全
  • int backlog:全连接队列长度,使用-1自动配置
  • const struct sockaddr *sa:用来监听的ip、端口、协议,与手动bind相同

void evconnlistener_free(struct evconnlistener *lev)

函数说明:释放evconnlistener。

int evconnlistener_enable(struct evconnlistener *lev)

函数说明:使能evconnlistener回调函数。

int evconnlistener_disable(struct evconnlistener *lev)

函数说明:失能evconnlistener回调函数。

实现TCP服务端

#include <iostream>
#include <cstring>
#include <cerrno>
#include <arpa/inet.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
using namespace std;

event_base *eb;

void listener_cb (struct evconnlistener *, evutil_socket_t, struct sockaddr *, int, void *);
void read_cb (struct bufferevent *bev, void *ctx);

int main(int argc, char *argv[]) {
    // 创建监听事件
    eb = event_base_new();
    sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(8886);
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    evconnlistener *listener = 
        evconnlistener_new_bind(eb,
                                listener_cb, nullptr,
                                LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
                                -1, (sockaddr*)&sa, sizeof(sa));
    event_base_dispatch(eb);
    evconnlistener_free(listener);
    event_base_free(eb);
}

void listener_cb (struct evconnlistener *listener,
                  evutil_socket_t cfd,
                  struct sockaddr *csa,
                  int socklen,
                  void *ctx) {
    sockaddr_in *csai = (sockaddr_in*)csa;
    int cport = ntohs(csai->sin_port);
    char cip_chars[16];
    if (nullptr == inet_ntop(AF_INET, &(*csai).sin_addr.s_addr, cip_chars, sizeof(cip_chars))) {
        perror("inet_ntop");
        exit(-1);
    }
    cout << "Accept Client " << cip_chars << ":" << cport << endl;

    bufferevent *bufev = bufferevent_socket_new(eb, cfd, BEV_OPT_CLOSE_ON_FREE);
    if (bufev == nullptr) {
        perror("bufferevent_socket_new");
        exit(-1);
    }
    bufferevent_setcb(bufev, read_cb, nullptr, nullptr, csai);
	bufferevent_enable(bufev, EV_READ);
}

void read_cb (struct bufferevent *bev, void *ctx) {
    char buffer[1024];
    memset(buffer, 0x00, sizeof(buffer));
    sockaddr_in *csai = (sockaddr_in*)ctx;
    int size = bufferevent_read(bev, buffer, sizeof(buffer));
    int cport = ntohs(csai->sin_port);
    char cip_chars[16];
    if (nullptr == inet_ntop(AF_INET, &(*csai).sin_addr.s_addr, cip_chars, sizeof(cip_chars))) {
        perror("inet_ntop");
        exit(-1);
    }
    cout << "Recived Message From Client " << cip_chars << ":" << cport << " ";
    cout << buffer << endl;
}
posted @ 2024-04-14 19:02  杨谖之  阅读(19)  评论(0编辑  收藏  举报