libevent之bufferevent的使用

53 阅读4分钟

监听连接

代码

// test_buffer_event.cpp

#include <cerrno>
#include <cstdio>
#include <cstring>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>

#ifdef _WIN32
#else
#include <signal.h>
#endif

// 回调函数
void ListenCallback(struct evconnlistener *evc, evutil_socket_t client_socket,
                    struct sockaddr *client_addr, int socklen, void *arg) {
  char ip[16] = {0};
  evutil_inet_ntop(AF_INET, &((sockaddr_in *)client_addr)->sin_addr, ip,
                   sizeof(ip));
  std::cout << "accept new connection from " << ip << std::endl;
}

int main(int argc, char *argv[]) {

  // 端口地址
  int server_port = 8080;

  if (argc > 1) {
    // 从命令行传入指定的端口号
    server_port = atoi(argv[1]);
  }

#ifdef _WIN32
  WSADATA wsa;
  WSAStartup(MAKEWORD(2, 2), &wsa);
#else
  // 使用断开连接socket,会发出此信号,造成程序退出,因此忽略此信号
  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
    return 1;
  }
#endif

  std::cout << "test event server!\n";
  // 创建libevent的上下文,默认是创建base锁
  event_base *base = event_base_new();
  if (base) {
    std::cout << "event_base_new success!" << std::endl;
  }

  // 绑定端口
  sockaddr_in sin;

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(server_port);

  /**
 Allocate a new evconnlistener object to listen for incoming TCP connections
 on a given address.

 @param base The event base to associate the listener with.
 @param cb A callback to be invoked when a new connection arrives. If the
    callback is NULL, the listener will be treated as disabled until the
    callback is set.
 @param ptr A user-supplied pointer to give to the callback.
 @param flags Any number of LEV_OPT_* flags
 @param backlog Passed to the listen() call to determine the length of the
    acceptable connection backlog.  Set to -1 for a reasonable default.
 @param addr The address to listen for connections on.
 @param socklen The length of the address.
*/


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

  auto evc = evconnlistener_new_bind(base, 
                    ListenCallback, // 回调函数
                    base,  // 传给回调函数的参数
                          LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE, 
                          10,  // listen缓存大小
                          (sockaddr *)&sin, sizeof(sin));

  // 事件循环
  // 判断事件是否发生,分发事件到回调函数
  event_base_dispatch(base);

  evconnlistener_free(evc);

  // 释放libevent的上下文
  event_base_free(base);

  return 0;
}

代码中使用的函数主要是evconnlistener_new_bind, 该函数简化了event的操作,实现了绑定监听添加回调

编译

test_event_server:test_buffer_event.cpp
	g++ $^ -o $@ -levent

clean:
	rm -f test_event_server
	rm -f *.o
make test_event_server

运行查看监听

image.png

image.png

处理事件

代码

#include <bits/types/struct_timeval.h>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <event2/bufferevent.h>
#include <event2/event.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <iostream>
#include <netinet/in.h>
#include <sys/socket.h>

#ifdef _WIN32
#else
#include <signal.h>
#endif

void ReadCallback(struct bufferevent *bev, void *ctx) {
  char buf[1024] = {0};
  int len = bufferevent_read(bev, buf, sizeof(buf) - 1);
  std::cout << "recv data: " << buf << std::endl;

  // 插入buffer链表
  bufferevent_write(bev, "OK", 3);
}

void WriteCallback(struct bufferevent *bev, void *ctx) {
  std::cout << "write callback" << std::endl;
}

