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’未定义的引用

运行结果:

客户端:

服务器:

猜你喜欢

转载自blog.csdn.net/amoscykl/article/details/82802116