1. 程式人生 > >libevent學習筆記【使用篇】——(零)非同步IO簡介

libevent學習筆記【使用篇】——(零)非同步IO簡介

非同步IO簡介

  大多數的初級程式設計者都是從阻塞IO呼叫開始網路程式設計的。阻塞(同步)IO呼叫指的是:呼叫會一直阻塞,不會返回,直到發生下面兩種情況之一: 要麼操作完成;要麼經歷相當長的時間,網路協議棧自己放棄。
  比如,當在TCP連線上呼叫connect時,作業系統會發送SYN包到TCP的遠端主機。connect會一直阻塞而不返回,直到它接收到了遠端主機發來的SYN+ACK包,或者經歷太長的時間而自己放棄。
  下面是一個簡單的使用阻塞網路呼叫的客戶端例子。它連結google,傳送簡單的HTTP請求,然後將響應輸出到stdout。
Example:A simple blocking HTTP client:

/* For  sockaddr_in */
#include  <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>
/* For gethostbyname */
#include <netdb.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>

int main(int c,char **v)
{
   const char query[] =
        "GET / HTTP/1.0\r\n"
"Host: www.google.com\r\n" "\r\n"; const char hostname[] = "www.google.com"; struct sockaddr_in sin; struct hostent *h; const char *cp; int fd; ssize_t n_written, remaining; char buf[1024]; /* Look up the IP address for the hostname. Watch out; this isn't threadsafe on most platforms. */
h = gethostbyname(hostname); if (!h) { fprintf(stderr, "Couldn't lookup%s: %s", hostname, hstrerror(h_errno)); return 1; } if (h->h_addrtype != AF_INET) { fprintf(stderr, "No ipv6 support,sorry."); return 1; } /* Allocate a new socket */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return 1; } /* Connect to the remote host. */ sin.sin_family = AF_INET; sin.sin_port = htons(80); sin.sin_addr = *(struct in_addr*)h->h_addr; if (connect(fd, (struct sockaddr*) &sin, sizeof(sin))) { perror("connect"); close(fd); return 1; } /* Write the query. */ /* XXX Can send succeed partially? */ cp = query; remaining = strlen(query); while (remaining) { n_written = send(fd, cp, remaining, 0); if (n_written <= 0) { perror("send"); return 1; } remaining -= n_written; cp += n_written; } /* Get an answer back. */ while (1) { ssize_t result = recv(fd, buf,sizeof(buf), 0); if (result == 0) { break; }elseif (result < 0) { perror("recv"); close(fd); return 1; } fwrite(buf, 1, result, stdout); } close(fd); return 0; }

  上面例子中,所有的網路呼叫都是阻塞的:`gethostbyname`直到成功或失敗的解析了`www.google.com`才會返回;`connect`直到TCP建鏈成功了才會返回;`recv`直到收到資料時才會返回;`send`直到將輸出flushed到核心的寫緩衝區之後才會返回。
  當然,阻塞IO並不總是無用的。如果應用程式在同一時刻不需要做其他事,那麼阻塞IO同樣會很好的工作。 

  但是,如果你要編寫一個需要同時處理多個連線的程式,比如需要從兩個連線中讀取資料,而且不知道那個連線會先收到資料,那麼下面就是一個不好的例子:
BadExample

/* This  won't work. */
charbuf[1024];
int i, n;
while(i_still_want_to_read()) {
    for (i=0; i<n_sockets; ++i) {
        n = recv(fd[i], buf, sizeof(buf), 0);
        if (n==0)
            handle_close(fd[i]);
        else if (n<0)
            handle_error(fd[i], errno);
        else
            handle_input(fd[i], buf, n);
    }
}

  如果fd[2]上首先有資料到來,但是上面的程式碼只有在fd[0]和fd[1]上接收到資料之後,才能去處理fd[2]上的資料。

  有時,可以通過多執行緒(程序)來處理這樣的問題。一個最簡單的方式就是每個連結用一個執行緒(程序)進行處理。這樣每個連結都會有自己的執行緒(程序)處理,一個連結上的阻塞IO呼叫就不會影響到其他連結上的處理。
  下面就是一個例子:在TCP的40713埠上進行監聽的ROT13伺服器,每次從輸入中接收一行資料,經過簡單的處理後進行輸出。它使用fork產生新的程序來處理每個連結。
