Libevent library notes (two) annotated version of the sample code hello-world

Annotated version of hello-world.c in the sample sample code directory under libevent-2.1.11-stable decompression directory

/*
  This example program provides a trivial server program that listens for TCP
  connections on port 9995.  When they arrive, it writes a short message to
  each client connection, and closes each connection once it is flushed.

  Where possible, it exits cleanly in response to a SIGINT (ctrl-c).
*/


#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#ifndef _WIN32
#include <netinet/in.h>
# ifdef _XOPEN_SOURCE_EXTENDED
#  include <arpa/inet.h>
# endif
#include <sys/socket.h>
#endif

#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>

static const char MESSAGE[] = "Hello, World!\n";

static const int PORT = 9995; //服务端监听端口号    

static void listener_cb(struct evconnlistener *, evutil_socket_t,
    struct sockaddr *, int socklen, void *);
static void conn_writecb(struct bufferevent *, void *);
static void conn_eventcb(struct bufferevent *, short, void *);
static void signal_cb(evutil_socket_t, short, void *);

int
main(int argc, char **argv)
{
    
    
	struct event_base *base;  //libevent上下文对象,可理解为框架的根节点,可以从此节点直接或则间接地获取任何地当前框架运行状态信息
	struct evconnlistener *listener;//事件监听器
	struct event *signal_event;//信号事件

	struct sockaddr_in sin;
#ifdef _WIN32
	WSADATA wsa_data;
	WSAStartup(0x0201, &wsa_data);
#endif

	base = event_base_new(); //创建libevent上下文对象,失败返回NULL(0)
	if (!base) {
    
    
		fprintf(stderr, "Could not initialize libevent!\n");
		return 1;
	}

	memset(&sin, 0, sizeof(sin));
	sin.sin_family = AF_INET;//设置协议簇
	sin.sin_port = htons(PORT); //设置监听端口号

	/**
	函数功能:分配一个监听器对象,监听给定地址上的TCP连接 
	函数原型:
	EVENT2_EXPORT_SYMBOL
	struct evconnlistener *evconnlistener_new_bind(struct event_base *base,
	    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
	    const struct sockaddr *sa, int socklen);
	参数说明:
	   @param base 关联的libevent框架上下文.
	   @param cb 当新连接到来时,进行回调的函数.如果函数为NULL,监听器按被禁用运行,知道函数被设置为非NULL值.
	   @param ptr 提供给回调函数的参数指针.
	   @param flags 任意LEV_OPT_*的标识
	   @param backlog 类似于listen函数的backlog参数,设置为-1则使用默认设置.
	   @param addr 监听器监听地址.
	   @param socklen 监听器监听地址的字节长度.
	 */
	listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));

	if (!listener) {
    
    
		fprintf(stderr, "Could not create a listener!\n");
		return 1;
	}
	/**
	函数功能:分配一个信号事件 
	函数原型:
	#define evsignal_new(b, x, cb, arg)				\
	event_new((b), (x), EV_SIGNAL|EV_PERSIST, (cb), (arg))
	->
	struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)

	函数功能:在被添加到上下文对象前.创建指定一个事件
函数原型:struct event *
event_new(struct event_base *base, evutil_socket_t fd, short events, void (*cb)(evutil_socket_t, short, void *), void *arg)
返回:
	返回一个事件,其能后续被event_add() and event_del()添加和删除操作. 如果发生错误,返回NULL。
描述:
	如果事件包含 EV_READ, EV_WRITE, or EV_READ|EV_WRITE其中的一种,那么fd是文件描述符或socket描述符 如果事件包含EV_SIGNAL,那么fd是一个信号数值 如果事件不包含以上任意一种标识,那么事件只能被超时或则手动调用event_active()进行触发,这种情况下fd需设置为-1.

  EV_PERSIST标识使event_add() 持续的,直到 event_del()被调用.
  EV_ET标识兼容EV_READ和EV_WRITE, 它告诉libevent框架使用边沿触发事件
  EV_TIMEOUT标识在此没有效果

  相同的fds可以进行多种事件的监听,但是需要相同的触发模式。

 当事件被激活时, 事件循环会调用回调函数,回调函数会传入3个参数. 
	第一个为fd.  
	第二个参数为触发事件的位域  EV_READ, EV_WRITE, or EV_SIGNAL.  EV_TIMEOUT 标识指示超时事件
    EV_ET指示边沿事件触发.
	第三个参数自定义的回调函数参数指针 
参数描述:
  @param base 事件附加到的libevent框架上下文
  @param fd 被监控的文件描述符或则信号 
  @param events 需要被监控的事件,通过位定义:  EV_READ, EV_WRITE,EV_SIGNAL, EV_PERSIST, EV_ET.
  @param callback 事件发生时,需要调用的函数,即回调函数
  @param callback_arg 调用回调函数时,传入的自定义参数
 
  相关联的其他函数为:event_free(), event_add(), event_del(), event_assign()
	   
	 */
	signal_event = evsignal_new(base, SIGINT, signal_cb, (void *)base);
 /**
	函数功能:在需等待事件集中添加一个事件.

	函数原型:

	EVENT2_EXPORT_SYMBOL
	int event_add(struct event *ev, const struct timeval *timeout);

	函数描述:
		当event_assign()或event_new()中指定的事件发生时,或则指定的超时时间到时,调度ev事件的执行。如果timeout设置为NULL, 则一直等待指定的匹配事件。
	参数中的event必须已被event_assign()或则event_new()初始化, 如果事件已经有了一个预定的超时,调用将替换旧的timeout数值.

	参数说明:
	  @param ev 通过event_assign() 或event_new()初始化过的事件对象
	  @param timeout 最大等待事件的时间,  NULL则一直等待
	返回值:
		 0:成功,   -1 :发生错误
	  相关函数参见event_del(), event_assign(), event_new()
  */
	if (!signal_event || event_add(signal_event, NULL)<0) {
    
    
		fprintf(stderr, "Could not create/add a signal event!\n");
		return 1;
	}

