1. 程式人生 > >Libevent使用例項1 (簡單)

Libevent使用例項1 (簡單)

客戶端:

#include<event2/event-config.h>

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

	//兩個引數依次是伺服器端的IP地址,埠號
	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 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);
																			//回撥函式:引數是:fd,event,arg

	event_add(ev_cmd,NULL);		//新增事件

	event_base_dispatch(base);	//進入迴圈,等待就緒事件並執行事件處理
	
	printf("finished\n");
	
	return 0;
}


void cmd_msg_cb(int fd, short events, void* arg) { // fd:STDIN_FILENO,event:EV_READ | EV_PERSIST,arg:sockfd;
											      //該回調函式作用是從標準輸入讀到msg,再把msg的資料傳送給sockfd(伺服器端)
	char msg[1024];

	int ret = read(fd,msg,sizeof(msg));			//ret是讀取的長度
	if(ret <= 0) {
		perror("read fail");
		exit(1);
	}

	int sockfd = *((int*)arg);

	//把終端的訊息傳送給伺服器端
	//為了簡單起見,不考慮寫一半資料的情況
	
	write(sockfd,msg,ret);
}

void socket_read_cb(int fd, short events, void* arg) { //fd:sockfd,event:EV_READ | EV_PERSIST,arg是空
	char msg[1024];

	//為了簡單起見,不考慮寫一半資料的情況 
	int len = read(fd,msg,sizeof(msg)-1);
	if(len <= 0) {
		perror("read fail");
		exit(1);
	}

	msg[len] = '\0';
	
	printf("recv %s fron server\n",msg);
}

typedef struct sockaddr SA;
int tcp_connect_server(const char* server_ip, int port) {
	int sockfd, status,save_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);	//將IP地址轉換為網路位元組序整數


	if(status == 0)	//the server_ip is not valid value
	{
		errno = EINVAL;
		return -1;
	}

	sockfd = socket(PF_INET,SOCK_STREAM,0);
	if (sockfd == -1)
		return sockfd;

	status = connect(sockfd,(SA*)&server_addr, sizeof(server_addr) );

	if(status == -1) {
		save_errno = errno;
		close(sockfd);
		errno = save_errno;	//the close may be error 
		return -1;
	}

	evutil_make_socket_nonblocking(sockfd);	//設定成不阻塞

	return sockfd;
}

gcc -o  primary_client  primary_client.c   -levent   

伺服器端:

#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( 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);//將base引數傳給回撥函式
    
	event_add(ev_listen, NULL);

    event_base_dispatch(base);

    return 0;
}



void accept_cb(int fd, short events, void* arg)	//fd:listener , events: EV_READ | EV_PERSIST, arg:base;
{
    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;	//arg傳入的實參就是base,但是是void*,所以強制轉換回來

    //僅僅是為了動態建立一個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_assign等價於:struct event* ev = event_new(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) { //fd:sockfd, events:EV_READ | EV_PERSIST, arg:ev,監聽讀事件的回撥函式
	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 msg:%s",msg);
	char reply_msg[4096] = "I have recvieced the msg:";
	strcat(reply_msg + strlen(reply_msg),msg);	//將msg字串複製到reply_msg的末尾

	write(fd,reply_msg,strlen(reply_msg) );			//向客戶端傳送回覆已收到的資料
}


typedef struct sockaddr SA;

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, (SA*)&sin, sizeof(sin) ) < 0)
		goto error;

	if(listen(listener,listen_num) < 0)
		goto error;

	//跨平臺同一介面,將套接字設定為非阻塞狀態
	evutil_make_socket_nonblocking(listener);

	return listener;

error:
	errno_save = errno;
	evutil_closesocket(listener);
	errno = errno_save;

	return -1;
}

gcc  -o primary_server.c  primary_server    -levent

注意:

編譯時要在編譯語句後面加上 -levent 表示要連結event靜態函式庫,如果沒有這一句,那麼整個連結將會出錯,類似與下面的結局:

main.cpp:(.text+0x2c):對‘evutil_make_listen_socket_reuseable’未定義的引用
main.cpp:(.text+0xec):對‘evutil_make_socket_nonblocking’未定義的引用
main.cpp:(.text+0xf1):對‘event_base_new’未定義的引用
main.cpp:(.text+0x13f):對‘event_new’未定義的引用
main.cpp:(.text+0x154):對‘event_add’未定義的引用
main.cpp:(.text+0x160):對‘event_base_dispatch’未定義的引用
main.cpp:(.text+0x16c):對‘event_base_free’未定義的引用
/tmp/ccFk4bSL.o:在函式‘do_accept(int, short, void*)’中:
main.cpp:(.text+0x22e):對‘bufferevent_socket_new’未定義的引用
main.cpp:(.text+0x254):對‘bufferevent_setcb’未定義的引用
main.cpp:(.text+0x265):對‘bufferevent_enable’未定義的引用
/tmp/ccFk4bSL.o:在函式‘read_cb(bufferevent*, void*)’中:
main.cpp:(.text+0x28f):對‘bufferevent_getfd’未定義的引用
main.cpp:(.text+0x2f5):對‘bufferevent_write’未定義的引用
main.cpp:(.text+0x313):對‘bufferevent_read’未定義的引用
/tmp/ccFk4bSL.o:在函式‘error_cb(bufferevent*, short, void*)’中:
main.cpp:(.text+0x357):對‘bufferevent_getfd’未定義的引用
main.cpp:(.text+0x408):對‘bufferevent_free’未定義的引用

執行結果:

客戶端:

伺服器: