Lib1vent:10链接监听器接受TCP链接

         evconnlistener机制提供了监听并接受TCP链接的方法。除非特别注明,本章的所有函数和类型都在event2/listener.h中声明。

 

一:创建或释放evconnlistener

struct evconnlistener  *evconnlistener_new(struct  event_base  *base,

    evconnlistener_cb  cb,  void *ptr,  unsigned  flags,  int backlog,

    evutil_socket_t  fd);

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);

         两个evconnlistener_new*函数都是分配并返回一个新的链接监听器对象。链接监听器使用event_base,在给定监听socket上监听新的TCP链接的到来。当一个新的链接到来时,它调用给定的回调函数。

         两个函数中,base参数都是监听器用来监听链接的event_base。cb函数是新链接到来时需要调用的回调函数;如果cb为NULL,则直到设置了回调函数为止,监听器相当于被禁用。ptr指针会传递给回调函数。flag参数控制监听器的行为----详见下方。backlog参数表示在任何时刻,网络栈所允许的等待在“未接受”(not-yet-accepted)状态的挂起链接的最大个数。更多细节参考系统listen函数的手册。如果backlog为负数,则Libevent会自行选择一个比较好的backlog值;如果该值为0,则Libevent认为你已经在给定的socket上调用过listen函数了。

         这两个函数的区别在于如何设置监听socket。 evconnlistener_new函数假定已经在希望监听的端口上绑定了socket,也就是fd参数。如果希望Libevent分配并绑定自己的socket,则可以调用evconnlistener_new_bind函数,并且传递一个希望绑定的sockaddr地址及其长度。

         注意:使用evconnlistener_new函数时,确定已经通过evutil_make_socket_nonblocking或者手动设置socket选项,将监听socket设置为非阻塞模式。如果监听socket处于阻塞模式,则会有未定义的行为发生。

 

         释放一个链接监听器,调用evconnlistener_free函数。

 

flags标志位

         下面是可以传递给evconnlistener_new函数flags标志,可以使用or运算将任意数量的标志绑定在一起。

         LEV_OPT_LEAVE_SOCKETS_BLOCKING:默认情况下,当链接监听器接收一个新的到来的socket时,会将其置为非阻塞状态,从而方便Libevent后续的操作。如果设置了该标志,则会禁止这种行为。

         LEV_OPT_CLOSE_ON_FREE:如果设置了该标志,则链接监听器会在释放时关闭底层的socket。

         LEV_OPT_CLOSE_ON_EXEC:设置该标志,链接监听器会在底层监听socket上设置“执行时关闭”(close-on-exec)标志。详细信息可以参考操作系统手册中的fcntl和FD_CLOEXEC部分。

         LEV_OPT_REUSEABLE:默认情况下在某些平台上,当一个监听socket关闭时,只有经过一定时间之后,其他的socket才能绑定到相同的端口上。设置该标志可以使Libevent标志该socket为可重复使用的,因此一旦它关闭了,则其他socket可以在同一个端口上进行监听。

         LEV_OPT_THREADSAFE:为监听器分配锁,因此可以在多线程中安全的使用。

         LEV_OPT_DISABLED:将监听器初始化为禁止状态。可以通过函数evconnlistener_enable手动将其使能。

         LEV_OPT_DEFERRED_ACCEPT:设置该标志,则告知内核,直到接收到对端数据,并且本地socket准备好读取之前,不通知socket接收新链接。如果网络协议并非以客户端传递数据为开始,则不要使用该标志,因为这样有时会使得内核永远不通知新链接的到来。并非所有系统都支持该标志:在那些不支持的系统上,该标志没有任何作用。

 

链接监听器的回调函数

typedef void  (*evconnlistener_cb)(struct  evconnlistener  *listener,

    evutil_socket_t  sock,  struct sockaddr  *addr,  int len,  void  *ptr);

         当新的链接到来时,就会调用回调函数。其中的Listener参数就是接收链接的链接监听器,sock参数就是新的socket本身。addr和len就是链接对端的地址及其长度。ptr就是用户提供的传递给evconnlistener_new函数的参数。

        

二:将evconnlistener使能和禁止

int  evconnlistener_disable(struct  evconnlistener *lev);

int  evconnlistener_enable(struct  evconnlistener *lev);

         这些函数可以将evconnlistener暂时的使能或禁止。

 

三:调整evconnlistener的回调函数

