【libevent】libevent快速入门
目录
简介
基本的socket编程是阻塞/同步的,每个操作除非已经完成,出错,或者超时才会返回,这样对于每一个请求,要使用一个线程或者单独的进程去处理,系统资源没有办法支撑大量的请求。posix定义了可以使用异步的select系统调用,但是因为它采用了轮询的方式来判断某个fd是否变成active,效率不高。于是各系统就分别提出了基于异步的系统调用,例如Linux的epoll,由于在内核层面做了支持,所以可以用O(1)的效率查找到active的fd。基本上,libevent就是对这些高效IO的封装,提供统一的API,简化开发。
libevent和epoll区别?
epoll是Linux提供的一个事件检测api或者说一种检测机制,libevent是一个事件处理框架。
libevent支持多种事件检测机制,比如select,poll,epoll等,epoll只是其中一种机制,是Linux平台上效率最高的,也是Linux平台特有的。其他平台有和epoll类似的对应物件,比如bsd的kqueue,Windows的iocp...
一句话,libevent是一个开源库,epoll是一个Linux系统调用。
链接:https://www.zhihu.com/question/56908845/answer/494680014
struct event_base *base; 相当于声明epoll_fd
base = event_base_new(); 创建epoll_create
struct event; 相当于socket
event = event_new();创建epoll_event,绑定回调
event_add(); 相当于epoll_ctl,fd加入对应epoll_fd ,epoll_ctl_add,同时指定超时
evtimer_new(base,cb,arg); 创建超时事件 然后event_add()加入循环
最小堆保存时间事件,相同时长的事件保存在同一个队列中,不加入堆。
event_base_dispatch(); 相当于while(1){epoll_wait()}
被触发的各种event会被放入active队列,统一执行
https://blog.csdn.net/welongfor/article/details/86689224
原理简介
libevent默认情况下是单线程的,可以配置成多线程,每个线程有且只有一个event_base,对应一个struct event_base结构体以及附于其上的事件管理器,用来调度托管给它的一系列event,可以和操作系统的进程管理类比。当一个事件发生后,event_base会在合适的时间,不一定是立即去调用绑定在这个事件上的函数,直到这个函数执行完,再去调度其他的事件。
//创建一个event_base
struct event_base *base = event_base_new();
assert(base != NULL);
event_base内部有一个循环,循环阻塞在epoll等系统调用上,直到有一个/一些时间发生,然后去处理这些事件。当然,这些事件要被绑定在这个event_base上,每个事件对应一个struct event,可以是监听一个fd或者信号量之类的,struct event使用event_new来创建和绑定,使用event_add来将event绑定到event_base中。
// 创建并绑定一个event
struct event* listen_event;
//参数:event_base,监听的对象,需要监听的事件,事件发生后的回调函数,传给回调函数的参数
listen_event = event_new(base, listener, EV_READ | EV_PERSIST, callback_func, (void*)base);
//参数:event,超时时间,NULL表示无超时设置
event_add(listen_event, NULL);
注:libevent支持的事件及属性包括(使用bitfield实现)
- EV_TIMEOUT:超时;
- EV_READ:只要网络缓冲中还有数据,回调函数就会被触发;
- EV_WRITE:只要塞给网络缓冲的数据被写完,回调函数就会被触发;
- EV_SIGNAL:POSIX信号量;
- EV_PERSIST:不指定这个属性,回调函数被触发后事件会被删除;
- EV_ET:Edge-Trigger边缘触发(这个还不懂是什么意思)
然后启动event_base的循环,开始处理事件。循环地启动使用event_base_dispatch,循环将一直持续,找到不再有需要关注的事件,或者是遇到event_loopbreak()/event_loopexit()函数。
//启动循环,开始处理事件
event_base_dispatch(base);
接下来再来关注事件发生时的回调函数callback_func,callback_func的原型如下所示
typedef void(* event_callback_fn)(evutil_socket_t sockfd, short event_type, void *arg)
传给callback_func的是一个监听的fd,监听的事件类型,以及event_new中最后一个参数。在上述程序中,是将event_base传给了callback_func,实际中更常用的是构造一个结构体,把需要传给回调函数的参数都放进来,然后传给event_new,event_new再传给回调函数。
所以总结一下,对于一个服务器而言,流程大致如下:
- 获取待监听的内容的fd;
- 创建一个event_base;
- 创建一个event,指定待监听的fd,待监听事件的类型,以及事件放生时的回调函数及传给回调函数的参数;
- 将event添加到event_base的事件管理器中;
- 开启event_base的事件处理循环;
- (异步)当事件发生的时候,调用前面设置的回调函数。
简易版QuickStart
安装
在线安装
yum install libevent
编译安装
1. 下载
官方网站:http://libevent.org/,目前最新版是2.1
下载压缩包名为 libevent-2.1.11-stable.tar.gz
2. 解压
tar -zxvf libevent-2.1.11-stable.tar.gz
3. 配置安装路径
cd libevent-2.1.11-stable
./autogen.sh #生成configure
./configure
也可以指定后缀,比如路径为./configure -prefix=/usr
4. 编译安装
make
make install
5. 测试是否已安装
ls -al /usr/local/lib | grep libevent
6. 使用样例程序进行测试
cd sample/
此目录下,有很多样例,可以进行测试,也可以进行学习
实战
https://www.cnblogs.com/WindSun/p/12142656.html
查看libevent库是否存在
使用Libevent编写简单的程序和编译
使用libevent库开发的程序,默认编译需要加入-levent
问题:如果在编译或运行时候发现出现:error while loading shared libraries: libevent-2.1.so.7: cannot open shared object file: No such file or directory
则需要查看libevent-2.1.so.7是否存在,需要先安装mlocate
locate libevent-2.1.so.7
结果: 系统已经安装了该模块,在路径 /usr/local/lib/
映射libevent-2.1.so.7到lib64下即可
ln -s /usr/local/lib/libevent-2.1.so.7 /usr/lib64/libevent-2.1.so.7
然后就可以正常的运行了
编写代码
下面的代码实现了一个简单的echo server,server启动后,client端启动并连接,在cmd中输入文字,server端收到后,将文字再返回给client。
server端代码:
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Or you can simply run this command to install on Mac: brew install libevent
Cmd to compile this piece of code: g++ LibeventQuickStartServer.c -o LibeventQuickStartServer /usr/local/lib/libevent.a
**/
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<event.h>
void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void* arg);
int tcp_server_init(int port, int listen_num);
int main(int argc, char const *argv[])
{
/* code */
int listener = tcp_server_init(9999, 10);
if (listener == -1)
{
perror("tcp_server_init error");
return -1;
}
struct event_base* base = event_base_new();
// 监听客户端请求链接事件
struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST, accept_cb, base);
event_add(ev_listen, NULL);
event_base_dispatch(base);
return 0;
}
void accept_cb(int fd, short events, void* arg)
{
evutil_socket_t sockfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
sockfd = ::accept(fd, (struct sockaddr*)&client, &len);
evutil_make_socket_nonblocking(sockfd);
printf("accept a client %d\n", sockfd);
struct event_base* base = (event_base*)arg;
//动态创建一个event结构体,并将其作为回调参数传递给
struct event* ev = event_new(NULL, -1, 0, NULL, NULL);
event_assign(ev, base, sockfd, EV_READ | EV_PERSIST, socket_read_cb, (void*)ev);
event_add(ev, NULL);
}
void socket_read_cb(int fd, short events, void* arg)
{
char msg[4096];
struct event* ev = (struct event*)arg;
int len = read(fd, msg, sizeof(msg) - 1);
if(len <= 0)
{
printf("some error happen when read\n");
event_free(ev);
close(fd);
return;
}
msg[len] = '\0';
printf("recv the client msg : %s\n", msg);
char reply_msg[4096] = "I have received the msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
write(fd, reply_msg, strlen(reply_msg));
}
typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{
int errno_save;
evutil_socket_t listener;
listener = ::socket(AF_INET, SOCK_STREAM, 0);
if( listener == -1 )
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )
goto error;
if( ::listen(listener, listen_num) < 0)
goto error;
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
error:
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
client端代码:
/**
You need libevent2 to compile this piece of code
Please see: http://libevent.org/
Or you can simply run this command to install on Mac: brew install libevent
Cmd to compile this piece of code: g++ LibeventQuickStartClient.c -o LibeventQuickStartClient /usr/local/lib/libevent.a
**/
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/util.h>
int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int main(int argc, char** argv)
{
if( argc < 3 )
{
printf("please input 2 parameter\n");
return -1;
}
//两个参数依次是服务器端的IP地址、端口号
int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));
if( sockfd == -1)
{
perror("tcp_connect error ");
return -1;
}
printf("connect to server successful\n");
struct event_base* base = event_base_new();
struct event *ev_sockfd = event_new(base, sockfd,
EV_READ | EV_PERSIST,
socket_read_cb, NULL);
event_add(ev_sockfd, NULL);
//监听终端输入事件
struct event* ev_cmd = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST, cmd_msg_cb,
(void*)&sockfd);
event_add(ev_cmd, NULL);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
void cmd_msg_cb(int fd, short events, void* arg)
{
char msg[1024];
int ret = read(fd, msg, sizeof(msg));
if( ret <= 0 )
{
perror("read fail ");
exit(1);
}
int sockfd = *((int*)arg);
//把终端的消息发送给服务器端
//为了简单起见,不考虑写一半数据的情况
write(sockfd, msg, ret);
}
void socket_read_cb(int fd, short events, void *arg)
{
char msg[1024];
//为了简单起见,不考虑读一半数据的情况
int len = read(fd, msg, sizeof(msg)-1);
if( len <= 0 )
{
perror("read fail ");
exit(1);
}
msg[len] = '\0';
printf("recv %s from server\n", msg);
}
typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{
int sockfd, status, save_errno;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr) );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
status = inet_aton(server_ip, &server_addr.sin_addr);
if( status == 0 ) //the server_ip is not valid value
{
errno = EINVAL;
return -1;
}
sockfd = ::socket(PF_INET, SOCK_STREAM, 0);
if( sockfd == -1 )
return sockfd;
status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );
if( status == -1 )
{
save_errno = errno;
::close(sockfd);
errno = save_errno; //the close may be error
return -1;
}
evutil_make_socket_nonblocking(sockfd);
return sockfd;
}
程序运行截图
客户端
Paste_Image.png
服务端
Paste_Image.png
使用BufferEvent
在上面的代码中,client的cmd中有信息输入时,client直接将数据写入到fd中,server中收到信息后,也是直接将信息写入到fd中,因为fd是非阻塞的,所以不能保证正确。那么需要一个自己管理的缓存来管理自己的数据。那么步骤将稍微有些变化,如下所示:
- 设置scokfd为nonblocking;
- 使用bufferevent_socket_new创建一个struct bufferevent* bev,关联上面的sockfd,并托管给event_base;
- 使用bufferevent_setcb(bev, read_cb, write_cb, error_cb, (void*)arg);
- 使用buffevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST)来启动read/write事件
代码如下所示:
使用bufferevent的server端代码
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<event.h>
#include<event2/bufferevent.h>
void accept_cb(int fd, short events, void* arg);
void socket_read_cb(bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num);
int main(int argc, char** argv)
{
int listener = tcp_server_init(9999, 10);
if( listener == -1 )
{
perror(" tcp_server_init error ");
return -1;
}
struct event_base* base = event_base_new();
//添加监听客户端请求连接事件
struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,
accept_cb, base);
event_add(ev_listen, NULL);
event_base_dispatch(base);
event_base_free(base);
return 0;
}
void accept_cb(int fd, short events, void* arg)
{
evutil_socket_t sockfd;
struct sockaddr_in client;
socklen_t len = sizeof(client);
sockfd = ::accept(fd, (struct sockaddr*)&client, &len );
evutil_make_socket_nonblocking(sockfd);
printf("accept a client %d\n", sockfd);
struct event_base* base = (event_base*)arg;
bufferevent* bev = bufferevent_socket_new(base, sockfd, 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(bufferevent* bev, void* arg)
{
char msg[4096];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv the client msg: %s", msg);
char reply_msg[4096] = "I have recvieced the msg: ";
strcat(reply_msg + strlen(reply_msg), msg);
bufferevent_write(bev, reply_msg, strlen(reply_msg));
}
void event_cb(struct bufferevent *bev, short event, void *arg)
{
if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n");
//这将自动close套接字和free读写缓冲区
bufferevent_free(bev);
}
typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{
int errno_save;
evutil_socket_t listener;
listener = ::socket(AF_INET, SOCK_STREAM, 0);
if( listener == -1 )
return -1;
//允许多次绑定同一个地址。要用在socket和bind之间
evutil_make_listen_socket_reuseable(listener);
struct sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
sin.sin_port = htons(port);
if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )
goto error;
if( ::listen(listener, listen_num) < 0)
goto error;
//跨平台统一接口,将套接字设置为非阻塞状态
evutil_make_socket_nonblocking(listener);
return listener;
error:
errno_save = errno;
evutil_closesocket(listener);
errno = errno_save;
return -1;
}
使用bufferevent的client端代码
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h>
int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void server_msg_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int main(int argc, char** argv)
{
if( argc < 3 )
{
printf("please input 2 parameter\n");
return -1;
}
//两个参数依次是服务器端的IP地址、端口号
int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));
if( sockfd == -1)
{
perror("tcp_connect error ");
return -1;
}
printf("connect to server successful\n");
struct event_base* base = event_base_new();
struct bufferevent* bev = bufferevent_socket_new(base, sockfd,
BEV_OPT_CLOSE_ON_FREE);
//监听终端输入事件
struct event* ev_cmd = event_new(base, STDIN_FILENO,
EV_READ | EV_PERSIST, cmd_msg_cb,
(void*)bev);
event_add(ev_cmd, NULL);
//当socket关闭时会用到回调参数
bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);
bufferevent_enable(bev, EV_READ | EV_PERSIST);
event_base_dispatch(base);
printf("finished \n");
return 0;
}
void cmd_msg_cb(int fd, short events, void* arg)
{
char msg[1024];
int ret = read(fd, msg, sizeof(msg));
if( ret < 0 )
{
perror("read fail ");
exit(1);
}
struct bufferevent* bev = (struct bufferevent*)arg;
//把终端的消息发送给服务器端
bufferevent_write(bev, msg, ret);
}
void server_msg_cb(struct bufferevent* bev, void* arg)
{
char msg[1024];
size_t len = bufferevent_read(bev, msg, sizeof(msg));
msg[len] = '\0';
printf("recv %s from server\n", msg);
}
void event_cb(struct bufferevent *bev, short event, void *arg)
{
if (event & BEV_EVENT_EOF)
printf("connection closed\n");
else if (event & BEV_EVENT_ERROR)
printf("some other error\n");
//这将自动close套接字和free读写缓冲区
bufferevent_free(bev);
struct event *ev = (struct event*)arg;
//因为socket已经没有,所以这个event也没有存在的必要了
event_free(ev);
}
typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{
int sockfd, status, save_errno;
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr) );
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
status = inet_aton(server_ip, &server_addr.sin_addr);
if( status == 0 ) //the server_ip is not valid value
{
errno = EINVAL;
return -1;
}
sockfd = ::socket(PF_INET, SOCK_STREAM, 0);
if( sockfd == -1 )
return sockfd;
status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );
if( status == -1 )
{
save_errno = errno;
::close(sockfd);
errno = save_errno; //the close may be error
return -1;
}
evutil_make_socket_nonblocking(sockfd);
return sockfd;
}
欢迎留言交流学习
参考链接:
https://www.felix021.com/blog/read.php?2068
http://blog.csdn.net/luotuo44/article/details/39670221
作者:shareLin
链接:https://www.jianshu.com/p/8ea60a8d3abb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
/**
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.
*/
EVENT2_EXPORT_SYMBOL
struct evconnlistener *evconnlistener_new_bind(
struct event_base *base, //事件基座
evconnlistener_cb cb, //回调函数
void *ptr, //回调函数的参数
unsigned flags, //flags参数控制回调函数的行为
int backlog, //backlog是任何时刻网络栈允许处于还未接受状态的最大未决连接数
const struct sockaddr *sa,
int socklen);
flags取值:http://www.cppblog.com/mysileng/archive/2013/02/07/197763.aspx
backlog是任何时刻网络栈允许处于还未接受状态的最大未决连接数。更多细节请查看系统的listen()函数文档。如果backlog是负的,libevent会试图挑选一个较好的值;如果为0,libevent认为已经对提供的套接字调用了listen()。
使用bufferevent的server端代码(libevent-2.1.10-stable)
/********************************************************
Copyright (C), 2016-2018,
FileName: main
Author: woniu201
Email: wangpengfei.201@163.com
Created: 2018/11/21
Description: 使用libevent实现TCP Server
********************************************************/
#include <iostream>
#include <string.h>
#include <event2/event.h>
#include <event2/bufferevent.h>
#include <event2/listener.h>
#ifdef _WIN32
#include <windows.h>
#include <time.h>
#include <ws2tcpip.h>
#else
#include <unistd.h>
#include <arpa/inet.h>
#endif
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "wsock32.lib")
using namespace std;
char IPdotdec[20]; //存放点分十进制IP地址
struct in_addr s; // IPv4地址结构体
/************************************
@ Brief: 读缓冲区回调
@ Author: woniu201
@ Created: 2018/11/21
@ Return:
************************************/
void read_cb(struct bufferevent* bev, void* arg)
{
char buf[1024] = { 0 };
char* ip = (char*)arg;
bufferevent_read(bev, buf, sizeof(buf));
cout << "client " << ip << " say:" << buf << endl;
//写数据给客户端
const char* p = "i am server, i received your msg!";
bufferevent_write(bev, p, strlen(p) + 1);
}
/************************************
@ Brief: 写缓冲区回调
@ Author: woniu201
@ Created: 2018/11/21
@ Return:
************************************/
void write_cb(struct bufferevent* bev, void* arg)
{
cout << "I'm 服务器,成功写数据给客户端,写缓冲回调函数被调用..." << endl;
}
/************************************
@ Brief: 事件回调
@ Author: woniu201
@ Created: 2018/11/21
@ Return:
************************************/
void event_cb(struct bufferevent* bev, short events, void* arg)
{
char* ip = (char*)arg;
if (events & BEV_EVENT_EOF)
{
cout << "connection closed:" << ip << endl;
}
else if (events & BEV_EVENT_ERROR)
{
cout << "some other error !" << endl;
}
bufferevent_free(bev);
cout << "bufferevent 资源已经被释放..." << endl;
}
/************************************
@ Brief: 监听回调
@ Author: woniu201
@ Created: 2018/11/21
@ Return:
************************************/
void cb_listener(struct evconnlistener* listener, evutil_socket_t fd, struct sockaddr* addr, int len, void* ptr)
{
struct sockaddr_in* client = (sockaddr_in*)addr;
inet_ntop(AF_INET, (void*)&(client->sin_addr), IPdotdec, 16);
cout << "connect new client: " << IPdotdec << "::" << ntohs(client->sin_port) << endl;
struct event_base* base = (struct event_base*)ptr;
//添加新事件
struct bufferevent* bev;
bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
//给bufferevent缓冲区设置回调
bufferevent_setcb(bev, read_cb, write_cb, event_cb, IPdotdec);
//启动 bufferevent的 读缓冲区。默认是disable 的
bufferevent_enable(bev, EV_READ);
}
int main()
{
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
(void)WSAStartup(wVersionRequested, &wsaData);
#endif
//init server
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8888);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
//创建 event_base
struct event_base* base;
base = event_base_new();
//创建套接字
//绑定
//接收连接请求
struct evconnlistener* listener;
listener = evconnlistener_new_bind(base, cb_listener, base, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 36, (struct sockaddr*) & serv, sizeof(serv));
//启动循环监听
event_base_dispatch(base);
evconnlistener_free(listener);
event_base_free(base);
return 0;
}
笔记
当有可读事件时会调用readcb函数,当有可写事件时调用writecb函数,发生错误时调用eventcb函数。
libevent有个机制大家要注意,bufferevent_socket读写事件和普通的描述符读写事件。如果是带有bufferevent的,bufferevent本身又带有一层,为什么能设置高水位和低水位,就是通过bufferevent这一层来的;至于回调函数,
readcb是当套接字缓冲区有数据,对于bufferevent这种,如果超过低水位之类的才会触发;对于普通的描述符会立即触发(使用epool的LT模式的话);
writecb是能够写到缓冲区中才会触发,是一个后续的操作,可以理解成write-complete
也就是 writecb 是 bufferevent_write(bev, p, strlen(p) + 1);的数据真正的写到发送缓冲区 完成了才触发
这个writecb是 bufferevent_write 写完之后, 触发的,