Example:Forking ROT13 server

/* For sockaddr_in */
#include  <netinet/in.h>
/* For socket functions */
#include <sys/socket.h>

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define MAX_LINE 16384

char rot13_char(char c)
{
    if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

void child(int fd)
{
    char outbuf[MAX_LINE+1];
    size_t outbuf_used = 0;
    ssize_t result;

    while (1) 
    {
        char ch;
        result = recv(fd, &ch, 1, 0);
        if (result == 0) {
            break;
        } else if (result == -1) {
            perror("read");
            break;
        }

        /* We do this test to keep the userfrom overflowing the buffer. */
        if (outbuf_used < sizeof(outbuf)) {
            outbuf[outbuf_used++] = rot13_char(ch);
        }

        if (ch == '\n') {
            send(fd, outbuf, outbuf_used, 0);
            outbuf_used = 0;
            continue;
        }
    }
}

void run(void)
{
    int listener;
    struct sockaddr_in sin;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(40713);

    listener = socket(AF_INET, SOCK_STREAM, 0);

#ifndef WIN32
    {
        int one = 1;
        setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));
    }
#endif

    if (bind(listener, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return;
    }

    if (listen(listener, 16)<0) {
        perror("listen");
        return;
    }



    while (1) {
        struct sockaddr_storage ss;
        socklen_t slen = sizeof(ss);
        int fd = accept(listener, (struct sockaddr*)&ss, &slen);
        if (fd < 0) {
            perror("accept");
        } else {
            if (fork() == 0) {
                child(fd);
                exit(0);
            }
        }
    }
}

int main(int c, char **v)
{
    run();
    return 0;
}

  這樣,是否已經完美解決了同一時刻多連線的問題了呢?事實並非如此:
  第一,某些平臺上,建立新程序(甚至是執行緒)是十分昂貴的。當然在實際環境中,可以使用執行緒池,而不是每次都建立新執行緒。
  第二,更重要的是,執行緒無法如你所願的規模化使用。如果你的程式需要同時處理成千上萬個連結的時候,處理成千上萬個執行緒就不是那麼高效的了。

  如果執行緒不是處理多連線的答案,那什麼才是呢?

  在unix系統上,將socket設定為非阻塞:`fcntl(fd, F_SETFL, O_NONBLOCK)`。一旦將fd置為非阻塞,那麼從此刻起,無論何時進行網路呼叫,該呼叫會立即返回,要麼完成操作,返回成功,要麼就是返回一個特定的錯誤碼指出“當前無法完成任務,再試一次”。所以,上面2個連結的例子可以如下寫:
BadExample: busy-polling all sockets

/* This will work, but the performance will beunforgivably bad. */
int i, n;
char buf[1024];
for (i=0;i < n_sockets; ++i)
    fcntl(fd[i], F_SETFL, O_NONBLOCK);

while(i_still_want_to_read()) {
    for (i=0; i < n_sockets; ++i) {
        n = recv(fd[i], buf, sizeof(buf), 0);
        if (n == 0) {
            handle_close(fd[i]);
        }else if (n < 0) {
            if (errno == EAGAIN)
                 ; /* The kernel didn't haveany data for us to read. */
            else
                 handle_error(fd[i], errno);
         } else {
            handle_input(fd[i], buf, n);
         }
    }
}

  上面就是使用非阻塞sockets的例子,它雖然可以工作,但是效率卻很差,兩個原因:第一,當每個連結都沒有資料可讀的時候,就會無限的輪訓下去,用盡所有的CPU週期。第二,如果需要處理多個連結,那麼不管是否有資料可讀,每個連結都會進行一次核心呼叫。

  所以,我們需要一種方法,可以告訴核心“一直等待,直到某個socket已經有準備好了,而且要告訴我那個socket準備好了”。
  古老的解決方法是使用select,目前仍在使用。select使用三個socket fd集合(位陣列):可讀、可寫和異常。它會一直等待,直到集合中的某一個socket已經準備好了,而且,select返回時,會更改集合,使其只包含那些已經準備好了的socket fd。使用select的例子如下:
