livevent是一个轻量级的开源的高性能的事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。Libevent是用于编写高速可移植非阻塞IO应用的库。
ubuntu@VM-0-7-ubuntu:~/Project$ wget
https://github.com/libevent/libevent/releases/download/release-2.1.10-stable/libevent-2.1.10-stable.tar.gz
ubuntu@VM-0-7-ubuntu:~/Project$ tar -xzvf libevent-2.1.10-stable.tar.gz
ubuntu@VM-0-7-ubuntu:~/Project$ cd libevent-2.1.10-stable/
配置编译选项,如需交叉编译可在这一步指定交叉编译器以及安装路径–prefix指定编译路径 --host接编译后运行的机器 CC和CXX选项接交叉编译器
ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ ./configure
ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ make
ubuntu@VM-0-7-ubuntu:~/Project/libevent-2.1.10-stable$ sudo make
install
使用libevent函数之前需要分配一个或者多个event_base结构体。每个event_base结构体持有一个事件集合,可以检测以确定哪个事件是激活的。如果需要用多个线程检测IO,则需要为每个线程使用一个event_base。event_base_new()函数分配并且返回一个新的具有默认设置的event_base。
struct event_base *event_base_new(void);
接着就需要在base事件集合下创建event事件,event_new()试图分配和构造一个用于base的新的事件,设置事件标志读事件、写事件、边沿触发、信号还有持久化以及事件发生的回调函数。
struct event *event_new(struct event_base *base, evutil_socket_t fd,
short what, event_callback_fn cb, void *arg) ;
下面是可监听的事件标志:
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10
#define EV_ET 0x20
接着调用event_add()将添加事件,第一个参数为添加的事件,第二个参数可设置超时时间,若传NULL空指针则不超时。
int event_add(struct event *ev, const struct
timeval *tv)
启用事件循环,libevent循环处理事件,event_base_dispatch等同于没有设置标志的event_base_loop。所以,event_base_dispatch()将一直运行,直到被注册的事件全部被移除,或者调用了event_base_loopbreak()或者event_base_loopexit()为止。
int event_base_loop(struct event_base *base, int flags);
int event_base_dispatch(struct event_base *base);
多时候,除了响应事件之外,应用还希望做一定的数据缓冲。就用到bufferevent,基于套接字的bufferevent是最简单的,它使用libevent的底层事件机制来检测底层网络套接字是否已经就绪,可以进行读写操作,并且使用底层网络调用(如read、write)来发送和接收数据。
使用bufferevent_socket_new()创建基于套接字的bufferevent,将需要监听的文件描述符fd添加的到事件集合base中去,options为掩码通常为BEV_OPT_CLOSE_ON_FREE,表示释放bufferevent时关闭底层传输端口。这将关闭底层套接字,释放底层bufferevent等。
struct bufferevent *bufferevent_socket_new(struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options);
接着调用bufferevent_setcb()函数设置回调函数。readcb、writecb和eventcb函数将分别在已经读取足够的数据、已经写入足够的数据,或者发生错误时被调用,最后void *cbarg是传入回调函数的参数。
void bufferevent_setcb(struct bufferevent *bufev,
bufferevent_data_cb readcb, bufferevent_data_cb writecb,
bufferevent_event_cb eventcb, void *cbarg);
最后启用bufferevent上的EV_READ、EV_WRITE或者EV_READ | EV_WRITE事件,与之前的ev_add添加事件类似。
void bufferevent_enable(struct bufferevent *bufev, short events);
/*********************************************************************************
* Copyright: (C) 2020 WuYuJun<[email protected]>
* All rights reserved.
*
* Filename: libevent_demo.c
* Description: This file
*
* Version: 1.0.0(05/31/2020)
* Author: WuYuJun <[email protected]>
* ChangeLog: 1, Release initial version on "05/31/2020 03:00:52 PM"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
#include /* See NOTES */
#include
#include
#include
#include
#include
int tcp_server_init(int port, int back_log) ;
void accept_cb(int fd, short events, void* arg) ;
void socket_read_cb(struct bufferevent* bev, void* arg) ;
void event_cb(struct bufferevent *bev, short event, void *arg) ;
void to_upper(const char* buf, int buf_size, char *upper_buf, int upper_size) ;
int main(int argc, char **argv)
{
int serv_fd = -1 ;
struct event_base *base = NULL;
struct event *event_serv = NULL ;
struct sigaction sa;
serv_fd = tcp_server_init(9806, 13);
if(serv_fd < 0)
{
printf("tcp_server_init error\n") ;
return -1 ;
}
sa.sa_handler = SIG_IGN;
sigaction( SIGPIPE, &sa, 0 );
/* server close,client still write to server will have sigpipe and kill process */
base = event_base_new() ;
assert(base != NULL) ;
event_serv = event_new(base, serv_fd, EV_READ|EV_PERSIST, accept_cb, (void*)base);
event_add(event_serv, NULL);
/* 循环处理event事件 */
event_base_dispatch(base);
event_free(event_serv) ;
event_base_free(base) ;
return 0 ;
}/* End Of Main */
void accept_cb(int fd, short events, void* arg)
{
evutil_socket_t cli_fd;
struct sockaddr_in cli_addr ;
struct bufferevent *bev ;
socklen_t len = 0 ;
struct event_base *base ;
len = sizeof(cli_addr);
cli_fd = accept(fd, (struct sockaddr*)&cli_addr, &len );
evutil_make_socket_nonblocking(cli_fd);
printf("accept client%d successful\n", cli_fd);
base = (struct event_base*)arg;
bev = bufferevent_socket_new(base, cli_fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, socket_read_cb, NULL, event_cb, arg);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
}
void socket_read_cb(struct bufferevent* bev, void* arg)
{
char msg[4096];
char reply[4096] ;
size_t len = 0 ;
memset(msg,0,sizeof(msg)) ;
len = bufferevent_read(bev, msg, sizeof(msg));
printf("recv %d bytes the client: %s\n",len, msg);
memset(reply, 0, sizeof(reply)) ;
to_upper(msg, len, reply, sizeof(reply)) ;
bufferevent_write(bev, reply, len);
}
void event_cb(struct bufferevent *bev, short event, void *arg)
{
struct event *ev ;
if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n");
//这将自动close套接字和free读写缓冲区
if(bev != NULL)
bufferevent_free(bev);
}
int tcp_server_init(int port, int back_log)
{
evutil_socket_t serv_fd;
struct sockaddr_in serv_addr;
serv_fd = socket(AF_INET, SOCK_STREAM, 0);
if( serv_fd < 0 )
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(serv_fd);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
serv_addr.sin_port = htons(port);
if( bind(serv_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0 )
return -2;
if( listen(serv_fd, back_log) < 0)
return -3;
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(serv_fd);
return serv_fd;
}
void to_upper(const char* buf, int buf_size, char *upper_buf, int upper_size)
{
int i = 0 ;
if(buf == NULL || upper_buf == NULL || buf_size <= 0 || upper_size <= 0)
{
printf("Invaild input parameter in %s:%d\n" , __FUNCTION__, __LINE__) ;
return ;
}
for(i=0 ; i<buf_size; i++)
{
if(buf[i] >= 'a' && buf[i] <= 'z')
{
upper_buf[i] = buf[i] + 'A' - 'a' ;
}
else
{
upper_buf[i] = buf[i] ;
}
}
}
这里客户端程序将标准输入也加入监听,变量 base设置为全局变量,在bufferevent异常事件处理的回调函数将libevent的循环终止,从主函数中 event_base_dispatch(base); 跳出,接下来后面函数释放资源,进入大循环重新申请资源,重新连接服务器
/*********************************************************************************
* Copyright: (C) 2020 WuYuJun<[email protected]>
* All rights reserved.
*
* Filename: libevent_cli.c
* Description: This file
*
* Version: 1.0.0(06/01/2020)
* Author: WuYuJun <[email protected]>
* ChangeLog: 1, Release initial version on "06/01/2020 11:08:15 PM"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SOCK_PORT 9806
#define SOCK_IP "127.0.0.1"
int g_stop = 0 ;
void cmd_msg_cb(int fd, short events, void* arg) ;
static void event_cb(struct bufferevent *bev, short events, void *ctx) ;
void server_msg_cb(struct bufferevent* bev, void* arg) ;
void sig_handler(int SIG_NUM)
{
if(SIG_NUM == SIGUSR1)
g_stop = 1 ;
}
struct event_base *base = NULL ;
int main(int argc,char **argv)
{
struct bufferevent *bev = NULL ;
struct event *ev_cmd = NULL ;
struct sockaddr_in server_addr;
while(!g_stop)
{
base = event_base_new() ;
if(base == NULL)
{
printf("event_base_new() failed\n") ;
return -1 ;
}
bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
//监听标准输入
ev_cmd = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST, cmd_msg_cb, (void*)bev);
event_add(ev_cmd, NULL);
memset(&server_addr, 0, sizeof(server_addr) );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SOCK_PORT);
inet_aton(SOCK_IP, &server_addr.sin_addr);
if( bufferevent_socket_connect(bev, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0 )
{
printf("connect server %s:%d error!\n", SOCK_IP, SOCK_PORT) ;
}
bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
event_base_dispatch(base) ; // 阻塞 执行循环
bufferevent_free(bev) ;
event_base_free(base) ;
}/* End Of while(!g_stop) */
return 0 ;
}/* End Of Main */
void cmd_msg_cb(int fd, short events, void* arg)
{
char msg[1024];
int rv = -1 ;
struct bufferevent *bev = NULL ;
memset(msg, 0, sizeof(msg)) ;
rv = read(fd, msg, sizeof(msg));
if( rv < 0 )
{
perror("read fail ");
exit(1);
}
bev = (struct bufferevent*)arg ;
//把终端的消息发送给服务器端
bufferevent_write(bev, msg, rv);
}
static void event_cb(struct bufferevent *bev, short events, void *arg)
{
struct event *ev ;
ev = (struct event*)arg;
if (events & BEV_EVENT_ERROR)
{
fprintf(stdout,"Error from bufferevent!xxx \n");
}
if (events & BEV_EVENT_TIMEOUT)
{
fprintf(stdout,"Error from bufferevent BEV_EVENT_TIMEOUT ! \n");
}
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) // 断网重连机制
{
fprintf(stdout,"system error from bufferevent BEV_EVENT_ERROR ! \n");
if(ev != NULL)
{
event_free(ev) ;
}
else
{
fprintf(stdout,"system error ev is null");
}
if(base != NULL)
{
event_base_loopbreak(base);
}
else
{
fprintf(stdout,"system error base is null");
}
}
}
void server_msg_cb(struct bufferevent* bev, void* arg)
{
char msg[1024] ;
size_t len = 0 ;
len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv %ld bytes from server:%s\n",len, msg);
}
参考:https://blog.csdn.net/weixin_44836580/article/details/89644951
https://blog.csdn.net/nyiragongo/article/details/91974333
https://www.cnblogs.com/wainiwann/p/7096245.html