libevent (hello-world代码源码)----evconnlistener_new_bind

目录

if (!base)

memset(&sin, 0, sizeof(sin))

sin.sin_family = AF_INET

sin.sin_port = htons(PORT)

evconnlistener_new_bind

listener.h

event.h

listener.c

其他:


event_base_new中了解了event_base_new()函数的原理,初始化了一堆东西。现在继续向下看:

if (!base)

	if (!base) {
		fprintf(stderr, "Could not initialize libevent!\n");
		return 1;
	}

如果创建失败,就退出,没啥好说的。event_base_new()中event_config_new()失败,会返回NULL。而event_config_new()中event_mm_calloc_()失败会返回NULL。

/** Allocate memory initialized to zero.
 *
 * @return On success, return a pointer to (count * size) newly allocated
 *     bytes, initialized to zero.
 *     On failure, or if the product would result in an integer overflow,
 *     set errno to ENOMEM and return NULL.
 *     If either arguments are 0, simply return NULL.
 */
EVENT2_EXPORT_SYMBOL
void *event_mm_calloc_(size_t count, size_t size);

event_mm_calloc_()的作用是分配初始化为零的内存:
@返回成功后,返回一个指向(count*size)新分配字节的指针,初始化为零。失败时,或者如果乘积将导致整数溢出,请将errno设置为ENOMEM并返回NULL。如果任一参数为0,只需返回NULL即可。

memset(&sin, 0, sizeof(sin))

memset(&sin, 0, sizeof(sin));

初始化sin (描述网络连接参数的结构体,里面包含ip、端口、协议信息)

memset函数为初始化函数,可以将一段连续的内存初始化为某个值,memset以字节为单位进行初始化。(ps:int 占4字节)

其中,sin的ip地址已经被初始化为0,0,0,0

sin.sin_family = AF_INET

sin.sin_family = AF_INET;

AF_INET系列是 IPv4 的地址系列。用于指定套接字可以与之通信的地址类型(在本例中为Internet协议ipv4地址)。

创建套接字时,必须指定其地址族,然后只能使用该类型的地址与套接字。例如,Linux内核支持其他29个地址系列,例如UNIX(AF_UNIX)套接字和IPX(AF_IPX),以及与IRDA和蓝牙(AF_IRDA和AF_BLUETOOTH)的通信。

在大多数情况下,使用AF_INET进行套接字编程是最安全的选择。 Internet协议v6地址也有AF_INET6。

sin.sin_port = htons(PORT)

sin.sin_port = htons(PORT);

设置端口号,其中

htons是将整型变量从主机字节顺序转变成网络字节顺序, 就是整数在地址空间存储方式变为高位字节存放在内存的低地址处。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释,网络字节顺序采用big-endian排序方式。

evconnlistener_new_bind

	listener = evconnlistener_new_bind(base, listener_cb, (void *)base,
	    LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE, -1,
	    (struct sockaddr*)&sin,
	    sizeof(sin));

listener.h

分配一个新的evconnlistener对象来侦听给定地址上的传入TCP连接。
@param base将侦听器与之关联的事件库。
@param cb是新连接到达时要调用的回调。如果回调为空,则侦听器将被视为禁用,直到设置回调。
@param ptr是用户提供的指向回调的指针。
@param 标志任何数量的LEV_ OPT_*
@param  backlog传递给listen()调用,以确定可接受的连接backlog的长度。设置为-1表示合理的默认值。
@param addr侦听连接的地址。
@param socklen地址的长度。

/**
   Allocate a new evconnlistener object to listen for incoming TCP connections
   on a given address.

   @param base The event base to associate the listener with.
   @param cb A callback to be invoked when a new connection arrives. If the
      callback is NULL, the listener will be treated as disabled until the
      callback is set.
   @param ptr A user-supplied pointer to give to the callback.
   @param flags Any number of LEV_OPT_* flags
   @param backlog Passed to the listen() call to determine the length of the
      acceptable connection backlog.  Set to -1 for a reasonable default.
   @param addr The address to listen for connections on.
   @param socklen The length of the address.
 */
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);

/**
分配一个新的evconnlistener对象,以侦听给定文件描述符上的传入TCP连接。
@param base将侦听器与之关联的事件库。
@param cb是新连接到达时要调用的回调。如果回调为空,则在设置回调之前,侦听器将被视为禁用。
@param ptr是用户提供的指向回调的指针。
@param标志任何数量的LEV_ OPT_
@param backlog传递给listen()调用,以确定可接受的连接backlog的长度。设置为-1表示合理的默认值。如果套接字已在侦听,则设置为0。
@param fd监听的文件描述符。它必须是非阻塞文件描述符,并且应该已经绑定到适当的端口和地址。
*/

