网站怎么怎么做关键字,邯郸注册网络科技公司,文字直播网站怎么做的,wordpress发视频教程libevent
库概念和特点 开源。精简。跨平台#xff08;Windows、Linux、maxos、unix#xff09;。专注于网络通信#xff08;不一定非用在网络当中#xff0c;比如下面的读写管道#xff09;。 libevent特性#xff1a;基于事件#xff0c;面向“文件描述符…libevent
库概念和特点 开源。精简。跨平台Windows、Linux、maxos、unix。专注于网络通信不一定非用在网络当中比如下面的读写管道。 libevent特性基于事件面向“文件描述符”的异步回调通信模型。 异步函数注册时间和函数真正被执行的时间不同函数真正是被内核调用(等待某一条件满足)
【
事件库就干一件事监听文件描述符的库机制。 先创建一个事件底座然后创建你想监听的时间插到底座上libevent底座就会帮忙监听事件当事件发生时就会自动调用事件的回调函数。 】 libevent使用框架 常规事件
常规事件函数
//创建event_base(事件底座)
struct event_base* base event_base_new();//创建事件event
常规事件event:event_new();
例如
struct event* signal_event evsignal_new(base, SIGINT, signal_cb, (void *)base);buffer event:bufferevent_socket_new();//将事件添加到base上
int event_add(struct event* ev, const struct timeval* tv);//循环监听事件满足
int event_base_dispatch(struct event_base* base);
event_base_dispatch(base); //调用//释放event_base
event_base_free(base);其他函数 libevent函数使用的一个简单的例子
/*This example program provides a trivial server program that listens for TCPconnections on port 9995. When they arrive, it writes a short message toeach client connection, and closes each connection once it is flushed.Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/#include string.h
#include errno.h
#include stdio.h
#include signal.h
#ifndef _WIN32
#include netinet/in.h
# ifdef _XOPEN_SOURCE_EXTENDED
# include arpa/inet.h
# endif
#include sys/socket.h
#endif#include event2/bufferevent.h
#include event2/buffer.h
#include event2/listener.h
#include event2/util.h
#include event2/event.hstatic const char MESSAGE[] Hello, World!\n;static const int PORT 9995;static void listener_cb(struct evconnlistener *, evutil_socket_t,struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);//main函数中是使用框架剩下外面的函数就是事件的回调
int main(int argc, char **argv)
{//创建事件底座指针struct event_base *base;struct evconnlistener *listener;
//创建事件指针struct event *signal_event;
//创建套接字的地址结构struct sockaddr_in sin;
#ifdef _WIN32WSADATA wsa_data;WSAStartup(0x0201, wsa_data);
#endif
//真正创建事件底座base event_base_new();if (!base) {fprintf(stderr, Could not initialize libevent!\n);return 1;}memset(sin, 0, sizeof(sin));
//初始化套接字的地址结构sin.sin_family AF_INET;sin.sin_port htons(PORT);
//一个函数干了套接字通信的 socket、listen、bind、accept几个函数的事情listener evconnlistener_new_bind(base, listener_cb, (void *)base,LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,(struct sockaddr*)sin,sizeof(sin));if (!listener) {fprintf(stderr, Could not create a listener!\n);return 1;}
//注册一个信号事件signal_event evsignal_new(base, SIGINT, signal_cb, (void *)base);if (!signal_event || event_add(signal_event, NULL)0) {fprintf(stderr, Could not create/add a signal event!\n);return 1;}
//循环监听相当于 while循环加里面的epoll_wait下面的循环有很大概率没有机会调用event_base_dispatch(base);
//释放之前创建的事件evconnlistener_free(listener);event_free(signal_event);event_base_free(base);printf(done\n);return 0;
}//下面是千篇一律定义了三个回调分别独立对应三个事件。看一个就行
static void listener_cb(struct evconnlistener *listener, evutil_socket_t fd,struct sockaddr *sa, int socklen, void *user_data)
{struct event_base *base user_data;struct bufferevent *bev;bev bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);if (!bev) {fprintf(stderr, Error constructing bufferevent!);event_base_loopbreak(base);return;}bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);bufferevent_enable(bev, EV_WRITE);bufferevent_disable(bev, EV_READ);bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}static void conn_writecb(struct bufferevent *bev, void *user_data)
{struct evbuffer *output bufferevent_get_output(bev);if (evbuffer_get_length(output) 0) {printf(flushed answer\n);bufferevent_free(bev);}
}static void conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{if (events BEV_EVENT_EOF) {printf(Connection closed.\n);} else if (events BEV_EVENT_ERROR) {printf(Got an error on the connection: %s\n,strerror(errno));/*XXX win32*/}/* None of the other events can happen here, since we havent enabled* timeouts */bufferevent_free(bev);
}static void signal_cb(evutil_socket_t sig, short events, void *user_data)
{struct event_base *base user_data;struct timeval delay { 2, 0 };printf(Caught an interrupt signal; exiting cleanly in two seconds.\n);event_base_loopexit(base, delay);
}常规事件函数使用详解
*事件创建 event_new
struct event* event_new(struct event_base* base,evutil_socket_t fd,short what,event_callback_fd cb,void* arg);
//
参数
base基事件, 也就是event_base_new()的返回值
fd绑定到event上的文件描述符监听的对象
what文件描述符对应的事件(r/w/e)监听类型是什么what的取值EV_READ读一次后退出循环EV_WRITE写一次退出循环EV_PERSIST持续触发可以理解为while(read())或while(write())
cb一旦满足监听条件回调的函数
arg回调函数的参数//返回值成功返回创建的事件对象event
事件回调函数
参数跟上面一致实际用时候不用传参
typedef void (*event_callback_fn)(evutil_socket_t fd,short what,void* arg);
f
事件添加(到event_base上)
int event_add(struct event* ev,const strcut timeval* tv);
//参数
ev是要添加的事件对象就是event_new的返回值
tv一般传NULL表示一直等到事件被触发回调函数才会被调用。如果传非0会等待事件被触发如果事件一直不触发时间到回调函数依然会被调用//返回值成功返回0失败返回-1事件删除对应add从event_base上摘下事件(不常用)
int event_del(struct event* ev);
//ev是要摘下的事件对象就是event_new的返回值
事件销毁
int event_free(strcut event* ev);
//ev是要销毁的事件对象就是event_new的返回值 基于libevent实现的FIFO的读写
读FIFO进程
void perr_exit(const char* str) {perror(str);exit(1);
}
//读回调
void read_cb(evutil_socket_t fd, short what, void* arg) {char buf[BUFSIZ] {0};read(fd, buf, sizeof(buf));printf(Read from writer:%s\n, buf);printf(what%s\n, what EV_READ ? Yes : No);sleep(1);return;
}int main(int argc, char* argv[]) {int ret 0;int fd 0;unlink(myfifo);mkfifo(myfifo, 0644);fd open(myfifo, O_RDONLY | O_NONBLOCK);//借助epoll反应堆默认都是非阻塞if (fd -1)perr_exit(open error);
//读管道时候管道中不一定有数据此时就需要有事件概念介入了
//创建事件基座struct event_base* base event_base_new();struct event* ev NULL;
//创建事件ev event_new(base, fd, EV_READ | EV_PERSIST, read_cb, NULL);
//添加到事件基座event_add(ev, NULL);
//开启事件监听event_base_dispatch(base);
//释放事件event_base_free(base);close(fd);return 0;
}写FIFO进程
#include stdio.h
#include unistd.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include string.h
#include fcntl.h
#include event2/event.h// 对操作处理函数
void write_cb(evutil_socket_t fd, short what, void *arg)
{// write管道char buf[1024] {0};static int num 0;sprintf(buf, hello,world-%d\n, num);write(fd, buf, strlen(buf)1);sleep(1);
}// 写管道
int main(int argc, const char* argv[])
{// open file//int fd open(myfifo, O_WRONLY | O_NONBLOCK);int fd open(myfifo, O_WRONLY);if(fd -1){perror(open error);exit(1);}// 写管道struct event_base* base NULL;base event_base_new();// 创建事件struct event* ev NULL;// 检测的写缓冲区是否有空间写//ev event_new(base, fd, EV_WRITE , write_cb, NULL);ev event_new(base, fd, EV_WRITE | EV_PERSIST, write_cb, NULL);// 添加事件event_add(ev, NULL);// 事件循环event_base_dispatch(base);// 释放资源event_free(ev);event_base_free(base);close(fd);return 0;
}重点关注对写事件event_new函数入参写监听时长的定义写一次退出循环和一直触发的区别 libevent模型事件的运行状态未决和非未决
未决有资格被处理但还没有被处理 只剩数据没到达了
非未决没有资格被处理 除了数据没到达还有其他监听条件没满足比如事件没add 小注意事件被处理完如果设置了persistadd会被再调用一次 bufferevent
bufferevent概念
bufferevent的事件对象fd有两个缓冲区。 读读缓冲区有数据读回调函数被调用使用bufferevent_read()读数据 写使用bufferevent_write向写缓冲中写数据该缓冲区中有数据自动写出写完后回调函数被调用(鸡肋) 原理bufferent利用队列实现两个缓冲区(数据只能读一次读走就没, FIFO) bufferevent事件对象创建和销毁
创建socket事件对象 bufferevent_socket_new
struct bufferevent* bufferevent_socket_new(struct event_base* base,evutil_socket_t fd,enum bfferevent_options options)
//入参
base基事件event_base_new函数的返回值
fd封装到bufferevent内的fd(绑定在一起),即lfd
enum表示枚举类型一般取BEV_OPT_CLOSE_ON_FREE关闭时释放底层套接字//返回
成功返回bufferevent事件对象
对比event有区别。这个不是在 new的时候设置的回调而是借助后面的 bufferevent_setcb函数专门设置回调。
销毁事件对象 bufferevent_socket_free
void bufferevent_socket_free(struct bufferevent* ev)
//入参
bufevbufferevent_socket_new()函数的返回值readcb读缓冲对应的回调自己封装在其内部读数据(注意是用bufferevent_read()读而不是read())writecb鸡肋传NULL即可eventcb可传NULLcbarg回调函数的参数 给bufferevent事件设置回调 bufferevent_setcb
void bufferevent_setcb(struct bufferevent* bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void* cbarg);
//入参
bufevbufferevent_socket_new()函数的返回值readcb读缓冲对应的回调自己封装在其内部读数据(注意是用bufferevent_read()读而不是read())writecb鸡肋传NULL即可eventcb备用回调比如异常回调也可传NULLcbarg三个回调函数的相同参数 readcb对应的回调函数 void read_cb(struct bufferevent* bev,void* arg){...bufferevent_read();
}为什么 read_cb里面不封装read因为没有fd给read传。fd通过new函数封装好的bufferevent传。
size_t bufferevent_read(struct bufferevent* bufev,void* data,size_t size);writecb对应的回调函数 int bufferevent_write(struct bufferevent* bufev,const void* data,size_t size)eventcb对应的回调函数
typedef void (*bufferevent_event_cb)(struct bufferevent* bev,short events,void* ctx)buffer缓冲区开启和关闭
默认新建的bufferevent写缓冲是enable的而读缓冲是disable的
通过下面函数操作缓冲区读写使能 void bufferevent_enable(struct bufferevent* bufev,short events); //启用缓冲区
void bufferevnet_disable(struct bufferevent* bufev,short events); //禁用/*例如:开启读缓冲*/
void bufferevent_enable(bufev,EV_READ);//events的值可传入三个宏
EV_READ
EV_WRITE
EV_READ|EV_WRITE
也可获取缓冲区的禁用状态
short bufferevent_get_enable(struct bufferevent* bufev)客户端和服务器连接和监听函数详解
客户端建立连接 bufferevent_socket_connect
int bufferevent_socket_connect(struct bufferevent* bev,struct sockaddr* address,int addrlen);//入参
bevbufferevent事件对象(封装了fd)
addresslen等同于connect()的参2和参3
服务器创建监听器对象 evconnlistener_new_bind
这一个函数可以完成socket(),bind(),listen(),accept()四个函数的作用
struct evconnlistener* evconnlistener_new_bind(struct event_base* base,evconnlistener_cb cb,void* ptr,unsigned flags,int backlog,const struct sockaddr* sa,int socklen);
//入参
cb监听回调函数(建立连接后用户要做的操作)
ptr回调函数的参数
flags可识别的标志通常传
LEV_OPT_CLOSE_ON_FREE(释放bufferevent时关闭底层传输端口, 这将关闭底层套接字, 释放底层bufferevent等)
LEV_OPT_REUSEABLE(可以设置端口复用)
backlog相当于listen的参2,最大连接数 传-1表示使用默认的最大值
sa服务器自己的地址结构
socklensa的大小
回调函数 evconnlistener_cb cb
监听成功后说明客户端链接成功原来的accept传出的客户端地址结构现在在这里传出。
typedef void (*evconnlistener_cb)(struct evconnlistener* listener,evutil_socker_t sock,struct sockaddr* addr,int len,void* ptr);
//入参
listenerevconnlistener_new_bind 的返回值
sock用于通信的文件描述符即cfd
addr传出客户端的地址结构
len客户端地址结构的长度
ptr外部ptr传进来的值
libevent实现TCP服务器流程 感觉这个思路和epoll反应堆还有些距离 创建基事件event_base使用evconlistener_new_bind()创建监听服务器用来专门监听客户端链接事件有新链接上来就调用其回调函数完成socket(),bind(),listen(),accept()四个函数的作用 设置evconlistener_new_bind()回调函数listner_cb()该回调函数被调用说明有一个新的客户端连接上来会得到一个新的fd用于跟客户端通信。创建bufferevent事件对象bufferevent_socket_new()将fd封装到这个事件对象中使用bufferevent_setcb()函数给bufferevent的readwriteevent设置回调函数设置读写缓冲enable启动循环event_base_dispath()监听通信事件当监听的事件满足时read_cb会被调用在其内部bufferevent_read()读释放连接 libevent实现TCP服务器源码分析
#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));printf(client say: %s\n, buf);char *p 我是服务器, 已经成功收到你发送的数据!;// 发数据给客户端bufferevent_write(bev, p, strlen(p)1);//执行完后回调write_cbsleep(1);
}// 写缓冲区回调
void write_cb(struct bufferevent *bev, void *arg)
{printf(Im服务器, 成功写数据给客户端,写缓冲区回调函数被回调...\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 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;//创建出用于通信的socket事件对象bufferevent并给bufferevent缓冲区设置回调和读使能struct bufferevent *bev;//2参fd被封装在新的事件对象 bufferevent中bev bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, read_cb, write_cb, event_cb, NULL);bufferevent_enable(bev, EV_READ);
}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;
}客户端流程简析和回顾 创建event_base使用bufferevent_socket_new()创建一个用跟服务器通信的bufferevent事件对象使用bufferevent_socket_connect连接服务器使用bufferevent_setcb()给bufferevent对象的readwriteevent设置回调设置bufferevent对象的读写缓冲区使能 4、5放connect前应该也没问题接受发送数据bufferevent_read()/bufferevent_write()启动循环监听event_base_dispath()释放资源 #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.hvoid 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, len1);
}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 infostruct 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;
}