Libevent学习记录(4)
Libevent常用API:
三、监听器evconnlistener相关API
1、evconnlistener_new_bind
用户只需初始化struct sockaddr_in结构体变量,然后把它作为参数传给函数evconnlistener_new_bind即可。该函数会完成对socket、bind、listen、accept的一个封装。
函数功能:分配一个监听器对象,监听给定地址上的TCP连接
函数原型:
EVENT2_EXPORT_SYMBOL
struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
const struct sockaddr *sa, int socklen);
参数说明:
@param base 关联的libevent框架上下文.
@param cb 当新连接到来时,进行回调的函数.如果函数为NULL,监听器按被禁用运行,知道函数被设置为非NULL值.
@param ptr 提供给回调函数的参数指针,(一般传递base,以便监听回调中使用)
@param flags 任意LEV_OPT_*的标识,有下面这些:
LEV_OPT_LEAVE_SOCKETS_BLOCKING:默认情况下,当连接监听器接收到新的客户端socket连接后,会把该socket设置为非阻塞的。如果设置该选项,那么就把之客户端socket保留为阻塞的
LEV_OPT_CLOSE_ON_FREE:当连接监听器释放时,会自动关闭底层的socket
LEV_OPT_CLOSE_ON_EXEC:为底层的socket设置close-on-exec标志
LEV_OPT_REUSEABLE: 在某些平台,默认情况下当一个监听socket被关闭时,其他socket不能马上绑定到同一个端口,要等一会儿才行。设置该标志后,Libevent会把该socket设置成reuseable。这样,关闭该socket后,其他socket就能马上使用同一个端口
LEV_OPT_THREADSAFE:为连接监听器分配锁。这样可以确保线程安全
@param backlog 类似于listen函数的backlog参数,设置为-1则使用默认设置.
@param addr 监听器监听地址.
@param socklen 监听器监听地址的字节长度.
备注:这个函数相当于完成系统调用socket()、bind()、listen(),并设置accept回调函数
2、evconnlistener_cb监听回调函数原型:
当监听器监听到一个新连接的时候进行回调(应该是socket的accept成功返回有效的socket描述符后,进行回调,其中fd对应accept返回的文件描述符值).
函数原型:
typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t fd,
struct sockaddr *sa, int socklen, void *user_data);
参数说明:
@param listener 事件监听器
@param fd 新的文件描述符,相当于服务器创建的与客户端通信的socket描述符
@param addr 客户端地址
@param socklen 客户端地址字节数
@param user_arg evconnlistener_new()中传入的自定义参数
四、evBuffer介绍
BufferEvent在进行读取和写入数据时,将数据存放在evBuffer中,因此,要想获取数据的相关信息—>需要对evBuffer结构进行解析。
1.evbuffer以队列的形式管理字节,从尾部添加,从头部取出(FIFO)
2.evbuffer内部存储形式是多个独立的连续内存
1、struct evbuffer* 变量的赋值2种方式
方式1:手动创建+手动向evbuffer中添加数据
struct evbuffer* evbuf = evbuffer_new(); //手动创建evbuffer
evbuffer_add(evbuf,"I am a student",100); //手动将字符串添加到evbuf中
evbuffer_add_printf(evbuf,"%s,%s,%d","Tom","Student",24); //手动将字符串格式化添加到evbuf中
... ...
evbuffer_free(evbuf); //手动释放evbuffer
方式2:bufferevent_get_input/output–>获取输入/输出evbuffer
struct evbuffer *input =bufferevent_get_input(bev); //获取输入evbuffer
struct evbuffer *output =bufferevent_get_output(bev); //获取输出evbuffer
注意:
bufferevent_get_input/bufferevent_get_output只是获取输入/输出缓冲区中的数据,并没有将数据从evbuffer中移走。
而,bufferevent_write是真正的向输出缓冲区中写入数据;bufferevent_read是真正的读走输入缓冲区中的数据。
2、与evbuffer相关的常用函数API
五、数据缓冲bufferevent相关API
bufferevent其实也就是在event_base的基础上再进行一层封装,其本质还是离不开event和event_base,从bufferevent的结构体就可以看到这一点。
bufferevent结构体中有两个event,分别用来监听同一个fd的可读事件和可写事件。由于socket 是全双工的,所以在bufferevent结构体中,也有两个evbuffer成员,分别是读缓冲区和写缓冲区。
1、创建基于套接字的bufferevent:bufferevent_socket_new
struct bufferevent *bufferevent_socket_new(
struct event_base *base,
evutil_socket_t fd,
enum bufferevent_options options
);
返回值:成功返回一个bufferevent,失败返回NULL
参数:
base:event_base
fd:表示套接字的文件描述符(如果想以后设置文件描述符,则设置fd为-1)
options: bufferevent 选项的位掩码(bufferevent选项标志)
2、释放bufferevent:bufferevent_free
void bufferevent_free(struct bufferevent *bev);
bufferevent内部具有引用计数 (如果释放时还有未决的延迟回调,则在回调完成之前bufferevent不会被删除)
如果设置了 BEV_OPT_CLOSE_ON_FREE 标志,并且 bufferevent 有一个套接字或者底层 bufferevent 作为其传输端口,则释放 bufferevent 将关闭这个传输端口
3、设置回调函数:bufferevent_setcb
void bufferevent_setcb( //给bufev设置回调函数、回调函数参数
struct bufferevent *bufev,
bufferevent_data_cb readcb, //已经读取足够的数据时被回调
bufferevent_data_cb writecb, //已经写入足够的数据时被回调
bufferevent_event_cb eventcb, //发生错误时被回调
void *cbarg //用户提供的 cbarg 参数
);
回调函数原型:
当相应的事件到来时,会触发相应的回调函数,此时回调函数的参数已经会被自动赋值
typedef void (*bufferevent_data_cb)(
struct bufferevent *bev, //触发了的事件bufferevent
void *ctx //调用bufferevent_setcb()时用户提供的 cbarg 参数
);
typedef void (*bufferevent_event_cb)(
struct bufferevent *bev,
short events, //一个表示事件标志的位掩码
void *ctx
);
4、启用bufferevent_enable / 禁用bufferevent_disable
默认情况下,新创建的bufferevent的写入EV_WRITE 是启用的,读取EV_READ是没有启用的。
可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。
没有启用读取或者写入事件时, bufferevent 将不会试图进行数据读取或者写入。
void bufferevent_enable( //启用bufev上的events事件
struct bufferevent *bufev,
short events //events:EV_READ、EV_WRITE 或 EV_READ | EV_WRITE
);
void bufferevent_disable( //禁用bufev上的events事件
struct bufferevent *bufev, `在这里插入代码片`
short events
);
short bufferevent_get_enabled( //获取 bufferevent 上当前启用的事件
struct bufferevent *bufev
);
5、向bufferevent的输出缓冲区添加数据(write)
//从data处开始的size字节数据从内存中移除,并添加到输出缓冲区的末尾
int bufferevent_write(
struct bufferevent *bufev,
const void *data,
size_t size
);
//将buf中的所有内容从内存中移除,并添加到输出缓冲区的末尾
int bufferevent_write_buffer(
struct bufferevent *bufev,
struct evbuffer *buf
);
成功时,都返回 0;发生错误时,则返回-1
6、从bufferevent的输入缓冲区移除数据(read)
//至多从输入缓冲区bufev移除 size 字节的数据,将其存储到内存中 data 处
//返回值:实际移除的字节数
//注意,对于 bufferevent_read(),data 处的内存块必须有足够的空间容纳 size 字节数据。
size_t bufferevent_read(
struct bufferevent *bufev,
void *data,
size_t size
);
//抽空输入缓冲区bufev的所有内容,将其放置到 buf 中
//成功时,都返回 0;发生错误/失败时,则返回-1
int bufferevent_read_buffer(
struct bufferevent *bufev,
struct evbuffer *buf
);
7、基于socket的bufferevent进行connect()连接bufferevent_socket_connect
用户可以在调用bufferevent_socket_new函数时,传一个-1作为socket的文件描述符,然后调用bufferevent_socket_connect函数连接服务器,无需自己写代码调用connect函数连接服务器。
bufferevent_socket_connect函数会调用socket函数申请一个套接字fd,然后把这个fd设置成非阻塞。接着就connect服务器,因为该socket fd是非阻塞的,所以不会等待,而是马上返回,连接这工作交给内核来完成。所以,返回后这个socket还没有真正连接上服务器。那么什么时候连接上呢?内核又是怎么通知通知用户呢?
一般来说,当可以往socket fd可写,那就说明已经连接上了。也就是说这个socket fd变成可写状态,就连接上了。
函数功能: 使用基于socket的bufferevent进行connect()连接
函数原型:int bufferevent_socket_connect(struct bufferevent *bev, const struct sockaddr *sa, int socklen)
函数描述:
当连接成功时eventcb会被回调 ,使用BEV_EVENT_CONNECTED 集合.内部使用evutil_socket_connect_
内部使用连接函数原型为:int evutil_socket_connect_(evutil_socket_t *fd_ptr, const struct sockaddr *sa, int socklen)
(1)如果bufferevent还没有socket set, 则分配一个socket,并且设置为非阻塞
实现代码中:
fd = bufferevent_getfd(bev);
if (fd < 0) {
if (!sa)
goto done;
fd = evutil_socket_(sa->sa_family,
SOCK_STREAM|EVUTIL_SOCK_NONBLOCK, 0);
if (fd < 0)
goto freesock;
ownfd = 1;
}
(2)内部使用evutil_socket_connect_函数进行连接,此函数返回值:
2 :连接被拒绝(errno值为ECONNREFUSED)
1 :已经连接上
0 :尚未连接上(errno值为EINTR或则EINPROGRESS),
-1 发生错误
(3) 根据evutil_socket_connect_返回值,进一步判断,
if (r == 0) {
if (! be_socket_enable(bev, EV_WRITE)) {
bufev_p->connecting = 1;
result = 0;
goto done;
}
} else if (r == 1) {
/* The connect succeeded already. How very BSD of it. */
result = 0;
bufev_p->connecting = 1;
bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);
} else {
/* The connect failed already. How very BSD of it. */
result = 0;
bufferevent_run_eventcb_(bev, BEV_EVENT_ERROR, BEV_OPT_DEFER_CALLBACKS);
bufferevent_disable(bev, EV_WRITE|EV_READ);
}
a)若尚未连接上(errno值为EINTR或则EINPROGRESS),则设置为正在连接标识,并添加一个EV_WRITE事件(超时参数根据bufferevent当前设置的数值),并标记返回值为0(按成功返回),后续若连接上时,会调用事件回调,events值为:BEV_EVENT_CONNECTED,若发生错误,则events值为BEV_EVENT_ERROR
b)若已经连接上,设置返回成功,同时也是标记正在连接,并直接调用可写回调函数(bufferevent_trigger_nolock_(bev, EV_WRITE, BEV_OPT_DEFER_CALLBACKS);)。调用bufferevent_socket_new方法中添加bufferevent事件内部的ev_write事件对应的回调函数bufferevent_writecb
在bufferevent_writecb回调中,根据connecting结合查询socket当前的连接状态,若已经连接上,则清除connecting为0,并执行事件回调,events值为:BEV_EVENT_CONNECTED。
若在bufferevent_writecb回调过程中查询到socket连接状态是出错状态,执行事件回调,events值为:BEV_EVENT_ERROR。
c)若连接被拒绝,设置返回成功,并调用错误事件回调函数,同时禁用读写缓存;
d)若发生错误,设置返回失败;
参数说明:
@param bufev 通过bufferevent_socket_new()创建的bufferevent
@param addr 服务器地址
@param socklen 服务器地址长度
返回:
@return 0 连接成功, -1 :失败.