美文网首页
Bufferevents:advanced topics

Bufferevents:advanced topics

作者: 食梦狸猫 | 来源:发表于2019-03-20 21:07 被阅读0次

    成对bufferevents

    我们可以创建一对bufferevents,使其中一端所有写的数据都能在另一端收到。而且没有真正用到套接字

    int bufferevent_pair_new(struct event_base *base, int options,
        struct bufferevent *pair[2]);
    

    这个函数设置pair[0]和pair[1]是一对互相连接的bufferevents对。同时也支持所有参数,除了BEV_OPT_CLOSE_ON_FREE没实际作用,BEV_OP_DEFER_CALLBACKS一直保持。

    释放掉其中一端的bufferevent不会自动释放另一端的bufferevent或者造成EOF事件,这只会让释放的那端不可连接,而且不能成功读写。

    如果你想获得另一端的bufferevent,可以调用

    struct bufferevent *bufferevent_pair_get_partner(struct bufferevent *bev)
    

    bufferevents过滤器

    如果我们想通过一个bufferevent传输数据并且进行一些过滤。

    enum bufferevent_filter_result {
            BEV_OK = 0,
            BEV_NEED_MORE = 1,
            BEV_ERROR = 2
    };
    typedef enum bufferevent_filter_result (*bufferevent_filter_cb)(
        struct evbuffer *source, struct evbuffer *destination, ev_ssize_t dst_limit,
        enum bufferevent_flush_mode mode, void *ctx);
    
    
    struct bufferevent *bufferevent_filter_new(struct bufferevent *underlying,
            bufferevent_filter_cb input_filter,
            bufferevent_filter_cb output_filter,
            int options,
            void (*free_context)(void *),
            void *ctx);
    
    

    在创建bufferevent过滤器时,会有一个underlying的bufferevent,所有进入underlying的数据都会进过input_filter过滤,同理,输出的也会通过output_filter过滤。但是给一个bufferevent创建过滤器的话,会屏蔽掉bufferevent自己的回调函数。

    设置最大读写量

    int bufferevent_set_max_single_read(struct bufferevent *bev, size_t size);
    int bufferevent_set_max_single_write(struct bufferevent *bev, size_t size);
    
    ev_ssize_t bufferevent_get_max_single_read(struct bufferevent *bev);
    ev_ssize_t bufferevent_get_max_single_write(struct bufferevent *bev);
    
    

    Bufferevents和SSL

    Bufferevents可以使用OpenSSL库来支持SSL/TLS安全传输层。

    • 设置和使用一个OpenSSL-based bufferevent
    enum bufferevent_ssl_state {
            BUFFEREVENT_SSL_OPEN = 0,
            BUFFEREVENT_SSL_CONNECTING = 1,
            BUFFEREVENT_SSL_ACCEPTING = 2
    };
    
    struct bufferevent *
    bufferevent_openssl_filter_new(struct event_base *base,
        struct bufferevent *underlying,
        SSL *ssl,
        enum bufferevent_ssl_state state,
        int options);
    
    struct bufferevent *
    bufferevent_openssl_socket_new(struct event_base *base,
        evutil_socket_t fd,
        SSL *ssl,
        enum bufferevent_ssl_state state,
        int options);
    

    我们可以创造两种SSLbufferevents:一种有过滤器可以和其他下层bufferevent传输的的bufferevent,一种有socket的bufferevent可以让OpenSSL和互联网直接连接的bufferevent

    SSL *ctx = bufferevent_openssl_get_ssl(bev);
    
    /*
     * SSL_RECEIVED_SHUTDOWN tells SSL_shutdown to act as if we had already
     * received a close notify from the other end.  SSL_shutdown will then
     * send the final close notify in reply.  The other end will receive the
     * close notify and send theirs.  By this time, we will have already
     * closed the socket and the other end's real close notify will never be
     * received.  In effect, both sides will think that they have completed a
     * clean shutdown and keep their sessions valid.  This strategy will fail
     * if the socket is not ready for writing, in which case this hack will
     * lead to an unclean shutdown and lost session on the other end.
     */
    SSL_set_shutdown(ctx, SSL_RECEIVED_SHUTDOWN);
    SSL_shutdown(ctx);
    bufferevent_free(bev);
    
    SSL *bufferevent_openssl_get_ssl(struct bufferevent *bev);
    

    如果bev是个OpenSSL bufferevent这个函数返回SSL对象

    unsigned long bufferevent_get_openssl_error(struct bufferevent *bev);
    
    int bufferevent_ssl_renegotiate(struct bufferevent *bev);
    
    int bufferevent_openssl_get_allow_dirty_shutdown(struct bufferevent *bev);
    void bufferevent_openssl_set_allow_dirty_shutdown(struct bufferevent *bev,
        int allow_dirty_shutdown);
    
    

    例子

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    #include <openssl/ssl.h>
    #include <openssl/err.h>
    #include <openssl/rand.h>
    
    #include <event.h>
    #include <event2/listener.h>
    #include <event2/bufferevent_ssl.h>
    
    static void
    ssl_readcb(struct bufferevent * bev, void * arg)
    {
        struct evbuffer *in = bufferevent_get_input(bev);
    
        printf("Received %zu bytes\n", evbuffer_get_length(in));
        printf("----- data ----\n");
        printf("%.*s\n", (int)evbuffer_get_length(in), evbuffer_pullup(in, -1));
    
        bufferevent_write_buffer(bev, in);
    }
    
    static void
    ssl_acceptcb(struct evconnlistener *serv, int sock, struct sockaddr *sa,
                 int sa_len, void *arg)
    {
        struct event_base *evbase;
        struct bufferevent *bev;
        SSL_CTX *server_ctx;
        SSL *client_ctx;
    
        server_ctx = (SSL_CTX *)arg;
        client_ctx = SSL_new(server_ctx);
        evbase = evconnlistener_get_base(serv);
    
        bev = bufferevent_openssl_socket_new(evbase, sock, client_ctx,
                                             BUFFEREVENT_SSL_ACCEPTING,
                                             BEV_OPT_CLOSE_ON_FREE);
    
        bufferevent_enable(bev, EV_READ);
        bufferevent_setcb(bev, ssl_readcb, NULL, NULL, NULL);
    }
    static SSL_CTX *
    evssl_init(void)
    {
        SSL_CTX  *server_ctx;
    
        /* Initialize the OpenSSL library */
        SSL_load_error_strings();
        SSL_library_init();
        /* We MUST have entropy, or else there's no point to crypto. */
        if (!RAND_poll())
            return NULL;
    
        server_ctx = SSL_CTX_new(SSLv23_server_method());
    
        if (! SSL_CTX_use_certificate_chain_file(server_ctx, "cert") ||
            ! SSL_CTX_use_PrivateKey_file(server_ctx, "pkey", SSL_FILETYPE_PEM)) {
            puts("Couldn't read 'pkey' or 'cert' file.  To generate a key\n"
               "and self-signed certificate, run:\n"
               "  openssl genrsa -out pkey 2048\n"
               "  openssl req -new -key pkey -out cert.req\n"
               "  openssl x509 -req -days 365 -in cert.req -signkey pkey -out cert");
            return NULL;
        }
        SSL_CTX_set_options(server_ctx, SSL_OP_NO_SSLv2);
    
        return server_ctx;
    }
    int
    main(int argc, char **argv)
    {
        SSL_CTX *ctx;
        struct evconnlistener *listener;
        struct event_base *evbase;
        struct sockaddr_in sin;
    
        memset(&sin, 0, sizeof(sin));
        sin.sin_family = AF_INET;
        sin.sin_port = htons(9999);
        sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
    
        ctx = evssl_init();
        if (ctx == NULL)
            return 1;
        evbase = event_base_new();
        listener = evconnlistener_new_bind(
                             evbase, ssl_acceptcb, (void *)ctx,
                             LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024,
                             (struct sockaddr *)&sin, sizeof(sin));
    
        event_base_loop(evbase, 0);
    
        evconnlistener_free(listener);
        SSL_CTX_free(ctx);
    
        return 0;
    }
    

    线程安全OpenSSL

    例子

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <pthread.h>
    #include <openssl/ssl.h>
    #include <openssl/crypto.h>
    
    pthread_mutex_t * ssl_locks;
    int ssl_num_locks;
    
    /* Implements a thread-ID function as requied by openssl */
    static unsigned long
    get_thread_id_cb(void)
    {
        return (unsigned long)pthread_self();
    }
    
    static void
    thread_lock_cb(int mode, int which, const char * f, int l)
    {
        if (which < ssl_num_locks) {
            if (mode & CRYPTO_LOCK) {
                pthread_mutex_lock(&(ssl_locks[which]));
            } else {
                pthread_mutex_unlock(&(ssl_locks[which]));
            }
        }
    }
    int
    init_ssl_locking(void)
    {
        int i;
    
        ssl_num_locks = CRYPTO_num_locks();
        ssl_locks = malloc(ssl_num_locks * sizeof(pthread_mutex_t));
        if (ssl_locks == NULL)
            return -1;
    
        for (i = 0; i < ssl_num_locks; i++) {
            pthread_mutex_init(&(ssl_locks[i]), NULL);
        }
    
        CRYPTO_set_id_callback(get_thread_id_cb);
        CRYPTO_set_locking_callback(thread_lock_cb);
    
        return 0;
    }
    
    
    

    相关文章

      网友评论

          本文标题:Bufferevents:advanced topics

          本文链接:https://www.haomeiwen.com/subject/fafxvqtx.html