Libev - обратные вызовы ввода / вывода - PullRequest
2 голосов
/ 26 февраля 2012

У меня есть чат-сервер в C / Linux, использующий сокеты TCP.При использовании libev я могу создать наблюдатель ev_io для событий чтения один раз для сокета.Что-то вроде:

ev_io* new_watcher = (ev_io*)malloc(sizeof(ev_io));

//initialize the watcher
ev_init(new_watcher, read_cb);

//set the fd and event to fire on write
ev_io_set(new_watcher, watcher->fd, EV_READ);

//start watching
ev_io_start(loop, new_watcher);

, и это прекрасно работает, потому что событие чтения будет срабатывать только при наличии данных для чтения.Однако я должен по-разному относиться к событиям записи, потому что они постоянно запускаются, даже когда у меня нет данных для записи.Чтобы решить эту проблему, мой read_callback создает наблюдателя ev_io для записи данных только тогда, когда есть данные, готовые для записи, а затем write_callback удалит наблюдателя после того, как отправил свое сообщение.

Это означает, чтоЯ распределяю, инициализирую, устанавливаю, наблюдаю, снимаю и освобождаю наблюдателя записи каждый раз, когда мне нужно обработать сообщение.Я волнуюсь, что я могу обращаться с этим неправильно и неэффективно.

Каков наилучший метод обработки событий write_callback в libev?

Заранее спасибо.

Ответы [ 3 ]

4 голосов
/ 22 декабря 2012

Легко, есть также ev_io_stop, так что вы не запускаете средство записи, если у вас нет ничего для записи, и внутри обратного вызова вы вызываете ev_io_stop, когда записываете весь буфер.

InВ общем случае, когда вы редко переполняете буфер записи (поскольку ваши данные невелики и вы не слишком часто пишете), вы можете сделать это более эффективным, пытаясь записать данные напрямую (если наблюдатель не активен) и только буферизуяданные и запуск средства записи, если вы не можете полностью записать его.

В соответствии с приведенными выше предположениями это означает, что вам почти никогда не нужно запускать средство записи.Недостатком является значительно более сложный код, поэтому во многих случаях лучше начинать с простой логики «добавление данных в буфер записи, запуск наблюдателя, остановка внутри наблюдателя, если буфер был полностью записан».

0 голосов
/ 11 июля 2012

Способ, которым я решил эту ситуацию, заключался в том, чтобы иметь функцию для записи данных, которая принимает указатель на буфер и длину. Он сохраняет указатель и длину в структуре данных очереди и включает событие записи.

Когда срабатывает обратный вызов события write, он проверяет очередь записи, чтобы узнать, есть ли ожидающие записи. Если они есть, выполняется следующая ожидающая запись в очереди и записывается в дескриптор файла. Затем, непосредственно перед завершением обратного вызова записи, он проверяет, пуста ли ожидающая очередь записи. Если так, то это отключает событие записи.

Если вы сделаете объекты событий чтения / записи глобальными переменными, они будут выделяться и освобождаться только один раз. Событие записи включается всякий раз, когда вы знаете, что есть данные для записи, и отключаете его, когда больше нет данных для записи.

Мой код немного сложнее, чем приведенное выше описание, но я опубликую здесь ссылку, чтобы вы могли посмотреть. Код, о котором я говорю конкретно, находится в aiofd.h и aiofd.c (aiofd == дескриптор файла асинхронного ввода-вывода): https://bitbucket.org/wookie/cutil/

Надеюсь, это поможет.

0 голосов
/ 29 февраля 2012

Распределение может добавить некоторые издержки, вы можете использовать статическую переменную вместо malloc или malloc один раз и только бесплатно после завершения цикла событий.Вам нужно только установить перед записью и сбросить после того, как это успешно.Но да, вот как это должно быть сделано.

...