在上一篇博文中,我们只是稍加对libevent进行了学习,自己私下感觉好像好的多东西都没有涉及,于是在这篇中我们就来真正地学习下libevent的一些API用法,废话不说,直接上代码,这段代码也是别人的代码,但是它很有针对性,所以在此列举出,稍后会有针对性地修改,代码如下:
#ifndef __ECHO_SERVER__H #define __ECHO_SERVER__H #include <event.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <vector> using namespace std; #define EVENT_INIT {event_init();} #define MAX_LISTEN 10 namespace zmyer { struct client { client() { fd = -1; buf_ev = NULL; } int fd; struct bufferevent* buf_ev; }; void buf_read_callback(struct bufferevent* in,void* arg) { struct evbuffer* evreturn; char* buffer; buffer = evbuffer_readline(in->input); if(NULL == buffer) return; evreturn = evbuffer_new(); evbuffer_add_printf(evreturn,"you said:%s\n",buffer); bufferevent_write_buffer(in,evreturn); evbuffer_free(evreturn); free(buffer); } void buf_write_callback(struct bufferevent* out,void* arg) { } void buf_error_callback(struct bufferevent* in,short what,void* arg) { struct client* clit = (client*)arg; bufferevent_free(clit->buf_ev); close(clit->fd); free(clit); } static void setNonBlock(int sockfd) { int flags; flags = fcntl(sockfd,F_GETFL); flags |= O_NONBLOCK; fcntl(sockfd,F_SETFL,flags); } static void accept_callback(int fd,short ev,void* arg) { int client_fd; struct sockaddr_in client_addr; socklen_t client_len; struct client* clit; client_fd = ::accept(fd,(struct sockaddr*)&client_addr,&client_len); if(client_fd < 0) { perror("accpet failed\n"); return; } setNonBlock(client_fd); clit = (client*) calloc(1,sizeof(client)); if(clit == NULL) { perror("calloc failed\n"); return; } clit->fd = client_fd; clit->buf_ev = bufferevent_new( client_fd, buf_read_callback, buf_write_callback, buf_error_callback, (void*)clit); bufferevent_enable(clit->buf_ev,EV_READ | EV_PERSIST); } class EchoServer { public: EchoServer(const int port = 0):port(port){} ~EchoServer(){ close(sockfd); } bool init() { sockfd = ::socket(AF_INET,SOCK_STREAM,0); if(-1 == sockfd) { perror("create socket failed\n"); return false; } struct sockaddr_in server_addr; bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = htons(INADDR_ANY); if(bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) { perror("bind socket failed\n"); return false; } if(listen(sockfd,MAX_LISTEN)<0) { perror("listen socket failed\n"); return false; } int reuse = 1; setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)); zmyer::setNonBlock(sockfd); struct event accept_event; event_set(&accept_event,sockfd,EV_READ | EV_PERSIST,accept_callback,NULL); event_add(&accept_event,NULL); event_dispatch(); } private: int sockfd; int port; }; } #endif
测试程序:
#include "EchoServer.h" using namespace zmyer; int main() { EVENT_INIT; EchoServer echoServer(19999); echoServer.init(); return 0; }
测试结果:
Trying 127.0.0.1...
Connected to localhost.
Escape character is ‘^]‘.
hello
you said:hello
yes
you said:yes
back
you said:back
Connection closed by foreign host.
从上面的代码中可以看出,在libevent中有好多种对事件不同的处理方法,在上篇中,我们曾设计实现了一个机遇libevent的聊天室,那个聊天室只是是用来下libevent的事件处理机制,至于对于buffer的接收和发送,都是自己通过代码实现的,总感觉有点不爽,今天就在这里基于libevent再实现一遍那个聊天室,代码如下:
#ifndef __ECHO_SERVER__H #define __ECHO_SERVER__H #include <event.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <vector> #include <iostream> using namespace std; #define EVENT_INIT {event_init();bufferEventVec.clear();} #define MAX_LISTEN 10 namespace zmyer { struct client { client() { fd = -1; buf_ev = NULL; } int fd; struct bufferevent* buf_ev; }; vector<bufferevent*> bufferEventVec; typedef vector<bufferevent*>::iterator bufferEventVec_Iter; void display() { cout<<"bufferEventVec.size():"<<bufferEventVec.size()<<endl; } void bufferevent_remove(bufferevent* bufevent) { if(NULL == bufevent) return; for(bufferEventVec_Iter iter = bufferEventVec.begin(); iter != bufferEventVec.end();++iter) { if(*iter==bufevent) { bufferEventVec.erase(iter); break; } } } void buf_read_callback(struct bufferevent* in,void* arg) { struct evbuffer* evreturn; char* buffer; buffer = evbuffer_readline(in->input); if(NULL == buffer) return; printf("MSG:%s\n",buffer); evreturn = evbuffer_new(); evbuffer_add_printf(evreturn,"%s\n",buffer); for(bufferEventVec_Iter iter = bufferEventVec.begin();iter!= bufferEventVec.end();++iter) { if(*iter == in) continue; bufferevent_write_buffer(*iter,evreturn); } evbuffer_free(evreturn); free(buffer); } void buf_write_callback(struct bufferevent* out,void* arg) { } void buf_error_callback(struct bufferevent* in,short what,void* arg) { struct client* clit = (client*)arg; bufferevent_free(clit->buf_ev); bufferevent_remove(clit->buf_ev); close(clit->fd); free(clit); } static void setNonBlock(int sockfd) { int flags; flags = fcntl(sockfd,F_GETFL); flags |= O_NONBLOCK; fcntl(sockfd,F_SETFL,flags); } static void accept_callback(int fd,short ev,void* arg) { int client_fd; struct sockaddr_in client_addr; socklen_t client_len; struct client* clit; client_fd = ::accept(fd,(struct sockaddr*)&client_addr,&client_len); if(client_fd < 0) { perror("accpet failed\n"); return; } setNonBlock(client_fd); clit = (client*) calloc(1,sizeof(client)); if(clit == NULL) { perror("calloc failed\n"); return; } clit->fd = client_fd; clit->buf_ev = bufferevent_new( client_fd, buf_read_callback, buf_write_callback, buf_error_callback, (void*)clit); bufferevent_enable(clit->buf_ev,EV_READ | EV_PERSIST); bufferEventVec.push_back(clit->buf_ev); } class ChatServer { public: ChatServer(const int port = 0):port(port){} ~ChatServer(){ close(sockfd); } bool init() { sockfd = ::socket(AF_INET,SOCK_STREAM,0); if(-1 == sockfd) { perror("create socket failed\n"); return false; } struct sockaddr_in server_addr; bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = htons(INADDR_ANY); if(bind(sockfd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) { perror("bind socket failed\n"); return false; } if(listen(sockfd,MAX_LISTEN)<0) { perror("listen socket failed\n"); return false; } int reuse = 1; setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)); zmyer::setNonBlock(sockfd); struct event accept_event; event_set(&accept_event,sockfd,EV_READ | EV_PERSIST,accept_callback,NULL); event_add(&accept_event,NULL); event_dispatch(); } private: int sockfd; int port; }; } #endif
客户端代码:
#ifndef __LIBEVENT_SERVER__H #define __LIBEVENT_SERVER__H #include <iostream> #include <string> #include <string.h> #include <stdlib.h> #include <vector> #include <event.h> #include <boost/smart_ptr.hpp> #include <pthread.h> #include <sys/socket.h> #include <sys/epoll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <fstream> #include <iterator> using namespace std; using namespace boost; #define MAX_CONN 10 #define MAX_BUFFERSIZE 1024 #define EVENT_INIT {event_init();} namespace zmyer { struct Info { Info() { fd= -1; clientname.clear(); } Info(const Info& in) { clientname = in.clientname; fd = in.fd; } Info(const Info* in) { clientname = in->clientname; fd = in->fd; } Info& operator = (const Info& in) { if(this == &in) return *this; clientname = in.clientname; fd = in.fd; return *this; } ~Info(){} string clientname; int fd; }; struct Msg { Msg() { clientname.clear(); msg.clear(); } Msg(const Msg& ms) { clientname = ms.clientname; msg = ms.msg; } Msg& operator = (const Msg& ms) { clientname = ms.clientname; msg = ms.msg; } ~Msg(){} void setClientName(string clientname) { this->clientname = clientname; } void setMsg(string msg) { this->msg = msg; } string serialize() { string tag = "#"; return (clientname + tag + msg +"\n"); } void unserialize(string message) { string tag = "#"; string::size_type pos = message.find(tag); if(pos == string::npos) { clientname =""; msg = ""; } else { clientname.assign(message,0,pos); msg.assign(message,pos+1,(message.length() - pos)); } } string clientname; string msg; }; static void* send(void* arg) { shared_ptr<Info> p(new Info((Info*)arg)); string line,buffer; for(;;) { line.clear(); buffer.clear(); getline(cin,line); Msg msg; msg.clientname = p->clientname; msg.msg = line; buffer = msg.serialize(); ::send(p->fd,buffer.c_str(),strlen(buffer.c_str()),0); } return NULL; } static void* recv(void* arg) { shared_ptr<Info> p(new Info((Info*)arg)); char buffer[MAX_BUFFERSIZE]; for(;;) { bzero(buffer,sizeof(buffer)); int readlen = ::recv(p->fd,buffer,sizeof(buffer),0); if(readlen <=0) continue; Msg msg; msg.unserialize(buffer); if(msg.clientname !="") { printf("%s : %s\n",msg.clientname.c_str(),msg.msg.c_str()); } } return NULL; } class libeventClient { public: libeventClient(string ip = string(),string clientname = string(),const int port = 0):ip(ip),clientname { sock = -1; } ~libeventClient(){} bool init() { sock = ::socket(AF_INET,SOCK_STREAM,0); if(-1 == sock) { printf("sock init failed\n"); return false; } struct sockaddr_in client_addr; bzero(&client_addr,sizeof(client_addr)); client_addr.sin_family = AF_INET; client_addr.sin_port = htons(0); client_addr.sin_addr.s_addr = htons(INADDR_ANY); if(bind(sock,(struct sockaddr*)&client_addr,sizeof(client_addr))<0) { printf("bind the address failed\n"); return false; } struct sockaddr_in server_addr; bzero(&server_addr,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); inet_aton(ip.c_str(),&(server_addr.sin_addr)); if(::connect(sock,(struct sockaddr*)&server_addr,sizeof(server_addr))<0) { printf("connect to libeventserver failed\n"); return false; } return true; } void startSendThread() { Info in; in.fd = sock; in.clientname = clientname; pthread_create(&sendThread,NULL,&send,&in); } void startRecvThread() { Info in; in.fd = sock; in.clientname = clientname; pthread_create(&recvThread,NULL,&recv,&in); } void run() { startSendThread(); startRecvThread(); } void stop() { pthread_join(sendThread,NULL); pthread_join(recvThread,NULL); } private: string ip; string clientname; int port; int sock; pthread_t recvThread,sendThread; }; } #endif
服务端测试程序:
#include "EchoServer.h" using namespace zmyer; int main() { EVENT_INIT; ChatServer chatServer(19999); chatServer.init(); return 0; }
客户端测试程序:
#include "libeventServer.h" using namespace zmyer; int main(int argc,char** argv) { if(argc < 2) { printf("usage:%s <clientname>\n",argv[0]); return 0; } libeventClient libClient("127.0.0.1",argv[1],19999); if(!libClient.init()) { printf("libClient init failed\n"); return 0; } libClient.run(); sleep(60); libClient.stop(); return 0; }
测试结果:
服务器端:
MSG:B#hello,everyone,my name is B
MSG:A#weclome B,my name is A
MSG:A#how wonderful today is !!!!
MSG:B#year..,I perfer to walking,how about you A
^C
客户端A:
B : hello,everyone,my name is B
weclome B,my name is A
how wonderful today is !!!!
B : year..,I perfer to walking,how about you A
^C
客户端B:
hello,everyone,my name is B
A : weclome B,my name is A
A : how wonderful today is !!!!
year..,I perfer to walking,how about you A
^C
总结
本篇博文主要是针对libevent库又重新将我们的聊天室重新写了一篇,这一遍服务器端代码从整体上看要好于前几次,这次所使用的就是libevent库的一些接口函数,而客户端基本上和之前一样,我这样写主要是为了保持代码的完整性,在客户端方面我们需要注意一点就是libevent的evbuffer_readline函数只能读取\r\n,\r,\n等结尾的字符串,所以客户端在发送数据时需要带上至少一个类似的结尾,好了,本篇博文到此结束,后面我们将会以libevent库再来实现几个应用实例。
如果需要,请注明转载,多谢
原文:http://blog.csdn.net/zmyer/article/details/19838099