libevent库学习 链接监听器evconnlistener

简介

evconnlistener 机制提供了监听和接受 TCP 连接的方法

接口介绍

函数介绍
struct evconnlistener *evconnlistener_new(struct event_base *base,evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,evutil_socket_t fd)传入event_base、回调函数、选项、队列数、套接字来创建一个链接监听器
struct evconnlistener *evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,const struct sockaddr *sa, int socklen)和上面的类似,不同点在于上面的传入的是已经绑定好的套接字,这个是传入地址后自动绑定
void evconnlistener_free(struct evconnlistener *lev)释放一个链接监听器
typedef void (*evconnlistener_cb)(struct evconnlistener *listener,evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr)链接监听器的回调函数
int evconnlistener_disable(struct evconnlistener *lev)禁用
int evconnlistener_enable(struct evconnlistener *lev)启用
void evconnlistener_set_cb(struct evconnlistener *lev,evconnlistener_cb cb, void *arg)设置链接监听器的回调函数

一些补充

关于evconnlistener_new的参数

如果 cb 为 NULL,则监听器是禁用的,直到设置了回调函数为止。
ptr 指针将传递给回调函数。
flags 参数控制回调函数的行为
backlog 是任何时刻网络栈允许处于还未接受状态的最大未决连接数

flag 选项详细说明
LEV_OPT_LEAVE_SOCKETS_BLOCKING
默认情况下,连接监听器接收新套接字后,会将其设置为非阻塞的,以便
将其用于 libevent。如果不想要这种行为,可以设置这个标志。
LEV_OPT_CLOSE_ON_FREE
如果设置了这个选项,释放连接监听器会关闭底层套接字。
LEV_OPT_CLOSE_ON_EXEC
如果设置了这个选项,连接监听器会为底层套接字设置 close-on-exec
标志。更多信息请查 看 fcntl 和 FD_CLOEXEC 的平台文档。
LEV_OPT_REUSEABLE
某些平台在默认情况下 ,关闭某监听套接字后 ,要过一会儿其他套接字
才可以绑定到同一个 端口。设置这个标志会让 libevent 标记套接字是
可重用的,这样一旦关闭,可以立即打开其 他套接字,在相同端口进行监
听。
LEV_OPT_THREADSAFE
为监听器分配锁,这样就可以在多个线程中安全地使用了。这是 2.0.8-
rc 的新功能


关于链接监听器回调 evconnlistener_cb

listener 参数是接收连接的连接监听器 。
sock 参数是 新接收的套接字。
addr 和 len 参数是接收连接的地址和地址长度。
ptr 是调 用 evconnlistener_new() 时用户提供的指针。

简单的练习

在下面的案例中,有一个采用了监听器的服务器程序和一个采用了bufferevent的客户端程序。

服务器

先创建一个设置了ip和port的地址和一个event_base,用此地址和base创建一个链接监听器,并且设置链接监听器的回调函数。
在链接监听器的回调函数中创建一个buffer_event,设置它的读回调函数(read_cb)和异常回调函数(event_cb)。
在读回调函数中输出读到的数据。
在异常回调函数中报告异常。

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

void listener_cb(evconnlistener *listener,evutil_socket_t fd,
	struct sockaddr *sock,int socklen,void *arg);
void socket_read_cb(bufferevent *bev,void *arg);
void socket_event_cb(bufferevent *bev,short events,void *arg);

int main(){
	struct sockaddr_in sin;
	memset(&sin,0,sizeof(struct sockaddr_in));
	sin.sin_family = AF_INET;
	sin.sin_port=htons(9999);

	event_base *base =event_base_new();
	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;
}

void listener_cb(evconnlistener *listener,evutil_socket_t fd,
	struct sockaddr *sock,int socklen,void *arg){
	printf("accept a client :%d\n",fd);
	event_base *base=(event_base*)arg;

	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(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,sizeof(reply));
}
void socket_event_cb(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);
}

客户端

创建一个event_base,一个event(ev_cmd)用于监听控制台输入,一个bufferevent(bev)用于和服务器通信。
设置ev_cmd监听标注输入的读事件,有可读信息后将信息写入bev.
设置bev的读事件的回调函数和异常回调函数。

#include<sys/stat.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/buffer.h>
#include<event2/buffer.h>
#include<event2/util.h>


int tcp_connet_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("usage : ip port\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\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 error occurred\n");
	else if(event&BEV_EVENT_CONNECTED){
		printf("connection established\n");
		return;
	}
	bufferevent_free(bev);

	struct event* ev=(struct event*)arg;
	event_free(ev);

}

演示效果

分别编译运行服务器和客户端,客户端发送信息,服务器收到后返回确认信息。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值