libevent是一种支持i/o多路复用技术,epoll技术的轻量级网络开发库。
1. 两个重要的结构体 :
struct event :指一个事件(fd或者信号),供接下来监听使用。
struct event_base :统一管理所有事件,认为是所有事件的集合。
2.libevent 常用接口:
2.1 event_init :用于初始化事件集合
2.2 event_base_new 创建event_base对象
2.3 event_assign 给event对象赋值
2.5 event_add 将事件添加到集合中
2.4 event_set: 初始化事件, 将fd与事件ev绑定在一起,
函数原型:event_set( struct event *env, int fd, shorts events, void(callback*)(evutil_socket_t ,short,void*), void* arg) 事件 关联的文字描述符 事件类型 回调函数 回调函数参数
事件类型:1 EV_TIMEOUT 定时器事件
2 EV_READ 可读事件 fd上有可以读取的数据
3 EV_WRITE 可写数据 表示fd 可写
4 EV_SIGNAL 信号事件 表示有信号到达
5 EV_PERSIST 事件持续化标志 事件处理后继续保持监听
callback 事件发生时调用的回调函数的指针
3. 搭建高并发服务器
3.1 之前在搭建服务器时,我们一般经过以下4个步骤:
1. socket() 创建套接字
2. bind() 绑定信息
3. listen()监听
4. accept()接受连接
libevent将4个步骤封装在一起,这就是evconnlistener_new_bind()函数,其作用就是创建监听tcp or udp对象,用于接受传入得数据或者连接。
其函数原型为
struct evconnlistener* evconnlistener_new_bind(struct event_base* base, evconnlistener_cb cb, void* ptr, unsigned flags, int backlog, const struct sockaddr *sa, int socklen)
struct event_base* base : 和监听器关联得事件集合
evconnlistener_cb cb:当新连接到达时会触发调用函数,如果设置为NULL,则监听器视为禁用直至回调函数被调用,回调函数的原型是:
void (*evconnlistener_cb) (struct evconnlistener * ,evutil_socket_t , struct sockaddr* ,int socklen, void *)
struct evconnlistener * :用于表示监听器的结构体,这个结合体中包括监听操作所需的信息与状态。
evutil_socket_t :新连接或接受数据的文件描述符
struct sockaddr* :新连接的客户端地址信息或接收数据的对端地址信息。
int socklen: 上面结构体的大小
void * :传递给回调函数的用户自定义参数
void* ptr 传递给回调函数的自定义的参数
unsigned flags:一组标志,用于指定监听器的行为和特性。其主要包括:
1. LEV_OPT_CLOSE_ON_FREE: 当事件基础被释放,自动关闭监听器,并自动释放相关资源。
2. LEV_OPT_REUSEABLE: 使地址具有重用的功能,即使之前的连接任处于time_out 的状态,也可以立即重用监听地址。
3. LEV_OPT_THREADSAFE:将监听器设置为线程安全模式,以支持多线程下并发操作。
4. LEV_OPT_DISABLED:将创建的监听器视为禁用状态,必须调用evconnlistener_enable()来解禁
5. LEV_OPT_ LEAVE_SOCKETS_BLOCKING:在接受连接后,将套接字阻塞。
6. LEV_OPT_CLOSE_ON_EXEC: 将套接字视为在exec系统调用时关闭。
这些可以通过 | 来组合使用
backlog: 设置监听队列的长度
const struct sockaddr *sa 指向sockaddr结构体的指针,存储服务器的ip以及端口。
我们现在一般使用的是 sockaddr_in 结构体,因此需要强转,(struct sockaddr*)&sockaddr_in;
该结构体需要我们填写以下3个方面:
sockaddr_in.sin_family = AF_INET;//地址族 iv4
sockaddr_in.sin_port = 端口;
sockaddr_in.sin_addr.s_addr = inet_addr(" ");//inet_addr是将字符串形式的地址转为网络字节序的二进制。
3.2 处理读取和写入数据的操作
bufferevent是提供一个缓冲区,用以后续处理读取和写入数据的操作,
一般步骤为:
1. 创建一个与回调函数中套接字文件描述符相关联的bufferevent对象
这里用到bufferevent_socket_new()
函数原型为:struct bufferevent* bufferevent_socket_new(struct event_base* base, evutil_socket_t fd, enum buffererevent_options)
struct event_base* base : 事件集合
fd:套接字文件描述符
buffererevent_options:bufferevent选项标志,控制其行为。以下有几种选择项:
BEV_OPT_CLOSE_ON_FREE:释放bufferevent对象时,关闭套接字,并释放相资源。
BEV_OPT_THREADSAFE: 启用线程安全,当多线程操作时,启用这一项来保证线程安全。
BEV_OPT_DEFER_CALLBACKS: 推迟回调,当设置此选项后,只有在执行event_base_loop或者event_base_dispatch()后,才执行回调函数。
BEV_OPT_UNLOCK_CALLBACKS: 解锁回调,当启用线程安全时,使用此选项可以在执行回调函数前解锁回调锁,可以减少回调函数执行期间的锁竞争。
BEV_OPT_CLOSE_ON_EXEC: 当利用exec函数执行新程序时,会关闭套接字,不会继承到新的进程中。
BEV_OPT_IGNORE_WATERMARKS: 启用后会忽略高水位和低水位,继续读取和写入数据。
BEV_OPT_SET_TIMEOUTS:启用超时设置,启动后,可以使用bufferevent_set_timeout()函数来设置读取写入数据的超时时间。
2. 给bufferevent对象设置回调函数
这里用到bufferevent_setcb函数,其函数原型为:void bufferevent_setcb(struct bufferevent *,
bufferevent_data_cb readcb, bufferevent_data_cb writecb, bufferevent_event_cb eventcb,void *cbarg) 参数意思分别为buffer对象,读事件回调函数,写事件回调函数,其他事件回调函数(比如异常事件),自定义参数。前面两种事件不需要哪个,就用NULL代替。
void readcb(struct bufferevent*bev,void* ctx)
void writecb(struct bufferevent*bev,void* ctx)
void eventcb(struct bufferevent*bev,short event ,void* ctx) short event包括:"
1. BEV_EVENT_CONNECTED:连接已经建立
2. BEV_EVENT_ERROR: 发生错误。
3. BEV_EVENT_EOF:连接已经关闭。
4. BEV_EVENT_TIMEOUT: 超时事件。
3. 激活buffer对象
这里用到bufferevent_enable函数来使能。其函数原型如下:
int buffererevent_enable (struct bufferevent*,short event)
第一个参数:buffer对象
第二个参数:EV_READ | EV_WRITE 的任意组合