赞
踩
目录
使用bufferevent简单实现服务器与客户端之间的即时聊天通讯
上回我们说到初学libevent框架,使用利用event_new、fifo实现进程间通讯此为无缓冲方式,承上篇所述,本文将讲述使用bufferevent利用数据缓存区实现网络通讯,文章的最后将以此为例实现客户端与服务器之间的聊天通讯。
数据缓冲区---bufferevent
是libevent为IO缓冲区操作提供的一种通用机制,bufferevent 由一个底层的传输端口(如套接字 )。
数据缓冲区是由读缓冲区和写缓冲区两部分组成。
使用函数bufferevent_socket_new(),创建bufferevent的套接字
- // struct bufferevent也是一个 event
- struct bufferevent * bufferevent_socket_new(
- struct event_base *base,
- evutil_socket_t fd,
- enum bufferevent_options options
- );
- // 参数options: 例如:BEV_OPT_CLOSE_ON_FREE 释放 bufferevent 时关闭底层传输端口
- // 成功时返回bufferevent,失败则返回NULL
当然我们也忘不了释放函数 bufferevent_free()
void bufferevent_free(struct bufferevent *bev);
接下来就是我们为所欲为的时刻了,把我们的房间装修成想要的样子(为缓冲区添加读写操作,设置读写回调函数)
使用函数bufferevent_setcb(),设置回调函数
- //对bufferevent设置回调函数
- void bufferevent_setcb(
- struct bufferevent *bufev,
- bufferevent_data_cb readcb,//使用 bufferevent_read()读取buff中数据信息
- bufferevent_data_cb writecb,//写回调只是提示你发生出去数据,没有实质作用
- bufferevent_event_cb eventcb,
- void *cbarg
- );
- //读写回调函数typedef
- typedef void (*bufferevent_data_cb)(
- struct bufferevent *bev,
- void *ctx
- );
-
- typedef void (*bufferevent_event_cb)(
- struct bufferevent *bev,
- short events,
- void *ctx
- );
- //events参数:
- // EV_EVENT_READING: 读取操作时发生某事件,具体是哪种事件请看其他标志
- // BEV_EVENT_WRITING:写入操作时发生某事件,具体是哪种事件请看其他标志
- // BEV_EVENT_ERROR: 操作时发生错误
- // BEV_EVENT_TIMEOUT:发生超时
- // BEV_EVENT_EOF: 遇到文件结束指示。
- // BEV_EVENT_CONNECTED:请求的连接过程已经完成实现客户端的时候可以判断
-
- //EVUTIL_SOCKET_ERROR():关于错误的更多信息
EV_EVENT_READING | 读取操作时发生某事件,具体是哪种事件请看其他标志 |
BEV_EVENT_WRITING | 写入操作时发生某事件,具体是哪种事件请看其他标志 |
BEV_EVENT_ERROR | 操作时发生错误 |
BEV_EVENT_TIMEOUT | 发生超时 |
BEV_EVENT_EOF | 遇到文件结束指示 |
BEV_EVENT_CONNECTED | 请求的连接过程已经完成实现客户端的时候可以判断 |
对回调函数的管理--设置回调函数为启动状态或禁用态:
可以启用或者禁用 bufferevent 上的 EV_READ、EV_WRITE 或者 EV_READ | EV_WRITE 事件。没有启用读取或者写入事件时, bufferevent 将不会试图进行数据读取或者写入。
- //开启
- void bufferevent_enable(
- struct bufferevent *bufev,
- short events
- );
-
- //禁用, 对应的回调就不会被调用
- void bufferevent_disable(
- struct bufferevent *bufev,
- short events
- );
-
-
- short bufferevent_get_enabled(
- struct bufferevent *bufev
- );
向bufferevent的输出缓冲区添加数据
- int bufferevent_write(
- struct bufferevent *bufev,
- const void *data,
- size_t size
- );
从bufferevent的输入缓冲区移除数据
- size_t bufferevent_read(
- struct bufferevent *bufev,
- void *data,
- size_t size
- );
在实现通讯之前,我们需要拥有connect()作为客户端的启动链接,这时我们需要调用bufferevent_socket_connect()函数---思维导向理解为socket编程中需要connect()
- int bufferevent_socket_connect( struct bufferevent *bev,
- struct sockaddr *address,
- int addrlen
- );
- address 和 addrlen 参数跟标准调用 connect() 的参数相同
bufferevent 未设置套接字,该函数将为其分配一个新的流套接字, 并且设置状态为非阻塞
bufferevent 已设置套接字,调用 bufferevent_socket_connect() 将告知libevent 套接字还未连接,直到连接成功之前不应该对其进行读取或者写入操作。
连接完成之前可以向输出缓冲区添加数据
在实现通讯之前,我们需要拥有bind()绑定与listen()监听,当然libevent为我们提供了更好的选择就是evconnlistener_new_bind()
链接监听器 - evconnlistener
- struct evconnlistener *evconnlistener_new_bind(
- struct event_base *base, evconnlistener_cb cb, void *ptr,
- unsigned flags,
- int backlog,
- const struct sockaddr *sa, int socklen
- );
当然我们也可以使用evconnlistener_new()函数,这两个可以二者取其一使用,推荐使用bind处理起来更简单方便,原因在于其内部为我们实现了绑定操作,感兴趣的小伙伴可查看源码对其深入研究
- struct evconnlistener * evconnlistener_new(
- struct event_base *base,
- evconnlistener_cb cb,
- void *ptr,
- unsigned flags, int backlog, evutil_socket_t fd
- );
回调函数的编写
- typedef void (*evconnlistener_cb)(
- struct evconnlistener *listener, evutil_socket_t sock,
- struct sockaddr *addr,
- int len,
- void *ptr
- );
注:两个 evconnlistener_new*()函数都分配和返回一个新的连接监听器对象。连接监听器使用 event_base 来得知什么时候在给定的监听套接字上有新的 TCP 连接到达时,监听器调用回调函数。
当然我们也忘不了释放函数 vconnlistener_free()
void evconnlistener_free(struct evconnlistener *lev);
对evconnlistener的管理--设置为启动状态或禁用态以及修改回调设置:
以下两个函数暂时禁止或者重新允许监听新连接
- int evconnlistener_disable(struct evconnlistener *lev);
- int evconnlistener_enable(struct evconnlistener *lev);
函数调整 evconnlistener 的回调函数和其参数
- void evconnlistener_set_cb(
- struct evconnlistener *lev,
- evconnlistener_cb cb,
- void *arg
- );
demo
举个栗子?
- //server.c
- #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>
-
- // 读缓冲区回调
- void read_cb(struct bufferevent *bev, void *arg)
- {
- char buf[1024] = {0};
- bufferevent_read(bev, buf, sizeof(buf));
- char* p = "我已经收到了你发送的数据!";
- printf("client say: %s\n", buf);
-
- }
-
- // 写缓冲区回调
- 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");
- }
-
- bufferevent_free(bev);
- printf("buffevent 资源已经被释放...\n");
- }
-
-
- void send_cb(evutil_socket_t fd, short what, void *arg);
- void cb_listener(
- struct evconnlistener *listener,
- evutil_socket_t fd,
- struct sockaddr *addr,
- int len, void *ptr)
- {
- printf("connect new client\n");
-
- 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, NULL);
- bufferevent_enable(bev, EV_READ);
-
-
- // 创建一个事件
- struct event* ev = event_new(base, STDIN_FILENO,
- EV_READ | EV_PERSIST,
- send_cb, bev);
- event_add(ev, NULL);
-
- }
- void send_cb(evutil_socket_t fd, short what, void *arg)
- {
- char buf[1024] = {0};
- struct bufferevent* bev = (struct bufferevent*)arg;
- // printf("请输入要发送的数据: \n");
- read(fd, buf, sizeof(buf));
- bufferevent_write(bev, buf, strlen(buf)+1);
- }
-
-
- int main(int argc, const char* argv[])
- {
-
- // init server
- struct sockaddr_in serv;
- memset(&serv, 0, sizeof(serv));
- serv.sin_family = AF_INET;
- serv.sin_port = htons(9876);
- serv.sin_addr.s_addr = htonl(INADDR_ANY);
-
- 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;
- }
- //client.c
- #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/bufferevent.h>
-
-
- void read_cb(struct bufferevent *bev, void *arg)
- {
- char buf[1024] = {0};
- bufferevent_read(bev, buf, sizeof(buf));
- printf("Server say: %s\n", buf);
- }
-
- 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("服务器已连接\n");
- return;
- }
-
- bufferevent_free(bev);
- printf("free bufferevent...\n");
- }
-
- void send_cb(evutil_socket_t fd, short what, void *arg)
- {
- char buf[1024] = {0};
- struct bufferevent* bev = (struct bufferevent*)arg;
- // printf("请输入要发送的数据: \n");
- read(fd, buf, sizeof(buf));
- bufferevent_write(bev, buf, strlen(buf)+1);
- }
-
-
- int main(int argc, const char* argv[])
- {
- struct event_base* base;
- base = event_base_new();
-
-
- struct bufferevent* bev;
- bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
-
- // 连接服务器
- struct sockaddr_in serv;
- memset(&serv, 0, sizeof(serv));
- serv.sin_family = AF_INET;
- serv.sin_port = htons(9876);
- evutil_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 | EV_PERSIST);
-
- // 创建一个事件
- struct event* ev = event_new(base, STDIN_FILENO,
- EV_READ | EV_PERSIST,
- send_cb, bev);
- event_add(ev, NULL);
-
- event_base_dispatch(base);
-
- event_base_free(base);
-
- return 0;
- }
更换你想要的服务器地址ip编译后就尽情享受聊天吧!!~
- gcc server.c -o server -levent
- gcc client.c -o client -levent
文中函数相关参数可翻阅libevent中文手册https://download.csdn.net/download/lemon_tea666/11247645
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。