libevent (hello

编程入门 行业动态 更新时间:2024-10-26 05:29:01

<a href=https://www.elefans.com/category/jswz/34/1743314.html style=libevent (hello"/>

libevent (hello

目录

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 connectionson 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 thecallback is NULL, the listener will be treated as disabled until thecallback 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 theacceptable 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 connectionson 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 thecallback is NULL, the listener will be treated as disabled until thecallback 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 theacceptable 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 nonblockingfile descriptor, and it should already be bound to an appropriateport 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 usedin future calls to event_add() and event_del().  Unlike event_new(), itdoesn't allocate memory itself: it requires that you have alreadyallocated a struct event, probably on the heap.  Doing this willtypically make your code depend on the size of the event structure, andthereby create incompatibility with future versions of Libevent.The easiest way to avoid this problem is just to use event_new() andevent_free() instead.A slightly harder way to future-proof your code is to useevent_get_struct_event_size() to determine the required size of an eventat runtime.Note that it is NOT safe to call this function on an event that isactive or pending.  Doing so WILL corrupt internal data structures inLibevent, and lead to strange, hard-to-diagnose bugs.  You _can_ useevent_assign to change an existing event, but only if it is not activeor pending!The arguments for this function, and the behavior of the events that itmakes, 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 _WIN32if (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;}//申请内存和初始化levlev = 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?//如果设置了这些选项,必须用accept4if (!(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, ...);

更多推荐

libevent (hello

本文发布于:2024-03-23 21:59:10,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1743317.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:libevent

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!