/**
   Allocate a new evconnlistener object to listen for incoming TCP connections
   on a given file descriptor.

   @param base The event base to associate the listener with.
   @param cb A callback to be invoked when a new connection arrives.  If the
      callback is NULL, the listener will be treated as disabled until the
      callback is set.
   @param ptr A user-supplied pointer to give to the callback.
   @param flags Any number of LEV_OPT_* flags
   @param backlog Passed to the listen() call to determine the length of the
      acceptable connection backlog.  Set to -1 for a reasonable default.
      Set to 0 if the socket is already listening.
   @param fd The file descriptor to listen on.  It must be a nonblocking
      file descriptor, and it should already be bound to an appropriate
      port and address.
*/
EVENT2_EXPORT_SYMBOL
struct evconnlistener *evconnlistener_new(struct event_base *base,
    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
    evutil_socket_t fd);

 这是传入的flags的一些定义:

/** Flag: Indicates that we should not make incoming sockets nonblocking
 * before passing them to the callback. */
//标志:表示在将传入套接字传递到回调之前,不应使其成为非阻塞的。
#define LEV_OPT_LEAVE_SOCKETS_BLOCKING	(1u<<0)
/** Flag: Indicates that freeing the listener should close the underlying
 * socket. */
//标志:表示释放侦听器应关闭基础套接字。
#define LEV_OPT_CLOSE_ON_FREE		(1u<<1)
/** Flag: Indicates that we should set the close-on-exec flag, if possible */
//标志:如果可能的话,表示我们应该设置close-on-exec标志
#define LEV_OPT_CLOSE_ON_EXEC		(1u<<2)
/** Flag: Indicates that we should disable the timeout (if any) between when
 * this socket is closed and when we can listen again on the same port. */
//标志:表示我们应该在关闭此套接字和可以在同一端口上再次侦听之间禁用超时(如果有)
#define LEV_OPT_REUSEABLE		(1u<<3)
/** Flag: Indicates that the listener should be locked so it's safe to use
 * from multiple threadcs at once. */
//指示应锁定侦听器,以便同时从多个线程安全使用。
#define LEV_OPT_THREADSAFE		(1u<<4)
/** Flag: Indicates that the listener should be created in disabled
 * state. Use evconnlistener_enable() to enable it later. */
//标志:指示应在禁用状态下创建侦听器。稍后使用evconnlister_enable()启用它。
#define LEV_OPT_DISABLED		(1u<<5)
/** Flag: Indicates that the listener should defer accept() until data is
 * available, if possible.  Ignored on platforms that do not support this.
 *
 * This option can help performance for protocols where the client transmits
 * immediately after connecting.  Do not use this option if your protocol
 * _doesn't_ start out with the client transmitting data, since in that case
 * this option will sometimes cause the kernel to never tell you about the
 * connection.
 *
 * This option is only supported by evconnlistener_new_bind(): it can't
 * work with evconnlistener_new_fd(), since the listener needs to be told
 * to use the option before it is actually bound.
 */
/*
标志:表示侦听器应推迟accept(),直到数据可用为止(如果可能)。在不支持此功能的平台上忽略。
此选项可以帮助客户端在连接后立即传输协议的性能。如果您的协议_。
此选项仅由evconnlistener_new_bind()支持:它不能与evconnlistener_ new_ fd()一起使用,因为需要告知侦听器在实际绑定之前使用此选项
*/
#define LEV_OPT_DEFERRED_ACCEPT		(1u<<6)
/** Flag: Indicates that we ask to allow multiple servers (processes or
 * threads) to bind to the same port if they each set the option. 
 * 
 * SO_REUSEPORT is what most people would expect SO_REUSEADDR to be, however
 * SO_REUSEPORT does not imply SO_REUSEADDR.
 *
 * This is only available on Linux and kernel 3.9+
 */
/*
标志:表示我们要求允许多个服务器(进程或线程)绑定到同一个端口,如果它们都设置了该选项。

REUSEPORT是大多数人期望的SO_REUSEADDR,
然而,SO_ REUSEPORT并不意味着SO_REUSEADDR。

这仅在Linux和内核3.9+上正常工作。
*/
#define LEV_OPT_REUSEABLE_PORT		(1u<<7)
/** Flag: Indicates that the listener wants to work only in IPv6 socket.
 *
 * According to RFC3493 and most Linux distributions, default value is to
 * work in IPv4-mapped mode. If there is a requirement to bind same port
 * on same ip addresses but different handlers for both IPv4 and IPv6,
 * it is required to set IPV6_V6ONLY socket option to be sure that the
 * code works as expected without affected by bindv6only sysctl setting in
 * system.
 *
 * This socket option also supported by Windows.
 */