Example:Using select

/* If youonly have a couple dozen fds, this version won't be awful */
fd_setreadset;
int i, n;
charbuf[1024];

while (i_still_want_to_read()){
    int maxfd = -1;
    FD_ZERO(&readset);

    /* Add all of the interesting fds toreadset */
    for (i=0; i < n_sockets; ++i) {
         if (fd[i]>maxfd) maxfd = fd[i];
         FD_SET(fd[i], &readset);
    }

    /*Wait until one or more fds are ready to read */
    select(maxfd+1, &readset, NULL, NULL,NULL);

    /* Process all of the fds that are stillset in readset */
    for (i=0; i < n_sockets; ++i) {
        if (FD_ISSET(fd[i], &readset)) {
            n = recv(fd[i], buf, sizeof(buf),0);
            if (n == 0) {
                handle_close(fd[i]);
            } else if (n < 0) {
                if (errno == EAGAIN)
                     ; /* The kernel didn'thave any data for us to read. */
                else
                     handle_error(fd[i],errno);
             } else {
                handle_input(fd[i], buf, n);
             }
        }
    }
}

一個完整的使用select的ROT13的伺服器例子如下:

/* Forsockaddr_in */
#include<netinet/in.h>
/* For socketfunctions */
#include<sys/socket.h>
/* Forfcntl */
#include<fcntl.h>
/* forselect */
#include<sys/select.h>

#include<assert.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>

#defineMAX_LINE 16384

char rot13_char(charc)
{
    /* We don't want to use isalpha here;setting the locale would change
     * which characters are consideredalphabetical. */
    if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

structfd_state {
    char buffer[MAX_LINE];
    size_t buffer_used;

    int writing;
    size_t n_written;
    size_t write_upto;
};

structfd_state * alloc_fd_state(void)
{
    struct fd_state *state =malloc(sizeof(struct fd_state));
    if (!state)
        return NULL;
    state->buffer_used = state->n_written= state->writing =
        state->write_upto = 0;
    return state;
}

void free_fd_state(structfd_state *state)
{
    free(state);
}

void make_nonblocking(intfd)
{
    fcntl(fd, F_SETFL, O_NONBLOCK);
}

int do_read(intfd, struct fd_state *state)
{
    char buf[1024];
    int i;
    ssize_t result;
    while (1) {
        result = recv(fd, buf, sizeof(buf), 0);
        if (result <= 0)
            break;

        for (i=0; i < result; ++i)  {
            if (state->buffer_used <sizeof(state->buffer))
               state->buffer[state->buffer_used++] = rot13_char(buf[i]);
            if (buf[i] == '\n') {
                state->writing = 1;
                state->write_upto =state->buffer_used;
            }
        }
    }

    if (result == 0) {
        return 1;
    } else if (result < 0) {
        if (errno == EAGAIN)
            return 0;
        return -1;
    }

    return 0;
}

int do_write(intfd, struct fd_state *state)
{
    while (state->n_written <state->write_upto) {
        ssize_t result = send(fd,state->buffer + state->n_written,
                             state->write_upto - state->n_written, 0);
        if (result < 0) {
            if (errno == EAGAIN)
                return 0;
            return -1;
        }
        assert(result != 0);

        state->n_written += result;
    }

    if (state->n_written ==state->buffer_used)
        state->n_written =state->write_upto = state->buffer_used = 0;

    state->writing = 0;

    return 0;
}

void run(void)
{
    int listener;
    struct fd_state *state[FD_SETSIZE];
    struct sockaddr_in sin;
    int i, maxfd;
    fd_set readset, writeset, exset;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(40713);

    for (i = 0; i < FD_SETSIZE; ++i)
        state[i] = NULL;

    listener = socket(AF_INET, SOCK_STREAM, 0);
    make_nonblocking(listener);

#ifndefWIN32
    {
        int one = 1;
        setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));
    }