void  evconnlistener_set_cb(struct  evconnlistener  *lev,

                    evconnlistener_cb  cb,  void *arg);

         该函数改变evconnlistener的回调函数及其参数。

 

四:监测evconnlistener

evutil_socket_t evconnlistener_get_fd(struct  evconnlistener  *lev);

struct event_base  *evconnlistener_get_base(struct  evconnlistener  *lev);

         这些函数返回监听器的socket和event_base。

        

五:检测错误

         可以在监听器上设置错误回调函数,当accept调用失败时,就会调用该函数。当你遇到一个错误,而且解决该错误之前进程会一直锁住的话,这种机制是很有用的。

typedef void (*evconnlistener_errorcb)(struct  evconnlistener  *lis,  void*ptr);

void  evconnlistener_set_error_cb(struct  evconnlistener  *lev,

    evconnlistener_errorcb  errorcb);

         如果使用evconnlistener_set_error_cb函数设置了错误回调函数,则在监听器上,每次发生错误时都会调用该函数。监听器会作为第一个参数,传递给evconnlistener_new的ptr作为第二个参数。

 

六:例子:回显服务器:

#include <event2/listener.h>

#include <event2/bufferevent.h>

#include <event2/buffer.h>

 

#include <arpa/inet.h>

 

#include <string.h>

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

 

static void

echo_read_cb(struct bufferevent  *bev,  void *ctx)

{

        /* This callback is invoked when thereis data to read on bev. */

        struct  evbuffer  *input = bufferevent_get_input(bev);

        struct  evbuffer  *output = bufferevent_get_output(bev);

 

        /* Copy all the data from the inputbuffer to the output buffer. */

        evbuffer_add_buffer(output,  input);

}

 

static void

echo_event_cb(struct bufferevent  *bev,  short events,  void  *ctx)

{

        if (events & BEV_EVENT_ERROR)

                perror("Error frombufferevent");

        if (events & (BEV_EVENT_EOF |BEV_EVENT_ERROR)) {

                bufferevent_free(bev);

        }

}

 

static void

accept_conn_cb(struct evconnlistener  *listener,

    evutil_socket_t  fd,  struct sockaddr  *address,  int  socklen,

    void  *ctx)

{

        /* We got a new connection! Set up abufferevent for it. */

        struct  event_base*base = evconnlistener_get_base(listener);

        struct  bufferevent  *bev = bufferevent_socket_new(

                base,  fd,  BEV_OPT_CLOSE_ON_FREE);

 

        bufferevent_setcb(bev,  echo_read_cb,  NULL,  echo_event_cb, NULL);

 

        bufferevent_enable(bev,  EV_READ|EV_WRITE);

}

 

static void

accept_error_cb(struct evconnlistener  *listener,  void  *ctx)

{

        struct  event_base  *base = evconnlistener_get_base(listener);

        int err = EVUTIL_SOCKET_ERROR();

        fprintf(stderr, "Got an error %d(%s) on the listener. "

                "Shutting down.\n",err, evutil_socket_error_to_string(err));

 

        event_base_loopexit(base, NULL);

}

 

int

main(intargc, char **argv)

{

        struct  event_base  *base;

        struct  evconnlistener  *listener;

        struct  sockaddr_in  sin;

 

        int  port = 9876;

 

        if (argc > 1) {

                port = atoi(argv[1]);

        }

        if (port<=0 || port>65535) {

                puts("Invalid port");

                return 1;

        }

 

        base = event_base_new();

        if (!base) {

                puts("Couldn't open event base");

                return 1;

        }

 

        /* Clear the sockaddr before using it,in case there are extra

         * platform-specific fields that canmess us up. */

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

        /* This is an INET address */

        sin.sin_family = AF_INET;

        /* Listen on 0.0.0.0 */

        sin.sin_addr.s_addr = htonl(0);

        /* Listen on the given port. */

        sin.sin_port = htons(port);

 

        listener =evconnlistener_new_bind(base,  accept_conn_cb, NULL,

           LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,  -1,

            (struct sockaddr*)&sin,sizeof(sin));

        if (!listener) {

                perror("Couldn't createlistener");

                return 1;

        }

        evconnlistener_set_error_cb(listener,  accept_error_cb);

 

        event_base_dispatch(base);

        return 0;

}

转载于:https://www.cnblogs.com/gqtcgq/p/7247253.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值