libevent库tcp基本结构与使用流程
一、基本数据类型(结构)
1)evutil_socket_t:网络文件描述符
libevent中定义为:#define evutil_socket_t int,与linux下定义相同,操作应该也相同。
2)struct event_base:事件基本结构
该结构用于保存libevent事件分发循环的的信息和状态,是libevent的核心。每个应用都应有一个。它保持对挂起和活跃事件的监听,并把处于就绪态(活跃)的事件告知(通知)应用。
常用接口:
接口 | 描述 |
---|---|
struct event_base *event_base_new(void) | 获取event_base实例 |
int event_base_dispatch(struct event_base *) | 为event_base分配循环,直到无挂起或就绪事件,或调用接口终止时停止 |
int event_base_loopbreak(struct event_base *) | 立刻退出活跃的event_base循环 |
void event_base_free(struct event_base *) | 释放event_base占用空间 |
3)struct bufferevent:缓冲区事件
非透明结构,用于控制缓冲区读写。
相关宏定义标志:
名称 | 含义 |
---|---|
BEV_EVENT_READING | 读取过程中发生错误 |
BEV_EVENT_WRITING | 写入过程中发生错误 |
BEV_EVENT_EOF | 达到文件尾 |
BEV_EVENT_ERROR | 发生不可恢复错误 |
BEV_EVENT_TIMEOUT | 用户指定定时器过期 |
BEV_EVENT_CONNECTED | 连接操作完成 |
读取回调句柄类型:
typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void *ctx);
事件、错误回调句柄类型:
typedef void (*bufferevent_event_cb)(struct bufferevent *bev, short what, void *ctx);
常用接口:
定义 | 描述 |
---|---|
int bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) | 将bufferevent分配给指定的event |
struct event_base *bufferevent_get_base(struct bufferevent *bev) | 获取bufferevent对应的event_base |
void bufferevent_free(struct bufferevent *bufev) | 释放bufferevent占用的内存 |
void bufferevent_setcb(struct bufferevent *bufev, bufferevent_data_cb readcb,bufferevent_data_cb writecb, bufferevent_event_cb eventcb, void *cbarg) | 设置bufferevent的事件回调句柄 |
int bufferevent_setfd(struct bufferevent *bufev, evutil_socket_t fd) | 设置bufferevent的socket描述符 |
int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size) | 向对应socket写入数据,数据被挂起再输出缓冲区,当缓冲区变为就绪态是自动写入 |
size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size) | 读取bufferevent对应socket中的数据 |
int bufferevent_enable(struct bufferevent *bufev, short event) | 设置bufferevent可用事件:EV_READ |
int bufferevent_socket_connect(struct bufferevent *, const struct sockaddr *, int) | 连接addr中指定的ip和端口 |
4) struct evconnlistener:socket监听
非透明结构,用于管理服务端socket的监听:新的连接请求和断开。
新连接请求回调类型:
typedef void (*evconnlistener_cb)(struct evconnlistener *, evutil_socket_t, struct sockaddr *, int socklen, void *);
错误回调类型:
typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
常用宏定义:
宏 | 含义 |
---|---|
LEV_OPT_CLOSE_ON_FREE | 释放监听的时候,关闭监听套接字 |
LEV_OPT_REUSEABLE | 设置套接字可复用 |
常用接口
接口 | 描述 |
---|---|
struct evconnlistener *evconnlistener_new(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, evutil_socket_t fd) | 创建监听实例:基于已存在的监听socket |
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog, const struct sockaddr *sa, int socklen) | 创建新的监听实例,并绑定到指定IP和端口 |
void evconnlistener_free(struct evconnlistener *lev) | 关闭并释放监听占用内存 |
int evconnlistener_enable(struct evconnlistener *lev) | 打开被关闭的监听 |
int evconnlistener_disable(struct evconnlistener *lev) | 关闭监听 |
void evconnlistener_set_cb(struct evconnlistener *lev, evconnlistener_cb cb, void *arg) | 设置监听回调 |
void evconnlistener_set_error_cb(struct evconnlistener *lev, evconnlistener_errorcb errorcb) | 设置错误回调 |
二、大致使用流程
1)服务端
创建基础事件 -->创建基于基础事件的监听(同时注册监听新连接回调)–>注册监听错误处理回调 -->为基础事件分配循环 --> … --> 终止事件循环 -->释放事件空间
2)客户端
创建基础事件 -->初始化socket(设置非阻塞) -->初始化服务端地址信息 --> 创建基于前置资源的socket bufferevent事件 --> 注册bufferevent事件回调(I/O,ERR) -->为基础事件分配循环 --> …IO业务 -->
停止基础业务循环 -->释放基础业务空间
三、实例代码
1)服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#define PORT 8000
#define BUF_SIZE 1024
const char *reply = "Hello client, I've received your data.";
//socket读回调
void read_cb(struct bufferevent *bev, void *ctx)
{
//读取数据
char buf[BUF_SIZE] = {0x00};
size_t len = 0;
printf("Receive data:");
len = bufferevent_read(bev, buf, BUF_SIZE);
while(0 < len)
{
printf("%s\n", buf);
if(0 == strcmp(buf, "stop"))
{
printf("Exit.\n");
exit(0);
}
memset(buf, 0x00, 1024);
len = bufferevent_read(bev, buf, BUF_SIZE);
}
printf("\n");
//回复数据
bufferevent_write(bev, reply, strlen(reply));
}
//事件回调
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
//连接断开
if(BEV_EVENT_EOF & events)
{
printf("One connection has closed.\n");
//释放事件
bufferevent_free(bev);
}
if(BEV_ERROR & events)
{
printf("Error occured.\n");
bufferevent_free(bev);
}
}
//监听新连接回调
void accept_new_connection_cb(struct evconnlistener *listener, evutil_socket_t sock, struct sockaddr *addr, int socklen, void *ctx)
{
//获取管理监听事件的基础事件
struct event_base *base = evconnlistener_get_base(listener);
//将新链接加入基础事件:设置释放事件时关闭连接
struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
//设置新连接读取、事件回调
bufferevent_setcb(bev, read_cb, NULL, event_cb, NULL);
//开启新连接读取
bufferevent_enable(bev, EV_READ| EV_WRITE);
}
//监听异常回调
void error_cb(struct evconnlistener *listener, void *ctx)
{
int err = EVUTIL_SOCKET_ERROR();
struct event_base *base = evconnlistener_get_base(listener);
printf("Error occured on listener: %d: %s\n", err, evutil_socket_error_to_string(err));
//退出服务端基础事件循环:终止服务、
event_base_loopexit(base, NULL);
}
int main(int argc, char **argv)
{
struct sockaddr_in addr;
struct event_base *base = NULL;
struct evconnlistener *listener = NULL;
//初始化服务端地址信息
memset(&addr, 0x00, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(PORT);
//1.创建基础事件实例
base = event_base_new();
if(NULL == base)
{
printf("Create instance of event_base failed.\n");
return -1;
}
//2.创建基于该基础事件与地址信息的监听
listener = evconnlistener_new_bind(base, accept_new_connection_cb, NULL, LEV_OPT_CLOSE_ON_FREE| LEV_OPT_REUSEABLE, -1, (struct sockaddr*)(&addr), sizeof(addr));
if(NULL == listener)
{
printf("Create instance of evconnlistener failed.\n");
return -1;
}
//设置监听错误回调
evconnlistener_set_error_cb(listener, error_cb);
//3.开启服务
printf("Server starts.\n");
event_base_dispatch(base);
return 0;
}
2)客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#define BUF_SIZE 1024
#define SERVER_IP "192.168.56.11"
#define SERVER_PORT 8000
const char *call = "Hello, my remote server.";
int cnt = 5;
//读取回调
void read_cb(struct bufferevent *bev, void *ctx)
{
//读取数据
char buf[BUF_SIZE] = {0x00};
size_t len = 0;
printf("Receive data:\n");
len = bufferevent_read(bev, buf, BUF_SIZE);
while(0 < len)
{
printf("%s", buf);
memset(buf, 0x00, 1024);
len = bufferevent_read(bev, buf, BUF_SIZE);
}
printf("\n");
}
//写回调
void write_cb(struct bufferevent *bev, void *ctx)
{
if(cnt--)
{
printf("Send data to server.\n");
if(-1 == bufferevent_write(bev, call, strlen(call)))
{
printf("Send data to server failed.\n");
}
sleep(5);
}
else
{
printf("send stop");
bufferevent_write(bev, "stop", strlen("stop"));
}
}
//事件回调
void event_cb(struct bufferevent *bev, short events, void *ctx)
{
if(events & BEV_EVENT_ERROR)
{
perror("Error from bufferevent.");
}
if(events & (BEV_EVENT_EOF | BEV_EVENT_ERROR))
{
bufferevent_free(bev);
}
}
int main(int argc, char **argv)
{
struct sockaddr_in addr;
struct event_base *base = NULL;
struct bufferevent *event = NULL;
int sock = -1;
//初始化服务端地址信息
memset(&addr, 0x00, sizeof(addr));
addr.sin_family = AF_INET;
inet_aton(SERVER_IP, &addr.sin_addr);
addr.sin_port = htons(SERVER_PORT);
//1.创建基础事件实例
base = event_base_new();
if(NULL == base)
{
printf("Create instance of event_base failed.\n");
return -1;
}
//2.创建基于此基本事件的缓存事件
//设置非阻塞socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if(-1 == sock)
{
printf("Create socket file descriptor failed.\n");
return -1;
}
evutil_make_socket_nonblocking(sock);
event = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
//开启读写
bufferevent_enable(event, EV_READ| EV_WRITE);
//设置回调
bufferevent_setcb(event, read_cb, write_cb, event_cb, NULL);
//3.连接服务端
if(0 != bufferevent_socket_connect(event, (struct sockaddr*)(&addr), sizeof(addr)))
{
printf("Connect to server failed.\n");
return -1;
}
//4.为基本事件分配循环:开启客户端
bufferevent_write(event, call, strlen(call));
event_base_dispatch(base);
return 0;
}
3)makefile
CC=gcc
LIB_DIR=-L/usr/local/lib
INCLUDE_DIR=-I/usr/local/include/event2
CC_FLAGS=-w
LIBS=-levent
#执行可执行文件前,再当前目录执行:export LD_LIBRARY_PATH=/usr/local/lib 即:libevent库路径
all:server client
@echo "Generate libevent server and client."
server:
$(CC) $(CC_FLAGS) $(INCLUDE_DIR) $(LIB_DIR) $(LIBS) server.c -o server.out
client:
$(CC) $(CC_FLAGS) $(INCLUDE_DIR) $(LIB_DIR) $(LIBS) client.c -o client.out
.PHONY:clean
clean:
rm -f *.out
四、测试结果
1)服务端
[pig@localhost libevent]$ ls
client.c makefile server.c
[pig@localhost libevent]$ make server
gcc -w -I/usr/local/include/event2 -L/usr/local/lib -levent server.c -o server.out
[pig@localhost libevent]$ export LD_LIBRARY_PATH=/usr/local/lib
[pig@localhost libevent]$ ./server.out
Server starts.
Receive data:Hello, my remote server.
Receive data:Hello, my remote server.
Receive data:Hello, my remote server.
Receive data:Hello, my remote server.
Receive data:Hello, my remote server.
Receive data:Hello, my remote server.
Receive data:stop
Exit.
2)客户端
[pig@localhost libevent]$ ls
client.c makefile server.c server.out
[pig@localhost libevent]$ make client
gcc -w -I/usr/local/include/event2 -L/usr/local/lib -levent client.c -o client.out
[pig@localhost libevent]$ export LD_LIBRARY_PATH=/usr/local/lib
[pig@localhost libevent]$ ./client.out
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.Hello client, I've received your data.
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.Hello client, I've received your data.
Send data to server.
send stopReceive data:
Hello client, I've received your data.
Send data to server.
Send data to server.
Receive data:
Hello client, I've received your data.