#endif

    if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return;
    }

    if (listen(listener, 16)<0) {
        perror("listen");
        return;
    }

    FD_ZERO(&readset);
    FD_ZERO(&writeset);
    FD_ZERO(&exset);

    while (1) {
        maxfd = listener;

        FD_ZERO(&readset);
        FD_ZERO(&writeset);
        FD_ZERO(&exset);

        FD_SET(listener, &readset);

        for (i=0; i < FD_SETSIZE; ++i) {
            if (state[i]) {
                if (i > maxfd)
                    maxfd = i;
                FD_SET(i, &readset);
                if (state[i]->writing) {
                    FD_SET(i, &writeset);
                }
            }
        }

        if (select(maxfd+1, &readset,&writeset, &exset, NULL) < 0) {
            perror("select");
            return;
        }

        if (FD_ISSET(listener, &readset)) {
            struct sockaddr_storage ss;
            socklen_t slen = sizeof(ss);
            int fd = accept(listener, (structsockaddr*)&ss, &slen);
           if (fd < 0) {
                perror("accept");
            } else if (fd > FD_SETSIZE) {
                close(fd);
            } else {
                make_nonblocking(fd);
                state[fd] = alloc_fd_state();
                assert(state[fd]);/*XXX*/
            }
        }

        for (i=0; i < maxfd+1; ++i) {
            int r = 0;
            if (i == listener)
                continue;

            if (FD_ISSET(i, &readset)) {
                r = do_read(i, state[i]);
            }
            if (r == 0 && FD_ISSET(i,&writeset)) {
                r = do_write(i, state[i]);
            }
            if (r) {
                free_fd_state(state[i]);
                state[i] = NULL;
                close(i);
            }
        }
    }
}

int main(intc, char **v)
{
    setvbuf(stdout, NULL, _IONBF, 0);

    run();
    return 0;
}

  但是問題還沒有解決。因為產生和讀取select的位陣列耗費的時間與最大的socket fd數成正比,所以當socket fd數變得很大時,select呼叫的效能就會下降很多。

  不同的作業系統都提供了不同的select替代函式。包括poll,epoll,kqueue, evports/dev/poll。所有這些介面都具有比select更好的效能,而且除了poll之外,他們在增加socket,刪除socket,通知哪個socket準備好這些方面,都可以達到O(1)的效能。

  不幸的是,所有這些不同的介面都沒有形成標準。linux提供了epoll,BSDs提供了kqueue,Solaris提供了evports/dev/poll,而且這些作業系統提供的介面相互獨立。所以,當你需要編寫一個可移植的、高效能非同步應用時,你需要一個封裝所有這些介面的抽象,而且提供那個最高效的介面。
這就是libeventAPI能提供的最底層的功能。它提供了一系列的select替代介面,並且使用當前作業系統所具有的,最高效的版本。
下面是另一個ROT13伺服器的例子。該例項使用libevent2替代select。去除了fd_sets,而是使用event_base新增和刪除事件,當然這是通過pollepollkqueue等來實現的。
Example:A low-level ROT13 server with Libevent

/* Forsockaddr_in */
#include<netinet/in.h>
/* Forsocket functions */
#include<sys/socket.h>
/* Forfcntl */
#include<fcntl.h>

#include<event2/event.h>

#include<assert.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>

#define MAX_LINE16384

voiddo_read(evutil_socket_t fd, short events, void *arg);
voiddo_write(evutil_socket_t fd, short events, void *arg);

