2011-08-03 10 views
2

私はlibevを学んでいますが、コードは分かりにくいので、私はlibeventを最初に学んでいます。そのコードは比較的明確です。しかし、例(http://www.wangafu.net/~nickm/libevent-book/01_intro.html)を試してみると問題が発生します。libeventのサンプルコードの質問:どのように呼び出されますか?

do_read()のevent_add(state-> write_event、NULL)のコードはどのようにdo_write()関数を呼び出すのですか?

/* For sockaddr_in */ 
#include <netinet/in.h> 
/* For socket functions */ 
#include <sys/socket.h> 
/* For fcntl */ 
#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_LINE 16384 

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

char 
rot13_char(char c) 
{ 
    return c; 
    /* We don't want to use isalpha here; setting the locale would change 
    * which characters are considered alphabetical. */ 
    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; 
} 

struct fd_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; 
}; 

struct fd_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(struct fd_state *state) 
{ 
    event_free(state->read_event); 
    event_free(state->write_event); 
    free(state); 
} 

void 
do_read(evutil_socket_t fd, 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 evutil macro 
      return; 
     perror("recv"); 
     free_fd_state(state); 
    } 
} 

void 
**do_write(evutil_socket_t fd, 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 use evutil 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_t listener, short event, void *arg) 
{ 
    struct event_base *base = arg; 
    struct sockaddr_storage ss; 
    socklen_t slen = sizeof(ss); 
    int fd = accept(listener, (struct sockaddr*)&ss, &slen); 
    if (fd < 0) { // XXXX eagain?? 
     perror("accept"); 
    } else if (fd > FD_SETSIZE) { 
     close(fd); // XXX replace all closes with 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); 

#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; 
    } 

    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(int c, char **v) 
{ 
    setvbuf(stdout, NULL, _IONBF, 0); 

    run(); 
    return 0; 
} 

答えて

4

私は同じ質問に答えていた場合、私はわからないんだけど、あなたは尋ねた - 私のようにそれを理解する:

どのように呼び出されてdo_write()event_add(state->write_event, NULL)do_read()でリードを呼び出していますか?

これを理解するための鍵は、do_read()の機能が実際に何をしているかを理解することです。 do_read()読み込むべきデータを持っているソケットに関連付けられたコールバック関数です:これはallocate_fd_state()で設定されています

struct fd_state * 
alloc_fd_state(struct event_base *base, evutil_socket_t fd) 
{ 
    /* 
    * Allocate a new fd_state structure, which will hold our read and write events 
    */
    struct fd_state *state = malloc(sizeof(struct fd_state)); 

    [...] 
    /* 
    * Initialize a read event on the given file descriptor: associate the event with 
    * the given base, and set up the do_read callback to be invoked whenever 
    * data is available to be read on the file descriptor. 
    */
    state->read_event = event_new(base, fd, EV_READ|EV_PERSIST, do_read, state); 
    [...] 
    /* 
    * Set up another event on the same file descriptor and base, which invoked the 
    * do_write callback anytime the file descriptor is ready to be written to. 
    */ 
    state->write_event = 
     event_new(base, fd, EV_WRITE|EV_PERSIST, do_write, state); 
    [...] 
    return state; 
} 

この時点で、しかし、これらのイベントのどちらがevent_baseにevent_add()「エドされていますベース。何をすべきかについての指示はすべて書き出されますが、誰もそれらを見ていません。それでは、何が読み込まれるのですか? state->read_eventは、着信接続が行われた後にベースに対して編集されたevent_add()です。 do_accept()を見てください:

void 
do_accept(evutil_socket_t listener, short event, void *arg) 
{ 
    [ ... accept a new connection and give it a file descriptor fd ... ] 

    /* 
    * If the file descriptor is invalid, close it. 
    */ 
    if (fd < 0) { // XXXX eagain?? 
     perror("accept"); 
    } else if (fd > FD_SETSIZE) { 
     close(fd); // XXX replace all closes with EVUTIL_CLOSESOCKET */ 
    /* 
    * Otherwise, if the connection was successfully accepted... 
    */ 
    } else { 
     [ ... allocate a new fd_state structure, and make the file descriptor non-blocking ...] 
     /* 
     * Here's where the magic happens. The read_event created back in alloc_fd_state() 
     * is finally added to the base associated with it. 
     */ 
     event_add(state->read_event, NULL); 
    } 
} 

だから、右の新しい接続を受け入れた後、プログラムが接続で利用可能なデータがあるとまで待ってから、do_read()コールバックを実行するためにlibeventを伝えます。この時点では、do_write()を呼び出すことはまだできません。それはevent_add() 'である必要があります。これはdo_read()で起こる:

void 
do_read(evutil_socket_t fd, short events, void *arg) 
{ 
    /* Create a temporary buffer to receive some data */ 
    char buf[1024]; 

    while (1) { 
     [ ... Receive the data, copying it into buf ... ] 
     [ ... if there is no more data to receive, or there was an error, exit this loop... ] 

     [ ... else, result = number of bytes received ... ] 
     for (i=0; i < result; ++i) { 
      [ ... if there's room in the buffer, copy in the rot13() encoded 
        version of the received data ... ] 
      /* 
      * Boom, headshot. If we've reached the end of the incoming data 
      * (assumed to be a newline), then ... 
      */ 
      if (buf[i] == '\n') { 
       [...] 
       /* 
       * Have libevent start monitoring the write_event, which calls do_write 
       * as soon as the file descriptor is ready to be written to. 
       */ 
       event_add(state->write_event, NULL); 
       [...] 
      } 
     } 
    } 
    [...]  
} 

だから、ファイルディスクリプタからいくつかのデータを読み込んで、プログラムは ファイルディスクリプタがに書き込まれる準備ができるまで待って開始し、その後、do_write()を呼び出します。 I a)は、あなたの質問の正しい解釈したこと、およびb)、これは便利な答えだったことを願っています

[ set up an event_base and start waiting for events ] 
[ if someone tries to connect ] 
    [ accept the connection ] 

[ ... wait until there is data to read on the connection ... ] 

[ read in data from the connection until there is no more left ] 

[ ....wait until the connection is ready to be written to ... ] 

[ write out our rot13() encoded response ] 

:プログラム 流れは次のようになります。

+2

よく説明されています! +1 –

関連する問題