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个例子开始的主要内容,如果未能解决你的问题,请参考以下文章

C语言100个经典算法源码片段

libevent源码学习研究(libevent-0.1)

libevent学习过程

libevent源码剖析

如何从 MenuItem 导航到片段(Android)?

libevent学习笔记 —— 牛刀小试:简易的响应式服务器