char
rot13_char(charc)
{
    /* We don't want to use isalpha here;setting the locale would change
     * which characters are consideredalphabetical. */
    if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

structfd_state {
    char buffer[MAX_LINE];
    size_t buffer_used;

    size_t n_written;
    size_t write_upto;

    struct event *read_event;
    struct event *write_event;
};

structfd_state * alloc_fd_state(struct event_base *base, evutil_socket_t fd)
{
    struct fd_state *state =malloc(sizeof(struct fd_state));
    if (!state)
        return NULL;
    state->read_event = event_new(base, fd,EV_READ|EV_PERSIST, do_read, state);
    if (!state->read_event) {
        free(state);
        return NULL;
    }
    state->write_event = event_new(base, fd,EV_WRITE|EV_PERSIST, do_write, state);

    if (!state->write_event) {
        event_free(state->read_event);
        free(state);
        return NULL;
    }

    state->buffer_used = state->n_written= state->write_upto = 0;

    assert(state->write_event);
    return state;
}

void free_fd_state(structfd_state *state)
{
    event_free(state->read_event);
    event_free(state->write_event);
    free(state);
}

void do_read(evutil_socket_tfd, short events, void *arg)
{
    struct fd_state *state = arg;
    char buf[1024];
    int i;
    ssize_t result;
    while (1) {
        assert(state->write_event);
        result = recv(fd, buf, sizeof(buf), 0);
        if (result <= 0)
            break;

        for (i=0; i < result; ++i)  {
            if (state->buffer_used <sizeof(state->buffer))
               state->buffer[state->buffer_used++] = rot13_char(buf[i]);
            if (buf[i] == '\n') {
                assert(state->write_event);
                event_add(state->write_event,NULL);
                state->write_upto =state->buffer_used;
            }
        }
    }

    if (result == 0) {
        free_fd_state(state);
    } else if (result < 0) {
        if (errno == EAGAIN) // XXXX use evutilmacro
            return;
        perror("recv");
        free_fd_state(state);
    }
}

void do_write(evutil_socket_tfd, short events, void *arg)
{
    struct fd_state *state = arg;

    while (state->n_written <state->write_upto) {
        ssize_t result = send(fd,state->buffer + state->n_written,
                             state->write_upto - state->n_written, 0);
        if (result < 0) {
            if (errno == EAGAIN) // XXX useevutil macro
                return;
            free_fd_state(state);
            return;
        }
        assert(result != 0);

        state->n_written += result;
    }

    if (state->n_written ==state->buffer_used)
        state->n_written =state->write_upto = state->buffer_used = 1;

    event_del(state->write_event);
}

void do_accept(evutil_socket_tlistener, short event, void *arg)
{
    struct event_base *base = arg;
    struct sockaddr_storage ss;
    socklen_t slen = sizeof(ss);
    int fd = accept(listener, (structsockaddr*)&ss, &slen);
    if (fd < 0) { // XXXX eagain??
        perror("accept");
    } else if (fd > FD_SETSIZE) {
        close(fd); // XXX replace all closeswith EVUTIL_CLOSESOCKET */
    } else {
        struct fd_state *state;
        evutil_make_socket_nonblocking(fd);
        state = alloc_fd_state(base, fd);
        assert(state); /*XXX err*/
        assert(state->write_event);
        event_add(state->read_event, NULL);
    }
}

void run(void)
{
    evutil_socket_t listener;
    struct sockaddr_in sin;
    struct event_base *base;
    struct event *listener_event;

    base = event_base_new();
    if (!base)
        return; /*XXXerr*/

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(40713);

    listener = socket(AF_INET, SOCK_STREAM, 0);
    evutil_make_socket_nonblocking(listener);

#ifndefWIN32
    {
        int one = 1;
        setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));
    }
#endif

    if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return;
    }

    if (listen(listener, 16)<0) {
        perror("listen");
        return;
    }

    listener_event = event_new(base, listener,EV_READ|EV_PERSIST, do_accept, (void*)base);
    /*XXX check it */
    event_add(listener_event, NULL);

    event_base_dispatch(base);
}

int main(intc, char **v)
{
    setvbuf(stdout, NULL, _IONBF, 0);

    run();
    return 0;
}

  (上面的程式碼需要注意的是,使用evutil_socket_t,而不是int作為socket的型別;使用evutil_make_socket_nonblocking而不是fcntl(O_NONBLOCK),將socket轉為非阻塞。這些改變使得我們的程式碼可以相容win32平臺下的網路API。)