/**
函数功能:事件调度循环
函数原型:
EVENT2_EXPORT_SYMBOL
int event_base_dispatch(struct event_base *);
函数描述:
  This loop will run the event base until either there are no more pending or
  active, or until something calls event_base_loopbreak() or
  event_base_loopexit().

参数说明:
  @param base  event_base_new() or event_base_new_with_config()创建的框架上下文对象
返回值:
  @return 0:成功,   -1 :发生错误,1 已退出循环,没有待监测或则激活的事件
   相关函数参见event_base_loop()
 */
	event_base_dispatch(base);//启动异步事件循环监听和回调处理,while(1){}
	//停止运行后清理创建出来的资源
	evconnlistener_free(listener);//清理监听器
	event_free(signal_event);//清理事件
	event_base_free(base);//清理上下文对象

	printf("done\n");
	return 0;
}
/**
函数功能:当监听器监听到一个新连接的时候进行回调(应该是socket的accept成功返回有效的socket描述符后,进行回调,其中fd对应accept返回的文件描述符值).
函数原型:
typedef void (*evconnlistener_cb)(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data);
参数说明:
   @param listener 事件监听器
   @param fd 新的文件描述符,相当于服务器创建的与客户端通信的socket描述符
   @param addr 客户端地址
   @param socklen 客户端地址字节数
   @param user_arg  evconnlistener_new()中传入的自定义参数
 */