/*
标志:表示侦听器只想在IPv6套接字中工作。
根据RFC3493和大多数Linux发行版,默认值是在IPv4映射模式下工作。
如果需要为IPv4和IPv6绑定相同ip地址上的相同端口,但处理程序不同,则需要设置IPv6_。
Windows也支持此套接字选项
*/
#define LEV_OPT_BIND_IPV6ONLY		(1u<<8)

event.h

在其中的一个关键函数:

/**
准备要添加的新的、已分配的事件结构。
函数event_assign()准备事件结构ev,以便在将来调用event_ add()和eventU del()时使用。与event_new()不同,它本身不分配内存:它要求您已经分配了一个struct事件,可能是在堆上。这样做通常会使代码取决于事件结构的大小,从而导致与Libevent的未来版本不兼容。
避免此问题的最简单方法就是使用event_new()和event_ free()。
要证明代码的未来性,一种稍微困难的方法是使用event_get_struct_event_ size()来确定运行时所需的事件大小。
请注意,在活动或挂起的事件上调用此函数是不安全的。这样做会破坏Libevent中的内部数据结构,并导致奇怪的、难以诊断的错误!
此函数的参数及其生成的事件行为与event_new()相同。
@param ev:要修改的事件结构
@param base ev应连接到的事件库。
@param fd:要监视的文件描述符
@参数事件:需要监视的事件;可以是EV_ READ和/或EV_
@事件发生时要调用的param回调函数
@param callback_arg:要传递给回调函数的参数
@如果成功,则返回0;如果参数无效,则返回-1。
@请参见event_new()、event_ add()、event_del()、event_base_once()、evelt_get_struct_
*/

/**
  Prepare a new, already-allocated event structure to be added.

  The function event_assign() prepares the event structure ev to be used
  in future calls to event_add() and event_del().  Unlike event_new(), it
  doesn't allocate memory itself: it requires that you have already
  allocated a struct event, probably on the heap.  Doing this will
  typically make your code depend on the size of the event structure, and
  thereby create incompatibility with future versions of Libevent.

  The easiest way to avoid this problem is just to use event_new() and
  event_free() instead.

  A slightly harder way to future-proof your code is to use
  event_get_struct_event_size() to determine the required size of an event
  at runtime.

  Note that it is NOT safe to call this function on an event that is
  active or pending.  Doing so WILL corrupt internal data structures in
  Libevent, and lead to strange, hard-to-diagnose bugs.  You _can_ use
  event_assign to change an existing event, but only if it is not active
  or pending!

  The arguments for this function, and the behavior of the events that it
  makes, are as for event_new().

  @param ev an event struct to be modified
  @param base the event base to which ev should be attached.
  @param fd the file descriptor to be monitored
  @param events desired events to monitor; can be EV_READ and/or EV_WRITE
  @param callback callback function to be invoked when the event occurs
  @param callback_arg an argument to be passed to the callback function

  @return 0 if success, or -1 on invalid arguments.

  @see event_new(), event_add(), event_del(), event_base_once(),
    event_get_struct_event_size()
  */
EVENT2_EXPORT_SYMBOL
int event_assign(struct event *, struct event_base *, evutil_socket_t, short, event_callback_fn, void *);

listener.c

struct evconnlistener *
evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
    void *ptr, unsigned flags, int backlog, const struct sockaddr *sa,
    int socklen)
{
	struct evconnlistener *listener;
	evutil_socket_t fd;
	int on = 1;
	int family = sa ? sa->sa_family : AF_UNSPEC;
	int socktype = SOCK_STREAM | EVUTIL_SOCK_NONBLOCK;

	if (backlog == 0)
		return NULL;

	if (flags & LEV_OPT_CLOSE_ON_EXEC)
		socktype |= EVUTIL_SOCK_CLOEXEC;

	fd = evutil_socket_(family, socktype, 0);
	if (fd == -1)
		return NULL;

	if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&on, sizeof(on))<0)
		goto err;

	if (flags & LEV_OPT_REUSEABLE) {
		if (evutil_make_listen_socket_reuseable(fd) < 0)
			goto err;
	}

	if (flags & LEV_OPT_REUSEABLE_PORT) {
		if (evutil_make_listen_socket_reuseable_port(fd) < 0)
			goto err;
	}

	if (flags & LEV_OPT_DEFERRED_ACCEPT) {
		if (evutil_make_tcp_listen_socket_deferred(fd) < 0)
			goto err;
	}

	if (flags & LEV_OPT_BIND_IPV6ONLY) {
		if (evutil_make_listen_socket_ipv6only(fd) < 0)
			goto err;
	}

	if (sa) {
		if (bind(fd, sa, socklen)<0)
			goto err;
	}

	listener = evconnlistener_new(base, cb, ptr, flags, backlog, fd);
	if (!listener)
		goto err;

	return listener;