更方便並且相容windows

  我們的程式碼雖然更加高效了,但是也變得更加複雜了。回到我們使用fork的例子,我們沒有為每個連結都管理一個快取,我們只是在每個程序上使用了獨立的棧快取。實際上,我們無需明確的跟蹤哪個socket在讀或寫,在程式碼中它是隱含的。我們也無需一個跟蹤多少操作已經完成的結構體,我們可以僅僅使用迴圈和棧變數即可。
  另外,如果你對windows上的網路程式設計很熟悉,則可以看出,使用libevent的上面的例子沒有達到最佳的效能。在Windows上,高效的非同步IO與並不是類似於select那樣的機制,而是使用IOCP(IO Completion Ports)API。與其他高效網路API不同的是,IOCP並不通知你的程式哪個socket已經準備好操作了,相反的,程式告訴windows網路棧開始一個網路操作,而IOCP告訴程式操作已經完成了。

  幸運的是,libevent2bufferevents介面可以解決上面的問題:它使得程式編寫更加簡單,而且可以在windows上、unix上都提供最高效的介面。下面是最後一個ROT13伺服器的例子,它使用了bufferevents API
 
Example:A simpler ROT13 server with Libevent

/* Forsockaddr_in */
#include<netinet/in.h>
/* Forsocket functions */
#include<sys/socket.h>
/* Forfcntl */
#include<fcntl.h>

#include<event2/event.h>
#include<event2/buffer.h>
#include<event2/bufferevent.h>

#include<assert.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<errno.h>

#defineMAX_LINE 16384

void do_read(evutil_socket_tfd, short events, void *arg);
voiddo_write(evutil_socket_t fd, short events, void *arg);

char  rot13_char(char c)
{
    /* We don't want to use isalpha here;setting the locale would change
     * which characters are consideredalphabetical. */
    if ((c >= 'a' && c <= 'm') ||(c >= 'A' && c <= 'M'))
        return c + 13;
    else if ((c >= 'n' && c <='z') || (c >= 'N' && c <= 'Z'))
        return c - 13;
    else
        return c;
}

void  readcb(struct bufferevent *bev, void *ctx)
{
    struct evbuffer *input, *output;
    char *line;
    size_t n;
    int i;
    input = bufferevent_get_input(bev);
    output = bufferevent_get_output(bev);

    while ((line = evbuffer_readln(input, &n,EVBUFFER_EOL_LF))) {
        for (i = 0; i < n; ++i)
            line[i] = rot13_char(line[i]);
        evbuffer_add(output, line, n);
        evbuffer_add(output, "\n",1);
        free(line);
    }

    if (evbuffer_get_length(input) >=MAX_LINE) {
        /* Too long; just process what there isand go on so that the buffer
         * doesn't grow infinitely long. */
        char buf[1024];
        while (evbuffer_get_length(input)) {
            int n = evbuffer_remove(input, buf,sizeof(buf));
            for (i = 0; i < n; ++i)
                buf[i] = rot13_char(buf[i]);
            evbuffer_add(output, buf, n);
        }
        evbuffer_add(output, "\n",1);
    }
}

void  errorcb(struct bufferevent *bev, short error,void *ctx)
{
    if (error & BEV_EVENT_EOF) {
        /* connection has been closed, do anyclean up here */
        /* ... */
    } else if (error & BEV_EVENT_ERROR) {
        /* check errno to see what erroroccurred */
        /* ... */
    } else if (error & BEV_EVENT_TIMEOUT) {
        /* must be a timeout event handle,handle it */
        /* ... */
    }
    bufferevent_free(bev);
}

void  do_accept(evutil_socket_t listener, shortevent, void *arg)
{
    struct event_base *base = arg;
    struct sockaddr_storage ss;
    socklen_t slen = sizeof(ss);
    int fd = accept(listener, (structsockaddr*)&ss, &slen);
    if (fd < 0) {
        perror("accept");
    } else if (fd > FD_SETSIZE) {
        close(fd);
    } else {
        struct bufferevent *bev;
        evutil_make_socket_nonblocking(fd);
        bev = bufferevent_socket_new(base, fd,BEV_OPT_CLOSE_ON_FREE);
        bufferevent_setcb(bev, readcb, NULL,errorcb, NULL);
        bufferevent_setwatermark(bev, EV_READ,0, MAX_LINE);
        bufferevent_enable(bev,EV_READ|EV_WRITE);
    }
}

