在对Libevent有了较清晰的认知之后,下面来实现一个基于Libevent的回射客户/服务器模型,以加深对Libevent机制的理解。
1.服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <arpa/inet.h> #include <event2/listener.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #define PORT 6666 #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) /*已连接套接字读回调函数*/ void echo_read_cb(struct bufferevent *bev, void *ctx) { /*将读到的数据,打印输出*/ char recvbuf[1024]={0}; bufferevent_read(bev, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout); /*再将读到的数据回射给客户端*/ bufferevent_write(bev, recvbuf, strlen(recvbuf)); } /*已连接套接字事件回调函数*/ void echo_event_cb(struct bufferevent *bev, short events, void *ctx) { if(events & BEV_EVENT_ERROR) perror("Error from bufferevent"); if(events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) bufferevent_free(bev); } /*监听套接字正常回调函数*/ void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen, void *ctx) { /*初始化bufferevent对象*/ struct event_base *base = evconnlistener_get_base(listener); struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE); /*为bufferevent设置回调函数*/ bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL); /*将bufferevent加入等待队列*/ bufferevent_enable(bev, EV_READ|EV_PERSIST); printf("连接已建立。\n"); } /*监听套接字accpet错误回调函数*/ void accept_error_cb(struct evconnlistener *listener, void *ctx) { struct event_base *base = evconnlistener_get_base(listener); int err = EVUTIL_SOCKET_ERROR(); fprintf(stderr, "Got an error %d(%s) on the listener." "Shutint down.\n", err, evutil_socket_error_to_string(err)); /*退出循环*/ event_base_loopexit(base, NULL); } int main() { struct event_base *base; struct evconnlistener *listener; struct sockaddr_in sin; /*初始化base对象*/ base = event_base_new(); if(base == NULL) ERR_EXIT("event_base_new"); /*初始化地址结构*/ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(PORT); sin.sin_addr.s_addr = htonl(INADDR_ANY); /*创建监听套接字*/ listener = evconnlistener_new_bind(base, accept_conn_cb, NULL, LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (struct sockaddr*)&sin, sizeof(sin)); if(listener == NULL) ERR_EXIT("evconnlistener_new_bind"); /*设置accept新连接时产生错误的回调函数*/ evconnlistener_set_error_cb(listener, accept_error_cb); /*轮询*/ event_base_dispatch(base); return 0; } |
2.客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <arpa/inet.h> #include <event2/bufferevent.h> #include <event2/buffer.h> #include <event2/event.h> #define PORT 6666 #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) /*事件回调函数*/ void echo_event_cb(struct bufferevent *bev, short events, void *ctx) { if(events & BEV_EVENT_CONNECTED){ printf("连接已建立.\n"); /*向服务器写数据*/ char sendbuf[1024] = {0}; fgets(sendbuf, sizeof(sendbuf), stdin); bufferevent_write(bev, sendbuf, strlen(sendbuf)); } else if(events & BEV_EVENT_ERROR){ bufferevent_free(bev); } } /*读回调函数*/ void echo_read_cb(struct bufferevent *bev, void *ctx) { /*读缓冲区数据*/ char recvbuf[1024] = {0}; bufferevent_read(bev, recvbuf, sizeof(recvbuf)); fputs(recvbuf, stdout); /*再次向服务器写数据*/ char sendbuf[1024] = {0}; fgets(sendbuf, sizeof(sendbuf), stdin); bufferevent_write(bev, sendbuf, strlen(sendbuf)); } int main() { struct event_base *base; struct bufferevent *bev; struct sockaddr_in sin; /*初始化base对象*/ base = event_base_new(); if(base == NULL) ERR_EXIT("event_base_new"); /*初始化地址结构*/ sin.sin_family = AF_INET; sin.sin_port = htons(PORT); sin.sin_addr.s_addr = inet_addr("127.0.0.1"); /*初始化bufferevent对象,自动分配一个非阻塞的流套接字*/ bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); /*为bufferevent设置回调函数*/ bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL); /*开启bufferevent读事件*/ bufferevent_enable(bev, EV_READ|EV_PERSIST); /*发起connect连接*/ if(bufferevent_socket_connect(bev, (struct sockaddr*)&sin, sizeof(sin)) < 0){ bufferevent_free(bev); ERR_EXIT("bufferevent_socket_connect"); } /*开始轮询*/ event_base_dispatch(base); return 0; } |
3.编译、运行
(1)服务端
1 2 3 4 5 |
[root@localhost demo]# gcc -Wall -g echosrv.c -o echosrv -levent [root@localhost demo]# ./echosrv 连接已建立。 hello world hahahahahahaha... |
(2)客户端
1 2 3 4 5 6 7 |
[root@localhost demo]# gcc -Wall -g echocli.c -o echocli -levent [root@localhost demo]# ./echocli 连接已建立. hello world hello world hahahahahahaha... hahahahahahaha... |
4.其他
至此,Libevent的初步学习和总结就结束了。