void EventCallback(struct bufferevent *bev, short what, void *ctx) {
  std::cout << "event callback" << std::endl;
  if (what & BEV_EVENT_TIMEOUT && what & BEV_EVENT_READING) {
    // 读超时
    std::cout << "read timeout" << std::endl;
    // 读取缓冲区中的内容

    // 清理空间,关闭监听
    bufferevent_free(bev);
  } else if (what & BEV_EVENT_TIMEOUT && what & BEV_EVENT_WRITING) {
    // 写超时
    std::cout << "write timeout" << std::endl;
    // 缓冲回滚

    // 清理空间,关闭监听
    bufferevent_free(bev);
  } else if (what & BEV_EVENT_ERROR) {
    // 错误
    std::cout << "error" << std::endl;
    // 清理空间,关闭监听
    bufferevent_free(bev);
  } else if (what & BEV_EVENT_EOF) {
    // 对端关闭
    std::cout << "EOF" << std::endl;

    // 考虑缓冲的处理

    // 清理空间,关闭监听
    bufferevent_free(bev);
  }
}

// 回调函数
void ListenCallback(struct evconnlistener *evc, evutil_socket_t client_socket,
                    struct sockaddr *client_addr, int socklen, void *arg) {
  char ip[16] = {0};
  evutil_inet_ntop(AF_INET, &((sockaddr_in *)client_addr)->sin_addr, ip,
                   sizeof(ip));
  std::cout << "accept new connection from " << ip << std::endl;

  event_base *base = (event_base *)arg;

  // 创建bufferevent对象(read和write)
  // BEV_OPT_CLOSE_ON_FREE表示当bufferevent销毁时,自动关闭底层的socket
  struct bufferevent *bev =
      bufferevent_socket_new(base, client_socket, BEV_OPT_CLOSE_ON_FREE);

  if (!bev) {
    std::cerr << "bufferevent_socket_new failed!" << std::endl;
    return;
  }

  // 添加监控事件, 设置内部权限参数
  bufferevent_enable(bev, EV_READ | EV_WRITE);

  // 设置超时时间 ,第一个为秒,第二个数据为微秒
  timeval t1 = {10, 0};

  // 超时需要设置上,防止死连接的问题出现
  bufferevent_set_timeouts(bev, &t1, &t1);

  // 设置回调函数
  bufferevent_setcb(bev, ReadCallback, WriteCallback, EventCallback, base);
}

int main(int argc, char *argv[]) {

  // 端口地址
  int server_port = 8080;

  if (argc > 1) {
    // 从命令行传入指定的端口号
    server_port = atoi(argv[1]);
  }

#ifdef _WIN32
  WSADATA wsa;
  WSAStartup(MAKEWORD(2, 2), &wsa);
#else
  // 使用断开连接socket,会发出此信号,造成程序退出,因此忽略此信号
  if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
    return 1;
  }
#endif

  std::cout << "test event server!\n";
  // 创建libevent的上下文,默认是创建base锁
  event_base *base = event_base_new();
  if (base) {
    std::cout << "event_base_new success!" << std::endl;
  }

  // 绑定端口
  sockaddr_in sin;

  memset(&sin, 0, sizeof(sin));
  sin.sin_family = AF_INET;
  sin.sin_port = htons(server_port);

  /**
 Allocate a new evconnlistener object to listen for incoming TCP connections
 on a given address.

 @param base The event base to associate the listener with.
 @param cb A callback to be invoked when a new connection arrives. If the
    callback is NULL, the listener will be treated as disabled until the
    callback is set.
 @param ptr A user-supplied pointer to give to the callback.
 @param flags Any number of LEV_OPT_* flags
 @param backlog Passed to the listen() call to determine the length of the
    acceptable connection backlog.  Set to -1 for a reasonable default.
 @param addr The address to listen for connections on.
 @param socklen The length of the address.
*/

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

  auto evc = evconnlistener_new_bind(base,
                                     ListenCallback, // 回调函数
                                     base,           // 传给回调函数的参数
                                     LEV_OPT_REUSEABLE | LEV_OPT_CLOSE_ON_FREE,
                                     10, // listen缓存大小
                                     (sockaddr *)&sin, sizeof(sin));

  // 事件循环
  // 判断事件是否发生,分发事件到回调函数
  event_base_dispatch(base);

  evconnlistener_free(evc);

  // 释放libevent的上下文
  event_base_free(base);

  return 0;
}

测试

image.png

image.png

客户端连接超时后会自动断开