libevent学习
reactor中io和事件的关系
- io
- io操作
- io 检测
- 事件 (网络事件、定时事件、信号事件)
- 读
- 写
- 错误事件
- 其他
- io是同步的,事件处理是异步的
libevent使用层次
- 需要自己处理io
- 在回调中处理io
- io处理交给libevent
- 直接操作缓冲区,直接开展业务逻辑
2)分割数据包- 以特性字符分割
- 以固定长度分割
- 分发数据
- 直接操作缓冲区,直接开展业务逻辑
- 要知道不同的事件处理流程
- listenfd
- clientfd
- connet(例如数据库链接)
libevent 封装层次
- reactor对象 (event_base)
- 构建对象:event_base_new
- 销毁对象: event_base_free
- 事件对象
- event
- 构建对象、绑定、事件回调:event_new
- event_set
- event_base_set
- 销毁对象:event_free
- 构建对象、绑定、事件回调:event_new
- bufferevent
- 构建对象:bufferevent_socket_new
- 销毁对象:bufferevent_free
- evconnlistener
- 构建对象、绑定、事件回调:evconnlistener_new
- 销毁对象:evconnlistener_free
- event
- 事件操作
- 注册事件:event_add
- 注销事件:event_del
- 注册事件:bufferevent_enable
- 注销事件:bufferevent_disable
- 事件循环
- 事件循环:event_base_dispatch
- 事件循环:event_base_loop
- 事件循环退出:event_base_loopexit
- 事件循环退出:event_base_break
- 事件处理
- 事件回调:brfferevent_setcb
libevent 解决了网络编程中那些痛点
- io函数使用以及网络原理
- 高效的网络缓冲区设计
- 多线程的处理
连接断开
- read=0(读关闭)
- write=-1 && errno=EPIPE (写关闭)
- epoll epollrdhup(读关闭) epollhub(双端关闭)
https://www.cnblogs.com/WindSun/p/12142656.html
初等
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/util.h>int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);int main(int argc, char argv)
{if( argc < 3 ){printf("please input 2 parameter\\n");return -1;}//两个参数依次是服务器端的IP地址、端口号int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));if( sockfd == -1){perror("tcp_connect error ");return -1;}printf("connect to server successful\\n");struct event_base* base = event_base_new();struct event *ev_sockfd = event_new(base, sockfd,EV_READ | EV_PERSIST,socket_read_cb, NULL);event_add(ev_sockfd, NULL);//监听终端输入事件struct event* ev_cmd = event_new(base, STDIN_FILENO,EV_READ | EV_PERSIST, cmd_msg_cb,(void*)&sockfd);event_add(ev_cmd, NULL);event_base_dispatch(base);printf("finished \\n");return 0;
}void cmd_msg_cb(int fd, short events, void* arg)
{char msg[1024];int ret = read(fd, msg, sizeof(msg));if( ret <= 0 ){perror("read fail ");exit(1);}int sockfd = *((int*)arg);//把终端的消息发送给服务器端//为了简单起见,不考虑写一半数据的情况write(sockfd, msg, ret);
}void socket_read_cb(int fd, short events, void *arg)
{char msg[1024];//为了简单起见,不考虑读一半数据的情况int len = read(fd, msg, sizeof(msg)-1);if( len <= 0 ){perror("read fail ");exit(1);}msg[len] = '\\0';printf("recv %s from server\\n", msg);
}typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{int sockfd, status, save_errno;struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr) );server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);status = inet_aton(server_ip, &server_addr.sin_addr);if( status == 0 ) //the server_ip is not valid value{errno = EINVAL;return -1;}sockfd = ::socket(PF_INET, SOCK_STREAM, 0);if( sockfd == -1 )return sockfd;status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );if( status == -1 ){save_errno = errno;::close(sockfd);errno = save_errno; //the close may be errorreturn -1;}evutil_make_socket_nonblocking(sockfd);return sockfd;
}
- 服务端
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<unistd.h>
#include<event.h>void accept_cb(int fd, short events, void* arg);
void socket_read_cb(int fd, short events, void *arg);
int tcp_server_init(int port, int listen_num);int main(int argc, char argv)
{int listener = tcp_server_init(9999, 10);if( listener == -1 ){perror(" tcp_server_init error ");return -1;}struct event_base* base = event_base_new();//添加监听客户端请求连接事件struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,accept_cb, base);event_add(ev_listen, NULL);event_base_dispatch(base);return 0;
}void accept_cb(int fd, short events, void* arg)
{evutil_socket_t sockfd;struct sockaddr_in client;socklen_t len = sizeof(client);sockfd = ::accept(fd, (struct sockaddr*)&client, &len );evutil_make_socket_nonblocking(sockfd);printf("accept a client %d\\n", sockfd);struct event_base* base = (event_base*)arg;//仅仅是为了动态创建一个event结构体struct event *ev = event_new(NULL, -1, 0, NULL, NULL);//将动态创建的结构体作为event的回调参数event_assign(ev, base, sockfd, EV_READ | EV_PERSIST,socket_read_cb, (void*)ev);event_add(ev, NULL);
}void socket_read_cb(int fd, short events, void *arg)
{char msg[4096];struct event *ev = (struct event*)arg;int len = read(fd, msg, sizeof(msg) - 1);if( len <= 0 ){printf("some error happen when read\\n");event_free(ev);close(fd);return ;}msg[len] = '\\0';printf("recv the client msg: %s", msg);char reply_msg[4096] = "I have recvieced the msg: ";strcat(reply_msg + strlen(reply_msg), msg);write(fd, reply_msg, strlen(reply_msg) );
}typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{int errno_save;evutil_socket_t listener;listener = ::socket(AF_INET, SOCK_STREAM, 0);if( listener == -1 )return -1;//允许多次绑定同一个地址。要用在socket和bind之间evutil_make_listen_socket_reuseable(listener);struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(port);if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )goto error;if( ::listen(listener, listen_num) < 0)goto error;//跨平台统一接口,将套接字设置为非阻塞状态evutil_make_socket_nonblocking(listener);return listener;error:errno_save = errno;evutil_closesocket(listener);errno = errno_save;return -1;
}
中等
- 客户端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h>int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void server_msg_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);int main(int argc, char argv)
{if( argc < 3 ){printf("please input 2 parameter\\n");return -1;}//两个参数依次是服务器端的IP地址、端口号int sockfd = tcp_connect_server(argv[1], atoi(argv[2]));if( sockfd == -1){perror("tcp_connect error ");return -1;}printf("connect to server successful\\n");struct event_base* base = event_base_new();struct bufferevent* bev = bufferevent_socket_new(base, sockfd,BEV_OPT_CLOSE_ON_FREE);//监听终端输入事件struct event* ev_cmd = event_new(base, STDIN_FILENO,EV_READ | EV_PERSIST, cmd_msg_cb,(void*)bev);event_add(ev_cmd, NULL);//当socket关闭时会用到回调参数bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);bufferevent_enable(bev, EV_READ | EV_PERSIST);event_base_dispatch(base);printf("finished \\n");return 0;
}void cmd_msg_cb(int fd, short events, void* arg)
{char msg[1024];int ret = read(fd, msg, sizeof(msg));if( ret < 0 ){perror("read fail ");exit(1);}struct bufferevent* bev = (struct bufferevent*)arg;//把终端的消息发送给服务器端bufferevent_write(bev, msg, ret);
}void server_msg_cb(struct bufferevent* bev, void* arg)
{char msg[1024];size_t len = bufferevent_read(bev, msg, sizeof(msg));msg[len] = '\\0';printf("recv %s from server\\n", msg);
}void event_cb(struct bufferevent *bev, short event, void *arg)
{if (event & BEV_EVENT_EOF)printf("connection closed\\n");else if (event & BEV_EVENT_ERROR)printf("some other error\\n");//这将自动close套接字和free读写缓冲区bufferevent_free(bev);struct event *ev = (struct event*)arg;//因为socket已经没有,所以这个event也没有存在的必要了event_free(ev);
}typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port)
{int sockfd, status, save_errno;struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr) );server_addr.sin_family = AF_INET;server_addr.sin_port = htons(port);status = inet_aton(server_ip, &server_addr.sin_addr);if( status == 0 ) //the server_ip is not valid value{errno = EINVAL;return -1;}sockfd = ::socket(PF_INET, SOCK_STREAM, 0);if( sockfd == -1 )return sockfd;status = ::connect(sockfd, (SA*)&server_addr, sizeof(server_addr) );if( status == -1 ){save_errno = errno;::close(sockfd);errno = save_errno; //the close may be errorreturn -1;}evutil_make_socket_nonblocking(sockfd);return sockfd;
}
- 服务端
#include<stdio.h>
#include<string.h>
#include<errno.h>
#include<event.h>
#include<event2/bufferevent.h>void accept_cb(int fd, short events, void* arg);
void socket_read_cb(bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);
int tcp_server_init(int port, int listen_num);int main(int argc, char argv)
{int listener = tcp_server_init(9999, 10);if( listener == -1 ){perror(" tcp_server_init error ");return -1;}struct event_base* base = event_base_new();//添加监听客户端请求连接事件struct event* ev_listen = event_new(base, listener, EV_READ | EV_PERSIST,accept_cb, base);event_add(ev_listen, NULL);event_base_dispatch(base);event_base_free(base);return 0;
}void accept_cb(int fd, short events, void* arg)
{evutil_socket_t sockfd;struct sockaddr_in client;socklen_t len = sizeof(client);sockfd = ::accept(fd, (struct sockaddr*)&client, &len );evutil_make_socket_nonblocking(sockfd);printf("accept a client %d\\n", sockfd);struct event_base* base = (event_base*)arg;bufferevent* bev = bufferevent_socket_new(base, sockfd, BEV_OPT_CLOSE_ON_FREE);bufferevent_setcb(bev, socket_read_cb, NULL, event_cb, arg);bufferevent_enable(bev, EV_READ | EV_PERSIST);
}void socket_read_cb(bufferevent* bev, void* arg)
{char msg[4096];size_t len = bufferevent_read(bev, msg, sizeof(msg));msg[len] = '\\0';printf("recv the client msg: %s", msg);char reply_msg[4096] = "I have recvieced the msg: ";strcat(reply_msg + strlen(reply_msg), msg);bufferevent_write(bev, reply_msg, strlen(reply_msg));
}void event_cb(struct bufferevent *bev, short event, void *arg)
{if (event & BEV_EVENT_EOF)printf("connection closed\\n");else if (event & BEV_EVENT_ERROR)printf("some other error\\n");//这将自动close套接字和free读写缓冲区bufferevent_free(bev);
}typedef struct sockaddr SA;
int tcp_server_init(int port, int listen_num)
{int errno_save;evutil_socket_t listener;listener = ::socket(AF_INET, SOCK_STREAM, 0);if( listener == -1 )return -1;//允许多次绑定同一个地址。要用在socket和bind之间evutil_make_listen_socket_reuseable(listener);struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.s_addr = 0;sin.sin_port = htons(port);if( ::bind(listener, (SA*)&sin, sizeof(sin)) < 0 )goto error;if( ::listen(listener, listen_num) < 0)goto error;//跨平台统一接口,将套接字设置为非阻塞状态evutil_make_socket_nonblocking(listener);return listener;error:errno_save = errno;evutil_closesocket(listener);errno = errno_save;return -1;
}
高等
- 客户端
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<event.h>
#include<event2/bufferevent.h>
#include<event2/buffer.h>
#include<event2/util.h>int tcp_connect_server(const char* server_ip, int port);
void cmd_msg_cb(int fd, short events, void* arg);
void server_msg_cb(struct bufferevent* bev, void* arg);
void event_cb(struct bufferevent *bev, short event, void *arg);int main(int argc, char argv)
{if( argc < 3 ){//两个参数依次是服务器端的IP地址、端口号printf("please input 2 parameter\\n");return -1;}struct event_base *base = event_base_new();struct bufferevent* bev = bufferevent_socket_new(base, -1,BEV_OPT_CLOSE_ON_FREE);//监听终端输入事件struct event* ev_cmd = event_new(base, STDIN_FILENO,EV_READ | EV_PERSIST,cmd_msg_cb, (void*)bev);event_add(ev_cmd, NULL);struct sockaddr_in server_addr;memset(&server_addr, 0, sizeof(server_addr) );server_addr.sin_family = AF_INET;server_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1], &server_addr.sin_addr);bufferevent_socket_connect(bev, (struct sockaddr *)&server_addr,sizeof(server_addr));bufferevent_setcb(bev, server_msg_cb, NULL, event_cb, (void*)ev_cmd);bufferevent_enable(bev, EV_READ | EV_PERSIST);event_base_dispatch(base);printf("finished \\n");return 0;
}void cmd_msg_cb(int fd, short events, void* arg)
{char msg[1024];int ret = read(fd, msg, sizeof(msg));if( ret < 0 ){perror("read fail ");exit(1);}struct bufferevent* bev = (struct bufferevent*)arg;//把终端的消息发送给服务器端bufferevent_write(bev, msg, ret);
}void server_msg_cb(struct bufferevent* bev, void* arg)
{char msg[1024];size_t len = bufferevent_read(bev, msg, sizeof(msg));msg[len] = '\\0';printf("recv %s from server\\n", msg);
}void event_cb(struct bufferevent *bev, short event, void *arg)
{if (event & BEV_EVENT_EOF)printf("connection closed\\n");else if (event & BEV_EVENT_ERROR)printf("some other error\\n");else if( event & BEV_EVENT_CONNECTED){printf("the client has connected to server\\n");return ;}//这将自动close套接字和free读写缓冲区bufferevent_free(bev);struct event *ev = (struct event*)arg;event_free(ev);
}
- 服务端
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<event.h>
#include<listener.h>
#include<bufferevent.h>
#include<thread.h> void listener_cb(evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sock, int socklen, void *arg);
void socket_read_cb(bufferevent *bev, void *arg);
void socket_event_cb(bufferevent *bev, short events, void *arg); int main()
{ //evthread_use_pthreads();//enable threads struct sockaddr_in sin; memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = htons(9999); event_base *base = event_base_new(); evconnlistener *listener = evconnlistener_new_bind(base, listener_cb, base, LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, 10, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)); event_base_dispatch(base); evconnlistener_free(listener); event_base_free(base); return 0;
} //一个新客户端连接上服务器了
//当此函数被调用时,libevent已经帮我们accept了这个客户端。该客户端的
//文件描述符为fd
void listener_cb(evconnlistener *listener, evutil_socket_t fd, struct sockaddr *sock, int socklen, void *arg)
{ printf("accept a client %d\\n", fd); event_base *base = (event_base*)arg; //为这个客户端分配一个bufferevent bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); bufferevent_setcb(bev, socket_read_cb, NULL, socket_event_cb, NULL); bufferevent_enable(bev, EV_READ | EV_PERSIST);
} void socket_read_cb(bufferevent *bev, void *arg)
{ char msg[4096]; size_t len = bufferevent_read(bev, msg, sizeof(msg)-1 ); msg[len] = '\\0'; printf("server read the data %s\\n", msg); char reply[] = "I has read your data"; bufferevent_write(bev, reply, strlen(reply) );
} void socket_event_cb(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"); //这将自动close套接字和free读写缓冲区 bufferevent_free(bev);
}