libevent学习,从3个例子开始
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libevent学习,从3个例子开始相关的知识,希望对你有一定的参考价值。
最近一直在ubuntu下研究c++开源代码,本文的内容是大名鼎鼎的libevent库。
本文将从3个例子着手,从简单到复杂,分别包含了client与server。
文章参考该文,并对其中的代码进行重新梳理。本文代码均在ubuntu下通过程序验证。http://blog.csdn.net/luotuo44/article/details/39670221
1.初级版
>gcc -o client client.c -levent
>gcc -o server server.c -levent
上述两句命令能够编译出2个可执行文件。-levent在这里起到的作用是,链接libevent库到程序中。
此程序中server默认使用9999端口,且client运行时需要指定ip和port,如>./client 127.0.0.1 9999
client.c如下:
#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; } //2 params, IP, port int sockfd=tcp_connect_server(argv[1],atoi(argv[2])); if(sockfd==-1){ perror("tcp_connect error\n"); 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; } /* * callback, */ 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\n"); exit(1); } msg[len]=‘\0‘; printf("recv %s from server\n",msg); } /* * callback,send msg to server */ 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\n"); exit(1); } int sockfd=*((int*)arg); //把终端的消息发送给服务器 //为了简单起见,不考虑写一半数据的情况 write(sockfd,msg,ret); } int tcp_connect_server(const char* server_ip,int port){ int sockfd,status,save_erro; 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){ errno=EINVAL; return -1; } sockfd=socket(PF_INET,SOCK_STREAM,0); if(sockfd==-1){ return sockfd; } status=connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)); if(status==-1){ int save_errno=errno; close(sockfd); errno=save_errno; return -1; } evutil_make_socket_nonblocking(sockfd); return sockfd; }
server.c如下:
#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 (-1==listener) { perror("tcp_server_init error\n"); 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 = (struct 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(%d) msg: %s\n",fd, msg); char reply_msg[4096] = "I have received the msg: "; strcat(reply_msg + strlen(reply_msg), msg); write(fd, reply_msg, strlen(reply_msg)); } 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, (struct sockaddr*) &sin, sizeof(sin)) < 0) { errno_save = errno; evutil_closesocket(listener); errno = errno_save; return -1; } if (listen(listener, listen_num) < 0) { errno_save = errno; evutil_closesocket(listener); errno = errno_save; return -1; } evutil_make_socket_nonblocking(listener); return listener; }
2.中级版
和上述版本类似,编译过程相同。
client.c
#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; } //2 param 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"); } //会自动关闭套接字和清空读写缓冲区 bufferevent_free(bev); //因此,socket没有了,event也就没有存在必要了 struct event*ev = (struct event*) arg; event_free(ev); } int tcp_connect_server(const char*server_ip, int port) { int sockfd, status, 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) { errno=EINVAL; return -1; } sockfd=socket(PF_INET,SOCK_STREAM,0); if(sockfd==-1) { return sockfd; } status=connect(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr)); if(status==-1) { int save_errno=errno; close(sockfd); errno=save_errno; return -1; } evutil_make_socket_nonblocking(sockfd); return sockfd; }
server.c
#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(struct 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 = (struct event_base*) arg; struct 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(struct 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\n", msg); char reply_msg[4096] = "I have received 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"); } //这自动关闭套接字和清空读写缓冲区 bufferevent_free(bev); } int tcp_server_init(int port, int listen_num) { int errno; 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,(struct sockaddr*)&sin,sizeof(sin))<0) { int errno_save=errno; evutil_closesocket(listener); errno=errno_save; return -1; } if(listen(listener,listen_num)<0) { int errno_save=errno; evutil_closesocket(listener); errno=errno_save; return -1; } evutil_make_socket_nonblocking(listener); return listener; }
3.高级版
在第三部分的代码中,有很多地方存在错误。主要存在与server的头文件引用和内部struct调用上。
目前所有的文档资料中,第三部分代码均很类似,且无法编译成功,本人也是修改了部分代码,可正常通过gcc编译和运行。
代码如下所示:
client.c
#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) { //2param 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; } //自动关闭套接字和清空缓冲区 bufferevent_free(bev); struct event*ev = (struct event*) arg; event_free(ev); }
server.c
#include<netinet/in.h> #include<sys/socket.h> #include<unistd.h> #include<stdio.h> #include<string.h> #include<event.h> #include<event2/listener.h> #include<event2/bufferevent.h> #include<event2/thread.h> void listener_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr*sock, int socklen, void*arg); void socket_read_cb(struct bufferevent*bev, void*arg); void socket_event_cb(struct 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); struct event_base*base = event_base_new(); struct 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(struct evconnlistener*listener, evutil_socket_t fd, struct sockaddr*sock, int socklen, void*arg) { printf("accept a client %d\n", fd); struct event_base*base = (struct event_base*) arg; //为此客户端分配一个bufferevent struct 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(struct 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 have read your data"; bufferevent_write(bev, reply, strlen(reply)); } void socket_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); }
以上是关于libevent学习,从3个例子开始的主要内容,如果未能解决你的问题,请参考以下文章