• Connection listeners: accepting TCP connections 翻译


    连接监听器:接收TCP连接

    evconnlistener机制给我们提供一种监听和接收TCP连接的方法。在本节中所有的函数和类型都声明在<event2/listener.h>头文件里。

    除非其他地方提到,否则这些在libevent-2.02-alpha首次给出。

    创建和释放 一个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去记下一个新
    的连接到指定listener socket上的TCP连接,当一个新连接到达时,监听器去调用你所指定的回调函数来处理。

    在这两个函数中,
    • base参数是一个event_base,listener用它来监听连接。
    • cb函数是一个回调函数,当一个新连接到达时,listener会调用这个函数。如果cb为NULL的话,系统会把listener当作为未开启状态直到设置cb为止。
    • ptr指针将会被传给cb函数
    • flags参数控制着listener的行为,更多信息请看下面介绍
    • backlog参数控制最大pending连接数,网络栈在任何时候都应该允许未被接受的连接处于等待状态。想了解更多的话,你最好还是察看一下自己系统listen()函数文档。如果backlog是负数的话,libevent将会自己尝试去选一个适当的数(个人认为这个适当的数应该和系统有关)来作为backlog。如果为0,libevent就会假设你自己已经在你所提供的socket上调用了listen()函数。

    这些函数的不同之处在于他们将如何建立他们自己的listener socket,evconnlistener_new()函数只是假设你已经绑定一个socket到指定的想要监听的
    端口上,而且你也把socket作为fd进行传递了。如果你想要libevent去分配绑定socket的话,请调用evconnlistener_new_bind()函数,把sockaddr和其
    长度作为参数传递给它。

    Tip
    当使用evconnlistener_new时,确保你已通过使用evutil_make_socket_nonblocking或者自己去设定正确的选项来
    使你要监听的socket处于非阻塞状态。如果处于阻塞状态,一些未定义的行为很有可能会发生。

    想要释放监听器,用它调用evconnlistner_free()即可。

    可以识别的标志
    以下这flags你可以作为flags参数传递个evconnlistner_new()函数。你也可以通过 | 或运算把任何的参数连在一起传递。

    • LEV_OPT_LEAVE_SOCKETS_BLOCKING
    当一个新连接到达时,这个是默认的flags。设置成非阻塞模式以便于你使用libevent。设置这个flag如果你不想执行此操作。

    • LEV_OPT_CLOSE_ON_FREE
    如果这个选项被设置的话,当你释放监听器时,监听器会自动关闭underlying socket。

    • LEV_OPT_CLOSE_ON_EXEC
    如果这个选项被设置的话,监听器将会对underlying listener socket设置close-on-exec标记,查看平台文档去了解更多有关于fcntl和FD_CLOEXEC的信息。

    • LEV_OPT_REUSEABLE
    在某些平台上是默认的,一旦一个listener socket被关闭掉,其他socket必须过一会才能绑定到这个端口(原话是:once a listener socket is closed, no other socket can bind to the same port until a while has passed.)。设置这个flag将会使libevent把socket标记为可重用的,那么当这个socket被关闭时,其他的socket也是可以打开到监听相同的端口的。

    • LEV_OPT_THREADSAFE 
    为listener分配锁,以确保在多线程的情况是安全的。New in Libevent 2.0.8-rc。

    • LEV_OPT_DISABLED
    初始化listener时,这个就被设置成disabled。 你可以调用evconnistener_enable()来开启它。

    • LEV_OPT_DEFFRRED_ACCEPT
    If possible, tell the kernel to not announce sockets as having been accepted until some data has been received on them, and they are ready for reading. 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. Not all operating systems support this option: on ones that don’t, this option has no effect. New in Libevent 2.1.1-alpha.

    回调函数
    接口

    typedef void (*evconnlistener_cb)(struct evconnlistener *listener,
        evutil_socket_t sock, struct sockaddr *addr, int len, void *ptr);

    当一个新的链接被接受时,这个提供的回调函数将会被调用。listener参数是链接监听器用来监听接收连接的。socket参数是新的socket自己本身。
    addr参数和len参数是已被接收的新的链接的地址和其长度。ptr参数是用户提供的指针,是通过evconnlistener_new()传递进来的。

    开启和关闭一个evconnlistener
    接口

    int evconnlistener_disable(struct evconnlistener *lev);
    int evconnlistener_enable(struct evconnlistener *lev);

    这些函数是暂时开启和关闭对于新连接的监听。

    设置evconnlistener的回调函数
    接口

    void evconnlistener_set_cb(struct evconnlistener *lev,
        evconnlistener_cb cb, void *arg);


    这个函数用来设置回调函数以及回调函数的参数。

    检查evconnlistener
    接口

    evutil_socket_t evconnlistener_get_fd(struct evconnlistener *lev);
    struct event_base *evconnlistener_get_base(struct evconnlistener *lev);

    这些函数返回一个和socket以及event_base关联的listener

    出错检测

    你可以设置一个出错回调函数,当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()为一个listener去设置出错回调函数,每当错误出现时,这个函数都会被调用,它将接收listener作为第一个参数,传递个evconnlistener_new()的指针ptr作为第二个参数。

    实例代码: 一个echo server

    #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 there is 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 input buffer 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 from bufferevent");
            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 a bufferevent 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(int argc, 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 can mess 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 create listener");
                    return 1;
            }
            evconnlistener_set_error_cb(listener, accept_error_cb);
    
            event_base_dispatch(base);
            return 0;
    }


  • 相关阅读:
    ActiveMQ 中的链表
    ActiveMQ 的线程池
    ActiveMQ broker解析
    ActiveMQ broker和客户端之间的确认
    ActiveMQ producer 流量控制
    基于大数据的精准营销与应用场景
    Math.abs为Integer.Min_VALUE返回错误的值
    使用http_load网站压力测试
    可伸缩系统的架构经验
    Tumblr:150亿月浏览量背后的架构挑战
  • 原文地址:https://www.cnblogs.com/java20130722/p/3206804.html
Copyright © 2020-2023  润新知