在上一篇博文中,我們只是稍加對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庫再來實現幾個應用實例。
如果需要,請註明轉載,多謝