Я не уверен, отвечаю ли я на тот же вопрос, который вы задали - я понимаю это как:
Как вызов event_add(state->write_event, NULL)
в do_read()
приводит к вызову do_write()
?
Ключом к этому является понимание того, что на самом деле делает функция 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_add()
'отправлено на базу event_base.Все инструкции для того, что делать, написаны, но никто не смотрит на них.Так как же что-то читать?state->read_event
is 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);
}
}
Итак, сразу после принятия нового соединения программа сообщает libevent, чтобы он дождался, пока не появятся данные о соединении, а затем запустил обратный вызов do_read()
.На этом этапе все еще невозможно вызвать 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()
.Ход программы выглядит следующим образом:
[ 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 ]
Я надеюсь, что а) это была правильная интерпретация вашего вопроса, и б) это был полезный ответ.