赞
踩
话接上回:libevent库学习_StudyWinter的博客-CSDN博客_libevent库
重点还是:libevent库是基于【事件】的异步通信模型========回调
本次重点介绍bufferevent。
很多时候,除了响应事件之外,应用还希望做一定的数据缓冲。比如说,写入数据的时候,通常的运行模式是:
(1)决定要向连接写入一些数据,把数据放入到缓冲区中
(2)等待连接可以写入
(3)写入尽量多的数据
(4)记住写入了多少数据,如果还有更多数据要写入,等待连接再次可以写入
这种缓冲lO模式很通用,libevent为此提供了一种通用机制,即 bufferevent。bufferevent由一个底层的传输端口(如套接字),一个读取缓冲区和一个写入缓冲区组成。与通常的事件在底层传输端口已经就绪,可以读取或者写入的时候执行回调不同的是,bufferevent 在读取或者写入了足够量的数据之后调用用户提供的回调。
如图;
带有buf的事件对象有两个缓冲区,一个读缓冲区,一个写缓冲区;
读缓冲区设置对应的回调函数,使用bufferevent_read函数代替read函数读取数据;
写缓冲区设置对应的回调函数,使用bufferevent_write函数代替write函数写数据;
接下来介绍一波函数,对照【libevent库学习_StudyWinter的博客-CSDN博客_libevent库】这一节的常规事件来记忆。
创建函数
- struct bufferevent *bufferevent_socket_new(struct event_base *base,
- evutil_socket_t fd,
- enum bufferevent_options options);
-
- // base: event_base
- // fd: 封装到bufferevent内的 fd
- // options:BEV_OPT_CLOSE_ON_FREE
- // 返回: 成功创建的 bufferevent事件对象。
销毁函数
- void bufferevent_socket_free(struct bufferevent *ev);
- // ev:bufferevent_socket_new函数的返回值
普通事件不同的是,普通事件在创建时就设置回调函数
- // 普通事件创建函数
- struct event *event_new(struct event_base *base,
- evutil_socket_t fd,
- short what,
- event_callback_fn cb;
- void *arg);
-
- // base: event_base_new()返回值。
- // fd: 绑定到 event 上的 文件描述符
- // what:对应的事件(r、w、e)
- // EV_READ 一次 读事件
- // EV_WRTIE 一次 写事件
- // EV_PERSIST 持续触发。 结合 event_base_dispatch 函数使用,生效。
- // cb:一旦事件满足监听条件,回调的函数。
- // typedef void (*event_callback_fn)(evutil_socket_t fd, short, void *)
- // arg: 回调的函数的参数。
- /// 返回值:成功创建的 event
而eventbufferevent事件需要在使用一个函数设置回调函数
- void bufferevent_setcb(struct bufferevent * bufev,
- bufferevent_data_cb readcb,
- bufferevent_data_cb writecb,
- bufferevent_event_cb eventcb,
- void *cbarg );
-
- // bufev: bufferevent_socket_new() 返回值
- // readcb: 设置 bufferevent 读缓冲,对应回调 read_cb{ bufferevent_read() 读数据 }
- // writecb: 设置 bufferevent 写缓冲,对应回调 write_cb { } -- 给调用者,发送写成功通知。 可以 NULL
- // eventcb: 设置 事件回调。 也可传NULL
- // cbarg: 上述回调函数使用的 参数。
-
-
- // 读回调函数
- typedef void (*bufferevent_data_cb)(struct bufferevent *bev, void*ctx);
- void read_cb(struct bufferevent *bev, void *cbarg )
- {
- .....
- bufferevent_read(); --- read();
- }
- size_t bufferevent_read(struct bufferevent *bev, void *buf, size_t bufsize);
-
-
- // 写回调
- // write 回调函数类型:
- int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
默认新建的bufferevent的写缓冲是enable,而读缓冲是disable的
开启读缓冲区
- // 函数
- void bufferevent_enable(struct bufferevent *bufev, short events); 启动
- events: EV_READ、EV_WRITE、EV_READ|EV_WRITE
-
-
- // 开启读缓冲
- bufferevent_enable(evev, EV_READ);
之前在简单版的服务器-客户端通信中,客户端中使用socket、connect函数用于连接服务端,现在一个函数搞定
- int bufferevent_socket_connect(struct bufferevent *bev,
- struct sockaddr *address,
- int addrlen);
-
- // bev: bufferevent 事件对象(封装了fd)
- // address、len:等同于 connect() 参2/3
之前在简单版的服务器-客户端通信中,服务器使用socket、bind、listen、accep建立连接并监听客户端,现在也一个函数搞定,当这个函数的回调函数被调用,说明已经有新的客户端连接上来了。
- struct evconnlistener *evconnlistener_new_bind (
- struct event_base *base,
- evconnlistener_cb cb,
- void *ptr,
- unsigned flags,
- int backlog,
- const struct sockaddr *sa,
- int socklen);
-
- // base: event_base
- // cb: 回调函数。 一旦被回调,说明在其内部应该与客户端完成, 数据读写操作,进行通信。
- // ptr: 回调函数的参数
- // flags: LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE
- // backlog: listen() 2参。 -1 表最大值
- // sa:服务器自己的地址结构体
- // socklen:服务器自己的地址结构体大小。
- // 返回值:成功创建的监听器。
释放监听服务器
void evconnlistener_free(struct evconnlistener *lev);
(1)创建event_base
(2)创建服务器连接监听器evconnlistener_new_bind()
(3)在evconnlistener_new_bind的回调函数中,处理接受连接后的操作
(4)回调函数被调用,说明有一个新的客户端连接上来。会得到一个新的fd,用于跟客户端通信(读、写)
(5)使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中
(6)使用bufferevent_setcb给这个事件对象的read、write、event设置回调
(7)设置bufferevent的读写缓冲区enable/disable
(8)接收、发送数据bufferevent_read()、bufferevent_write()
(9)启动循环
(10)释放资源
具体代码
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <event2/event.h>
- #include <event2/listener.h>
- #include <event2/bufferevent.h>
-
-
-
- /**
- * libevent实现TCP服务器流程
- * (1)创建event_base
- * (2)创建服务器连接监听器evconnlistener_new_bind()
- * (3)在evconnlistener_new_bind的回调函数中,处理接受连接后的操作
- * (4)回调函数被调用,说明有一个新的客户端连接上来。会得到一个新的fd,用于跟客户端通信(读、写)
- * (5)使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中
- * (6)使用bufferevent_setcb给这个事件对象的read、write、event设置回调
- * (7)设置bufferevent的读写缓冲区enable/disable
- * (8)接收、发送数据bufferevent_read()、bufferevent_write()
- * (9)启动循环
- * (10)释放资源
- *
- */
-
-
- // 8.1读回调
- // 即从读缓冲区读数据,再发给写缓冲区
- void read_cb(struct bufferevent* bev, void* arg) {
- char buf[1024] = {0};
- // 使用bufferevent_read读数据
- bufferevent_read(bev, buf, sizeof(buf));
- printf("client say:%s\n", buf);
- char* p = "我是服务器,已经成功收到你发的数据......\n";
-
- // 写回给缓冲区
- bufferevent_write(bev, p, strlen(p) + 1);
- sleep(1);
- }
-
-
- // 8.2写回调
- void write_cb(struct bufferevent* bev, void* arg) {
- printf("我是服务器,成功写数据给客户端,写缓冲回调函数被回调......\n");
- }
-
- // 8.3事件回调
- void event_cb(struct bufferevent* bev, short events, void* arg) {
- if (events & BEV_EVENT_EOF) {
- printf("connection closed......\n");
- } else if (events & BEV_EVENT_ERROR) {
- printf("some other error......\n");
- }
- bufferevent_free(bev);
- printf("bufferevent资源已经被释放......\n");
- }
-
- // 3在evconnlistener_new_bind的回调函数中处理接受连接后的操作
- // 这个函数回调,说明有客户端已经建立连接
- void listener_cb(struct evconnlistener* listener,
- evutil_socket_t fd,
- struct sockaddr* addr,
- int len, void* ptr)
- {
- printf("connect new client......\n");
- // 因为创建bufferevent事件需要base,所以将base封装到void* ptr里面
- struct event_base* base = (struct event_base*)ptr;
-
- // 5使用bufferevent_socket_new创建一个新的bufferevent事件,将fd封装到这个事件对象中
- struct bufferevent* bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
-
- // 6使用bufferevent_setcb给这个事件对象设置回调函数
- bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
-
- // 7设置bufferevent的读写缓冲区,默认是写打开,读关闭
- bufferevent_enable(bev, EV_READ);
- }
-
-
- int main(int argc, const char* argv[])
- {
- // 1创建event_base
- struct event_base* base = event_base_new();
-
- // 初始化服务器地址结构
- struct sockaddr_in server_addr;
- memset(&server_addr, 0, sizeof(server_addr));
- server_addr.sin_family = AF_INET;
- server_addr.sin_port = htons(9527);
- server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
-
- // 2创建服务器连接监听器evconnlistener_new_bind();
- // 这一个函数顶socket、bind、listen、accept
- struct evconnlistener* listener = NULL;
- listener = evconnlistener_new_bind(base, listener_cb, base,
- LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE,
- -1, (struct sockaddr*)(&server_addr), sizeof(server_addr));
-
-
- // 10启动循环
- event_base_dispatch(base);
-
- // 11释放资源
- evconnlistener_free(listener);
- event_base_free(base);
- return 0;
- }
(1)创建event_base
(2)使用bufferevent_socket_new创建一个和服务器通信的bufferevent事件对象
(3)使用bufferevent_socket_connect连接服务器
(4)使用bufferevent_setcb给bufferevent对象的read、write、event设置回调
(5)设置bufferevent对象的读写缓冲区enable/disenable
(6)接收、发送数据bufferevent_read()、bufferevent_write()
(7)释放资源
具体代码
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <event2/bufferevent.h>
- #include <event2/event.h>
- #include <arpa/inet.h>
-
- void read_cb(struct bufferevent *bev, void *arg)
- {
- char buf[1024] = {0};
- bufferevent_read(bev, buf, sizeof(buf));
-
- printf("fwq say:%s\n", buf);
-
- bufferevent_write(bev, buf, strlen(buf)+1);
- sleep(1);
- }
-
- void write_cb(struct bufferevent *bev, void *arg)
- {
- printf("----------我是客户端的写回调函数,没卵用\n");
- }
-
- void event_cb(struct bufferevent *bev, short events, void *arg)
- {
- if (events & BEV_EVENT_EOF)
- {
- printf("connection closed\n");
- }
- else if(events & BEV_EVENT_ERROR)
- {
- printf("some other error\n");
- }
- else if(events & BEV_EVENT_CONNECTED)
- {
- printf("已经连接服务器...\\(^o^)/...\n");
- return;
- }
-
- // 释放资源
- bufferevent_free(bev);
- }
-
- // 客户端与用户交互,从终端读取数据写给服务器
- void read_terminal(evutil_socket_t fd, short what, void *arg)
- {
- // 读数据
- char buf[1024] = {0};
- int len = read(fd, buf, sizeof(buf));
-
- struct bufferevent* bev = (struct bufferevent*)arg;
- // 发送数据
- bufferevent_write(bev, buf, len+1);
- }
-
- int main(int argc, const char* argv[])
- {
- struct event_base* base = NULL;
- base = event_base_new();
-
- int fd = socket(AF_INET, SOCK_STREAM, 0);
-
- // 通信的fd放到bufferevent中
- struct bufferevent* bev = NULL;
- bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
-
- // init server info
- struct sockaddr_in serv;
- memset(&serv, 0, sizeof(serv));
- serv.sin_family = AF_INET;
- serv.sin_port = htons(9876);
- inet_pton(AF_INET, "127.0.0.1", &serv.sin_addr.s_addr);
-
- // 连接服务器
- bufferevent_socket_connect(bev, (struct sockaddr*)&serv, sizeof(serv));
-
- // 设置回调
- bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);
-
- // 设置读回调生效
- // bufferevent_enable(bev, EV_READ);
-
- // 创建事件
- struct event* ev = event_new(base, STDIN_FILENO, EV_READ | EV_PERSIST,
- read_terminal, bev);
- // 添加事件
- event_add(ev, NULL);
-
- event_base_dispatch(base);
-
- event_free(ev);
-
- event_base_free(base);
-
- return 0;
- }
测试
在编译的时候,需要在后面加上 -levent
在使用libevent库的时候明显能感觉到很方便,就是搭架子,然后再组装在一起,难点就是函数太多,初学不好记
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。