void run(void)
{
    evutil_socket_t listener;
    struct sockaddr_in sin;
    struct event_base *base;
    struct event *listener_event;

    base = event_base_new();
    if (!base)
        return; /*XXXerr*/

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = 0;
    sin.sin_port = htons(40713);

    listener = socket(AF_INET, SOCK_STREAM, 0);
    evutil_make_socket_nonblocking(listener);

#ifndefWIN32
    {
        int one = 1;
        setsockopt(listener, SOL_SOCKET,SO_REUSEADDR, &one, sizeof(one));
    }
#endif

    if (bind(listener, (structsockaddr*)&sin, sizeof(sin)) < 0) {
        perror("bind");
        return;
    }

    if (listen(listener, 16)<0) {
        perror("listen");
        return;
    }

    listener_event = event_new(base, listener,EV_READ|EV_PERSIST, do_accept, (void*)base);
    /*XXX check it */
    event_add(listener_event, NULL);

    event_base_dispatch(base);
}

int main(intc, char **v)
{
    setvbuf(stdout, NULL, _IONBF, 0);

    run();
    return 0;
}

相關推薦

libevent學習筆記使用——非同步IO簡介

非同步IO簡介   大多數的初級程式設計者都是從阻塞IO呼叫開始網路程式設計的。阻塞(同步)IO呼叫指的是:呼叫會一直阻塞,不會返回,直到發生下面兩種情況之一: 要麼操作完成;要麼經歷相當長的時間,網路協議棧自己放棄。   比如,當在TCP連線上呼叫co

吳恩達《deeplearning深度學習》課程學習筆記1精簡總結

畢業以後就沒再寫過部落格,又想起來了。 Ps:本文只是個人筆記總結,沒有大段的詳細講解,僅僅是將自己不熟悉和認為重要的東西總結下來,算是一個大綱,用的時候方便回憶和查詢。 Ps2:部分筆記內容見圖片。 相關課程內容 一、神經網路和深度學習 第一週 深

吳恩達《deeplearning深度學習》課程學習筆記3精簡總結

相關課程內容 二、改善深層神經網路 第一週 深度學習的實用層面 知識點總結 1. 訓練集與測試集分佈不匹配問題 訓練集(train) 驗證集(dev) 測試集(test):test和

libevent學習筆記使用——6a. Bufferevents高階話題

1. 成對的bufferevents 有時, 網路程式可能需要和自己通訊。 舉個例子:通過某些協議對使用者連線進行隧道操作的程式,有時候也需要通過同樣的協議對自身的連線進行隧道操作。當然,可以通過開啟一個到自身監聽埠的連線,讓程式使用這個連線來達到這種目標

Spring Cloud學習筆記 一:分布式配置中心 Spring Colud Config

16px gin war imp web項目 tps conf name request 一、簡介 Spring Cloud Config提供了在分布式系統的外部配置的客戶端支持。通過配置服務(Config Server)來為所有的環境和應用提供外部配置的集中管理。這些概念

Verilog學習筆記簡單功能實現...............異步FIFO

另一個 gif 多個 可靠 基本原理 drs bar next 不同 基本原理: 1.讀寫指針的工作原理   寫指針:總是指向下一個將要被寫入的單元,復位時,指向第1個單元(編號為0)。   讀指針:總是指向當前要被讀出的數據,復位時,指向第1個單元(編號為0)

Django3創建網頁:學習筆記主頁

nbsp 圖片 png 剛才 需要 ews namespace render esp 創建Django的過程通常為三個階段:定義URL,編寫視圖和編寫模板。 映射URL 當在瀏覽器中輸入URL時,現在默認會返回默認的Django網站,現在需要將主頁映射到項目&ldquo

開始的unity2017筆記2D物理方面

三、物理方面 給sprite物件加剛體(Rigidbody 2D)裡面的【body type】中dynamic是物體受重力影響,kinematic就是不受重力影響。 給物件再加碰撞體(box collider 2d、circle collider 2d等等之類)在其元件裡面用edit colli

