zoukankan      html  css  js  c++  java
  • 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. ",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;

    }

  • 相关阅读:
    20199106 2019-2020-2 《网络攻防实践》第三周作业
    Vulnhub
    NEEPU-CTF 2021 Web后四题Writeup
    Vulnhub
    [VNCTF 2021]naive题解
    F5杯 Web部分题目Writeup by atao
    CTFSHOW SSTI 刷题
    C语言文件
    函数+进制转换器
    C语言知识点小结
  • 原文地址:https://www.cnblogs.com/gqtcgq/p/7247253.html
Copyright © 2011-2022 走看看