static void
listener_cb(struct evconnlistener *listener, evutil_socket_t fd,
    struct sockaddr *sa, int socklen, void *user_data)
{
    
    
	struct event_base *base = user_data;
	struct bufferevent *bev;


/**
函数功能:通过一个已存在的socket描述符创建socket bufferevent
函数原型:struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, int options);

参数说明:
  @param base 关联的框架上下文
  @param fd 关联的文件描述符.类似event_new 
  @param options 0或则BEV_OPT_*标识,一般只使用BEV_OPT_CLOSE_ON_FREE

返回值:
  @return  bufferevent结构体指针, 当出错时返回NULL
  @相关参见 bufferevent_free()
  */
	bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
	if (!bev) {
    
    
		fprintf(stderr, "Error constructing bufferevent!");
		event_base_loopbreak(base);
		return;
	}
	/**
函数功能:设置bufferevent事件的回调函数
函数原型:void bufferevent_setcb(struct bufferevent *bufev,
    bufferevent_data_cb readcb, bufferevent_data_cb writecb,
    bufferevent_event_cb eventcb, void *cbarg);
参数说明:
  @param bufev  需要设置回调函数的bufferevent事件
  @param readcb 数据可读时的回调,可设置为NULL不进行监测
  @param writecb 数据写入成功后进行回调通知,可设置为NULL不进行监测
  @param eventcb 当文件描述符有事件发生时的回调
  @param cbarg 回调调用时传入的自定义参数(readcb, writecb, and errorcb)
  @相关参见  bufferevent_new()
  */
	bufferevent_setcb(bev, NULL, conn_writecb, conn_eventcb, NULL);
	//备注:新建的bufferevent默认写缓存时enable,而读缓存是disable的
	bufferevent_enable(bev, EV_WRITE);//启用bufferevent相关写缓存区
	bufferevent_disable(bev, EV_READ);//禁用bufferevent相关读缓存区

	/**
	函数功能:写数据到bufferevent的写缓存区.
	函数原型:int bufferevent_write(struct bufferevent *bufev,const void *data, size_t size);
	函数描述:
	  The bufferevent_write() function can be used to write data to the file
	  descriptor.  The data is appended to the output buffer and written to the
	  descriptor automatically as it becomes available for writing.
	参数说明:
	  @param bufev  关联的bufferevent
	  @param data 数据指针,从此来源中获取数据,以写入到bufferevent写缓存区 
	  @param size 数据字节数

	返回值:
	  @return 如果成功为0, 失败为-1
	  @相关参见 bufferevent_write_buffer()
	  */
	bufferevent_write(bev, MESSAGE, strlen(MESSAGE));
}

static void
conn_writecb(struct bufferevent *bev, void *user_data)
{
    
    
	struct evbuffer *output = bufferevent_get_output(bev);
	if (evbuffer_get_length(output) == 0) {
    
    
		printf("flushed answer\n");
		bufferevent_free(bev);
	}
}

static void
conn_eventcb(struct bufferevent *bev, short events, void *user_data)
{
    
    
	if (events & BEV_EVENT_EOF) {
    
    
		printf("Connection closed.\n");
	} else if (events & BEV_EVENT_ERROR) {
    
    
		printf("Got an error on the connection: %s\n",
		    strerror(errno));/*XXX win32*/
	}
	/* None of the other events can happen here, since we haven't enabled
	 * timeouts */
	bufferevent_free(bev);
}

static void
signal_cb(evutil_socket_t sig, short events, void *user_data)
{
    
    
	struct event_base *base = user_data;
	struct timeval delay = {
    
     2, 0 };

	printf("Caught an interrupt signal; exiting cleanly in two seconds.\n");

/**
函数功能:指定时间后,退出loop循环 
函数原型:
EVENT2_EXPORT_SYMBOL
int event_base_loopexit(struct event_base *, const struct timeval *);
函数描述: 
   event_base_loop()后调用会正常处理.
参数说明:
  @param base  event_base_new() or event_base_new_with_config()创建的框架上下文对象
  @param tv loop结束前地时间,若指定为NULL,则运行完当前已激活地事件立马结束 
返回值:
  @return 0:成功,   -1 :发生错误
  @相关参见event_base_loopbreak()
 */
 
	event_base_loopexit(base, &delay);
}

Guess you like

Origin blog.csdn.net/skytering/article/details/104253782