libevent學習,從3個例子開始

最近一直在ubuntu下研究c++開源代碼,本文的內容是大名鼎鼎的libevent庫。html

本文將從3個例子着手,從簡單到複雜,分別包含了client與server。c++

文章參考該文 ,並對其中的代碼進行從新梳理。本文代碼均在ubuntu下經過程序驗證。ubuntu

1.初級版服務器

>gcc -o client client.c -leventapp

>gcc -o server server.c -leventsocket

上述兩句命令可以編譯出2個可執行文件。-levent在這裏起到的做用是,連接libevent庫到程序中。tcp

此程序中server默認使用9999端口,且client運行時須要指定ip和port,如>./client 127.0.0.1 9999ide

 

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以下:idea

#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);
}

  

Jetbrains,Avaliable License Servers

http://xidea.online/servers.html

相關文章
相關標籤/搜索