bufferevent的使用讲解
1.今天要讲的是libevent库的bufferevent使用流程并附带测试用例(公司的安全软件把我虚拟器强制重启,虚拟机之前的源码全没了还有配置,真tm炸裂,我下次还是老老实实把源码复制到本地吧,各位引以为鉴)
2.我的配置环境参考这个链接: libevent与qt环境
bufferevent
bufferevent的作用是大家不用思考如何能快速读取和写入,只需要知道bufferevent怎么用就可以,这样能让使用者只需要关注逻辑代码的实现而不需要关注接收和发送也减少了不必要的bug。
使用流程1.创建event_base(相当于调度器)struct event_base *base = event_base_new();
2.bufferevent创建bufferevent_socket_new
3.设置buffevent的事件bufferevent_setcb
4.buffevent使能bufferevent_enable
5.绑定bufferevent_socket_connect
6.启动事件event_base_dispatch
7.释放资源
bufferevent的测试用例源码
这个测试用例的逻辑是连接服务器然后发送数据和接受数据,你们以后就按着这个套路来写客户端就可以,bufferevernt能很好的帮我们管理读和写的。
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <iostream>
void readcb(struct bufferevent *bev, void *ctx) {
char buffer[1024];
int n = bufferevent_read(bev, buffer, sizeof(buffer)-1);
if (n > 0) {
buffer[n] = '\0';
qDebug("Received: %s\n",buffer);
}
}
void eventcb(struct bufferevent *bev, short events, void *ptr) {
if (events & BEV_EVENT_CONNECTED) {
qDebug("Connected to server. Sending data...");
const char *msg = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
bufferevent_write(bev, msg, strlen(msg));
} else if (events & BEV_EVENT_ERROR) {
qDebug(evutil_socket_error_to_string(evutil_socket_geterror(bufferevent_getfd(bev))));
bufferevent_free(bev);
} else if (events & BEV_EVENT_EOF) {
qDebug("Connection closed.");
bufferevent_free(bev);
}
}
int main_loop() {
struct event_base *base = event_base_new();
if (!base) {
qDebug("Failed to create event base.");
return -1;
}
struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
if (!bev) {
qDebug("Failed to create bufferevent.");
event_base_free(base);
return -1;
}
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
sin.sin_port = htons(8080); // Port 8080
// 设置回调:读、写、事件
bufferevent_setcb(bev, readcb, NULL, eventcb, NULL);
bufferevent_enable(bev, EV_READ | EV_WRITE);
if (bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
qDebug("Failed to connect: %s" ,evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
bufferevent_free(bev);
event_base_free(base);
return -1;
}
event_base_dispatch(base);
event_base_free(base);
qDebug("====finish====");
return 0;
}
服务器源码
#include <QCoreApplication>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> // 用于 inet_ntoa
#include <netinet/in.h> // 用于 sockaddr_in
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE] = {0};
// 1. 创建 TCP Socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
// 2. 设置 Socket 地址复用(避免端口占用)
int opt = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {
perror("setsockopt failed");
exit(EXIT_FAILURE);
}
// 3. 绑定 IP 和端口
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡
server_addr.sin_port = htons(PORT);
if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
perror("bind failed");
exit(EXIT_FAILURE);
}
// 4. 开始监听连接
if (listen(server_fd, 5) == -1) { // 最大等待队列为5
perror("listen failed");
exit(EXIT_FAILURE);
}
printf("Server listening on port %d...\n", PORT);
// 5. 接受客户端连接
if ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) == -1) {
perror("accept failed");
//continue; // 继续等待其他连接
}
printf("Client connected from %s:%d\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
ssize_t bytes_read = recv(client_fd, buffer, BUFFER_SIZE - 1, 0);
if (bytes_read == -1) {
perror("recv failed");
} else if (bytes_read == 0) {
printf("Client disconnected.\n");
} else
{
buffer[bytes_read] = '\0';
printf("Received data from client: %s\n", buffer);
// 7. 发送响应
const char *response = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, client!";
if (send(client_fd, response, strlen(response), 0) == -1) {
perror("send failed");
}
}
// 6. 向客户端发送数据
// const char *response = "Hello from server!\n";
// if (send(client_fd, response, strlen(response), 0) == -1) {
// perror("send failed");
// }
getchar();
sleep(1);
// 7. 关闭客户端连接
close(client_fd);
printf("Connection closed.\n");
// 8. 关闭服务器(此代码在无限循环中不会执行,需 Ctrl+C 退出)
close(server_fd);
return 0;
}
bufferevent的测试效果
buffevent是一个客户端然后连接服务器,然后发送数据接受数据
bufferevent各基本函数意义
bufferevent_socket_new
struct bufferevent *bufferevent_socket_new(struct event_base *base,evutil_socket_t fd,enum bufferevent_options options);
1.base,就是base_event(事件管理器)
2.fd,文件描述符就是要负责监听的,你可以填-1(意思就是后面再传文件描述符)
3.options 配置他有这几个配置选项
(1)BEV_OPT_CLOSE_ON_FREE
当 bufferevent 被释放时,关闭底层传输。这将关闭底层套接字、释放底层 bufferevent 等等。
(2)BEV_OPT_THREADSAFE
自动为 bufferevent 分配锁,以便可以安全地从多个线程使用。
(3)BEV_OPT_DEFER_CALLBACKS
当设置此标志时,bufferevent 会推迟其所有回调,如上所述。
(4)BEV_OPT_UNLOCK_CALLBACKS
默认情况下,当 bufferevent 设置为线程安全时,每当调用任何用户提供的回调时,bufferevent 的锁都会被持有
bufferevent_setcb
这个就是负责给buffevent设置回调函数让他在事件发生的时候做对应的处理
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb, bufferevent_data_cb writecb,bufferevent_event_cb eventcb, void *cbarg);
1.bufev这个填你要绑定bufferevent_socket_new
2.readcb 读的回调函数可以填NULL(就是没有)
3.writecb 写的回调函数可以填NULL(就是没有)
4.eventcb 事件的回调函数可以填NULL(就是没有)可以看看测试用例理解,这个事件就是检测是否连接,是否断开等
5.readcb 和writecb 的回调函数格式是typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);由于是libevent是c语言写的所以格式固定你可以理解为c++的虚函数重写了,参考我的测试用例readcb
6.eventcb 的回调函数格式typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short events, void *ctx);参考我的测试用例eventcb
7.cbarg是eventcb,readcb 和writecb 共享的参数,就是给那回调函数传同一个参数
bufferevent_enable,bufferevent_disable和bufferevent_get_enabled
这几个都是配置buffevent能否读和写,当未启用读取或写入时,bufferevent 将不会尝试读取或写入数据。
short bufferevent_get_enabled(struct bufferevent *bufev);来查看 bufferevent 上当前启用了哪些事件。
void bufferevent_enable(struct bufferevent *bufev, short events);//开启读或写
void bufferevent_disable(struct bufferevent *bufev, short events);//关闭读或写
1.bufev你要绑定的bufferevent 就是bufferevent_socket_new创建的那个
2.events有EV_READ(读),EV_WRITE(写),EV_READ|EV_WRITE(读写)
bufferevent_socket_connect
bufferevent_socket_connect(struct bufferevent *bev,struct sockaddr *address, int addrlen);
这个就是专门负责给那个没有绑定socke的bufferevent用的就是bufferevent_socket_new那个参数fd填了-1的,启动一个新连接。
1.bev就是buffevent
2.address就是connect的参数的address就是下面这个就是那个要连接的ip
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(0x7f000001); // 127.0.0.1
sin.sin_port = htons(8080); // Port 8080
3.addrlen是结构体的大小sizeof(sin)
bufferevent_read
bufferevent_read读取数据用
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
1.bufev填你绑定的buffevent
2.data填你要存放数据的空间地址
3.size读取数据的大小
bufferevent_write
bufferevent_write发送数据
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size);
1.bufev填你绑定的buffevent
2.data填你要写的数据的字符串指针
3.size发送数据的大小
bufferevent_free
void bufferevent_free(struct bufferevent *bev);释放资源