err:
	evutil_closesocket(fd);
	return NULL;
}

创建了个fd,然后检查fd是否合法,绑定端口号,调用evconnlistener_new()函数。

下面是evconnlistener_new()使用到的一些结构体:

struct evconnlistener_ops {
	int (*enable)(struct evconnlistener *);
	int (*disable)(struct evconnlistener *);
	void (*destroy)(struct evconnlistener *);
	void (*shutdown)(struct evconnlistener *);
	evutil_socket_t (*getfd)(struct evconnlistener *);
	struct event_base *(*getbase)(struct evconnlistener *);
};
struct evconnlistener {
	const struct evconnlistener_ops *ops;
	void *lock;
	evconnlistener_cb cb;
	evconnlistener_errorcb errorcb;
	void *user_data;
	unsigned flags;
	short refcnt;
	int accept4_flags;
	unsigned enabled : 1;
};
struct evconnlistener_event {
	struct evconnlistener base;
	struct event listener;
};
static const struct evconnlistener_ops evconnlistener_event_ops = {
	event_listener_enable,
	event_listener_disable,
	event_listener_destroy,
	NULL, /* shutdown */
	event_listener_getfd,
	event_listener_getbase
};

 下面是evconnlistener_new()函数的实现:

struct evconnlistener *
evconnlistener_new(struct event_base *base,
    evconnlistener_cb cb, void *ptr, unsigned flags, int backlog,
    evutil_socket_t fd)
{
	struct evconnlistener_event *lev;

#ifdef _WIN32
	if (base && event_base_get_iocp_(base)) {
		const struct win32_extension_fns *ext =
			event_get_win32_extension_fns_();
		if (ext->AcceptEx && ext->GetAcceptExSockaddrs)
			return evconnlistener_new_async(base, cb, ptr, flags,
				backlog, fd);
	}
#endif
	// listen 最主要的工作就是申请和初始化接收队列,包括全连接队列和半连接队列
	//其中全连接队列是一个链表,而半连接队列由于需要快速的查找,所以使用的是一个哈希表
	//(其实半连接队列更准确的的叫法应该叫半连接哈希表)。
	if (backlog > 0) {
		if (listen(fd, backlog) < 0)
			return NULL;
	} else if (backlog < 0) {
		if (listen(fd, 128) < 0)
			return NULL;
	}
	//申请内存和初始化lev
	lev = mm_calloc(1, sizeof(struct evconnlistener_event));
	if (!lev)
		return NULL;
	//初始化参数 
	lev->base.ops = &evconnlistener_event_ops;//绑定evconnlistener_event_ops(静态变量)都绑定这一个
	lev->base.cb = cb;//绑定回调函数,有新的tcp连接时触发
	lev->base.user_data = ptr;//用户自定义的,传到回调里的参数
	//LEV_OPT_CLOSE_ON_FREE:表示释放侦听器应关闭基础套接字
	//LEV_OPT_REUSEABLE:表示我们应该在关闭此套接字和可以在同一端口上再次侦听之间禁用超时(如果有)
	lev->base.flags = flags;//设置falgs,例如hello-world.c中设置了LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_FREE
	/*
	   此evbuffer上也有一个引用计数。
       当引用计数达到0时,缓冲区被销毁。
       使用evbuffer_incref和evbuffer_dectf_and_unlock和evbuffer_free进行操作。
	*/
	lev->base.refcnt = 1;// 引用计数 

	lev->base.accept4_flags = 0;//accept 和 accept4?
	//如果设置了这些选项,必须用accept4
	if (!(flags & LEV_OPT_LEAVE_SOCKETS_BLOCKING))
		lev->base.accept4_flags |= EVUTIL_SOCK_NONBLOCK;
	if (flags & LEV_OPT_CLOSE_ON_EXEC)
		lev->base.accept4_flags |= EVUTIL_SOCK_CLOEXEC;
	//指示应锁定侦听器,以便同时从多个线程安全使用。
	if (flags & LEV_OPT_THREADSAFE) {
		EVTHREAD_ALLOC_LOCK(lev->base.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
	}
	//给lev->listener事件(event)赋值
	///EV_PERSIST:持久事件:激活时不会自动删除。
	//EV_READ:等待套接字或FD变得可读
	event_assign(&lev->listener, base, fd, EV_READ|EV_PERSIST,
	    listener_read_cb, lev);
	//LEV_OPT_DISABLED:应在禁用状态下创建侦听器。使用evconnlister_enable()启用它。
	if (!(flags & LEV_OPT_DISABLED))
	    evconnlistener_enable(&lev->base);

	return &lev->base;
}

 evconnlistener_new_bind还是比较好懂的,如果忽略event_assign函数的话。下一篇就看event_assign函数了。

其他:

服务端经典代码:参考为什么服务端程序都需要先 listen 一下?

int main(int argc, char const *argv[])
{
 int fd = socket(AF_INET, SOCK_STREAM, 0);
 bind(fd, ...);
 listen(fd, 128);
 accept(fd, ...);
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《libevent参考手册(中文版)_libevent-2.1.5.pdf》是libevent事件驱动库的参考手册的中文翻译版本,主要是为中国的开发者提供方便和参考。libevent是一个开源的高性能网络库,广泛应用于服务器程序的开发中。 该手册详细介绍了libevent库的使用方法和相关的API函数,帮助开发者们更好地理解和使用这个库。手册的内容包括库的基本概述、事件循环、定时器、信号处理、缓冲区管理等各个方面。通过阅读手册,开发者可以学习如何使用libevent库来构建高效可靠的网络应用程序。 手册中的每个功能点都有详细的说明和示例代码,让开发者可以更快地上手使用libevent库。无论是初学者还是有一定经验的开发者,都可以通过这份参考手册快速入门和掌握libevent库的使用。 此外,手册还包含了常见问题的解答与技巧,帮助开发者在使用libevent时遇到问题时能够解决,提高开发效率。 总之,《libevent参考手册(中文版)_libevent-2.1.5.pdf》为开发者们提供了掌握libevent库的必备工具和参考资料。通过学习手册中的内容,开发者们可以更好地利用libevent库来实现高性能、高可靠性的网络应用程序。 ### 回答2: libevent是一个事件驱动的编程库,用于开发高效的网络通信应用程序。它提供了一个简单而灵活的接口,能够实现异步的、非阻塞的事件处理机制。 libevent参考手册中文版_libevent-2.1.5.pdf是libevent官方提供的参考手册的中文翻译版本。这个手册详细介绍了libevent库的各个方面,包括核心概念、函数接口、使用方法等。通过阅读这个手册,用户可以了解到如何使用libevent来开发高效的网络应用程序。 这个手册首先介绍了libevent的设计理念和特点,包括事件驱动的编程模型、事件循环机制等。然后详细介绍了libevent库中的各个功能模块,包括事件基、事件、缓冲区、定时器等。对于每个功能模块,手册提供了对应的API接口说明和使用示例。 除了功能模块的介绍外,手册还包括了一些高级话题的讨论,例如多线程编程、信号处理、SSL支持等。这些内容对于开发复杂的网络应用程序非常有用。 总的来说,libevent参考手册中文版_libevent-2.1.5.pdf是libevent库的官方文档的中文翻译版本。通过阅读这个手册,用户可以全面了解libevent库的使用方法和原理,从而能够更好地利用libevent开发高效的网络通信应用程序。 ### 回答3: libevent是一个事件驱动的网络编程库,它简化了网络应用程序的开发过程。libevent支持多种操作系统平台,包括Windows、Linux和Unix,并提供了跨平台的API接口。 libevent参考手册中文版_libevent-2.1.5.pdf是libevent库的中文参考手册,它详细介绍了libevent库的各个功能和使用方法,对于开发人员来说是非常有用的工具。 这个参考手册主要包含以下几个部分: 1. 简介:介绍了libevent库的概要和功能特点,以及它在网络编程中的作用。 2. 安装和配置:详细说明了如何安装和配置libevent库,包括下载源代码、编译安装和设置环境变量等步骤。 3. API接口:列举了libevent库提供的各种API接口,并对每个接口进行了详细的说明。开发人员可以根据自己的需求选择合适的接口来实现网络应用程序。 4. 示例代码:提供了一些实用的示例代码,演示了如何使用libevent库来开发各种类型的网络应用程序,如服务器、客户端和代理等。 5. 常见问题解答:收集了一些常见的问题和解答,帮助开发人员在使用libevent库过程中遇到问题时能够及时获取帮助。 通过学习和参考libevent参考手册,开发人员可以更好地理解和掌握libevent库的使用方法,提高网络应用程序的开发效率。同时,中文版的参考手册方便了不擅长英文阅读的开发人员使用libevent库进行开发工作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值