開始的unity2017筆記2D角色動畫

二、角色動畫 製作2d角色動畫的方式有兩種: A:預先做好一張張動畫幀式。 預先用繪圖軟體做好多張動畫幀圖片(記得給他們編號,目測動畫檔案會按照在project裡面排列順序來自動生成),匯入進unity的project裡,按shift多選選中一套動畫幀,直接拖進【hierarchy】裡面會彈出

開始的unity2017筆記2Dsprite精靈建立相關

一、建立精靈 1、把程式設定成2d編輯模式 【edit】-【project settings】-【editor】開啟編輯器,把【default behavior mode】設為【2d】 為了能讓多個圖片能隨時打包,將【editor】編輯器的【sprite packer】一項設為【alwa

linux學習筆記--第一基於nanopim2a的環境搭建,uboot編譯及下載

          接觸一個星期,搭建了虛擬機器,雙系統,行動硬碟ubuntu , 總結下來, 行動硬碟的系統最方便,硬碟可以分為2個分割槽,一個裝系統, 一個放檔案(windows也可以識別),感覺還是挺爽的。           以下是這幾天折騰u-boot的一

黑馬程式設計師Objective-C語言學習筆記之核心語法

--------------------------------------------IOS期待與您交流!-------------------------------------------- 一、點語法 1、沒有使用點語法的情況 此時我們使用setter和gette

構建AR APP新手教程Android4-APP互動邏輯及嵌入呼叫Unity

學習AR應用開發有一段時間了,自己開發了一款簡單的APP來練手,在這裡分享給大家。 前面介紹了Unity3D部分的實現,現在就來介紹Android原生部分的編碼實現。 1.APP基礎UI框架及互動邏輯 首頁就是簡單的ViewPager+Fragment,其中資料用

Unity3D有限狀態機FSM學習筆記7使用例項

using UnityEngine; using System.Collections; public class TestUIState : MonoBehaviour { public static EventSystem.Dispatcher Events = new EventSystem.Di

DL學習筆記22增強學習Reinforcement Learning

據說瞭解增強學習首先要了解馬爾可夫性 馬爾可夫性 在已知目前狀態(現在)的條件下,它未來的演變(將來)不依賴於它以往的演變 (過去 )  馬爾可夫過程按照其狀態和時間引數是否連續或者離散分為三種: 時間和狀態都離散的叫做馬爾科夫鏈 時間和狀態都是連續的叫做馬

Unity3D有限狀態機FSM學習筆記3FSState類

本系列筆記轉載自遊戲蠻牛專欄作家Jackel的論壇文章,詳細介紹了FSM的建立與使用,特與眾分享。連結:http://www.manew.com/thread-37136-1-1.html 該類主要是狀態的基本操作及事件的新增與觸發。程式碼如下: using System;

機器學習框架ML.NET學習筆記8目標檢測採用YOLO2模型

一、概述 本篇文章介紹通過YOLO模型進行目標識別的應用,原始程式碼來源於:https://github.com/dotnet/machinelearning-samples 實現的功能是輸入一張圖片,對圖片中的目標進行識別,輸出結果在圖片中通過紅色框線標記出來。如下:  YOLO簡介

TDD學習筆記一Unit Test - Stub, Mock, Fake 簡介

-i moc load customers eight foreach 存在 執行 repo 這篇文章簡介一下,如何通過 mock framework,來輔助我們更便利地模擬目標對象的依賴對象,而不必手工敲堆只為了這次測試而存在的輔助類型。 而模擬目標對象的部分,常見的有

Docker學習筆記安裝Redis

art port 再次 dock 使用 contain bash Go red 項目中使用到Redis,平常都是別人搭建的,今天試著在Google Cloud Platform 上搭建一個學習環境。 1.使用 docker pull redis 從docker hub中下載

system generator學習筆記01

分享 基本功 hle ima 安裝 分享圖片 use 內容整理 blank 作者:桂。 時間:2018-05-18 18:26:50 鏈接:http://www.cnblogs.com/xingshansi/p/9045914.html 前言 學習使用s